Rotation, retention and deletion¶
There is an intuition here that is very easy to get backwards, and getting it backwards leads to protecting the wrong operation.
Rotation does not lose messages¶
The natural fear is that rotating a key makes everything encrypted to the old key unreadable. For AWS KMS, that is not what happens:
When you use the rotated KMS key to decrypt ciphertext, AWS KMS uses the key material that was used to encrypt it. You cannot select a particular key material for decrypt operations, AWS KMS automatically chooses the correct key material.
And retention is not conditional on rotation being enabled:
AWS KMS retains all key material for a KMS key with
AWS_KMSorigin, even if key rotation is disabled. AWS KMS deletes key material only when you delete the KMS key.
So KMS keeps every version and picks the right one. Rotation is safe.
It is moot anyway for these keys¶
Both keys here are asymmetric, and AWS supports automatic and on-demand rotation "only on symmetric encryption KMS keys". Asymmetric keys are listed under manual rotation only.
enable_key_rotation = false is therefore the only valid setting, not a
choice. Security scanners that flag it are pattern-matching on the attribute
without discriminating by key usage; the module suppresses that finding
explicitly rather than leaving it to severity filtering.
Deletion is the operation that loses messages¶
Because material is discarded only when the key is deleted, deletion is the irreversible one. And for this key type AWS is unusually blunt about the consequences:
scheduling deletion has no effect on public keys outside of AWS KMS. Users who have the public key can continue to use them to encrypt messages. They do not receive any notification that the key state is changed.
Worse, for detection:
Alarms, logs, and other strategies that detect attempted use of KMS key that is pending deletion cannot detect use of the public key outside of AWS KMS.
A published certificate is "the public key outside of AWS KMS". Delete the key and senders keep encrypting to it, see no error, and produce messages nobody can ever read — with no signal on either side. The sender thinks they have reported something. You never receive it.
The retention rule¶
For an OpenPGP identity the fingerprint is the identity, so superseding one means minting a new key and publishing a new certificate — not swapping key material under a stable ID. Which gives the operational rule:
[!CAUTION] Never delete a retired key. Anything encrypted against an old certificate is still bound to the key of that era, and an archive may go unread for months. Retire by disabling at most.
Disabling is reversible; deletion is not. An enabled unused key costs about $1/month, which is cheaper than any amount of unreadable archive.
What the module does about it¶
Protection is layered, because there is no native "deletion protection" flag on a KMS key — what AWS offers is a waiting period, key-policy control over who can schedule deletion, and the advice to disable rather than delete.
| Layer | Control | Stops |
|---|---|---|
| Tofu | prevent_destroy (default true) |
tofu destroy, a removed module block, and ForceNew replacement |
| Key policy | allow_administrator_key_deletion (default false) |
an administrator scheduling deletion by accident |
| KMS | deletion_window_in_days (default 30, the maximum) |
a scheduled deletion, for 30 days, if noticed |
| AWS | account root keeps kms:* |
the key ever becoming undeletable |
The ForceNew coverage is the quietly valuable part. certify_key_spec and
encryption_key_spec are both ForceNew, so without prevent_destroy a
one-word edit plans a destroy-and-recreate and takes the private half with it.
The input validations make a wrong spec unrepresentable; prevent_destroy
makes a changed spec non-destructive.
Two honest limits¶
Stated because a control described as stronger than it is becomes a liability.
The key-policy layer is strong but not absolute. Administrator actions are
enumerated explicitly and exclude kms:PutKeyPolicy, so an administrator
cannot rewrite the policy to grant themselves deletion — a wildcard that grants
the power to rewrite the grant would not be a guardrail at all. What remains is
the account root, which retains kms:* as break-glass, and the automation role,
whose changes flow through a reviewed plan/apply.
prevent_destroy from a variable is OpenTofu-only. Terraform rejects a
non-literal there. Verified against OpenTofu 1.12.5, where the destroy is
correctly refused.
See also¶
- Retire an identity — the procedure.
- The two-key structure — why the fingerprint is the identity.