Enabled customer-managed KMS keys with no aliases and no provable use
What does ZopNight detect here?
Customer-managed KMS keys bill $1 monthly whether or not anything encrypts under them. ZopNight nominates enabled keys with an alias count of 0 as unused candidates, but only when measured request activity or billing-backed cost can actually prove the silence, because deleting a key that still protects data destroys that data.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-058 |
| Category | orphan |
| Severity | low |
| Metric | none — pure configuration read |
| Source | kms_unused.go |
Where it applies
The most dangerous dollar in the directory
The saving here is $1 per key per month. The downside of getting it wrong is unbounded: a KMS key still referenced by an EBS volume, an S3 bucket, or an RDS snapshot renders that data permanently unreadable once the key is gone. That asymmetry shapes everything about this rule. It is the most heavily guarded orphan check in the AWS set, and it deliberately says “may be unused, verify” rather than “delete”.
Zero aliases is a proxy, and the rule admits it
KMS exposes per-key usage only through CloudTrail, which discovery does not join, so there is no last-used timestamp to gate on. What discovery does emit is the alias count, and an enabled customer-managed key with zero aliases (nothing human-facing points at it) is a strong candidate. Candidate, not verdict: services can reference keys by raw ARN with no alias involved.
Proof, or abstention
Before firing, the rule demands the silence be measurable, through either of two independent signals. A present per-key request metric showing zero requests per day is activity proof; billing-backed cost sitting at the bare $1 base is cost proof, since KMS adds $0.03 per 10,000 requests on top of that flat key fee and paid request charges mean the key is alive. Any measured activity, or cost above the base, suppresses. And when activity could not be measured and the cost figure is a rack-rate estimate rather than a real invoice, the rule abstains entirely. An unprovable orphan stays unflagged.
Do the verification the rule cannot
aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=ResourceName,AttributeValue=<key-id> \ --start-time "$(date -u -v-90d +%Y-%m-%dT%H:%M:%SZ)" --max-results 20Ninety days of no Encrypt/Decrypt/GenerateDataKey events is the confirmation worth having.
Deletion is scheduled, never instant
KMS refuses immediate deletion: you schedule it with a 7–30 day waiting period, during which the key is disabled and any surviving dependency surfaces as an error you can still undo. Use the full 30 days for anything with history. Disabling the key first and waiting a billing cycle is the even gentler path: reversible, and it flushes out the annual batch job nobody remembered.