Key policies¶
Both KMS keys carry a policy that is the single source of permission grants. Neither IAM role has an attached identity policy, so there is exactly one place to read to know who can do what.
Four principal classes¶
Each policy grants the same four classes, mirroring the sibling signing module:
| Class | Gets | Explicitly does not get |
|---|---|---|
| account root | kms:* |
whatever the Deny statements below withhold |
key_administrator_arns |
key lifecycle: describe, enable/disable, tag, aliases | kms:Sign, kms:DeriveSharedSecret, kms:PutKeyPolicy |
automation_role_arn |
manage the key as an OpenTofu resource | kms:Sign, kms:DeriveSharedSecret |
| the reader / certifier role | exactly one crypto action each | the other one |
The separation that matters most is the third row. The apply pipeline can create, tag and re-policy these keys, and cannot use them directly. Because it can re-policy them, a compromise able to act as the apply role could grant itself either crypto action, so the runner holding that role needs protecting as if it held the keys. No other principal can change a key policy.
Why account root keeps kms:*¶
This looks like an over-broad grant and is deliberate. It is AWS's recommended break-glass provision, and it is what guarantees the key can never become unmanageable. A key whose policy has locked out every administrator is unrecoverable in a way that no amount of care prevents.
It is scoped to the root principal specifically, rather than the whole account, which is a narrower grant than the AWS default "Enable IAM policies" statement.
The Deny statements¶
Each key carries two:
- the key's one crypto operation (
kms:Signon the primary,kms:DeriveSharedSecretandkms:Decrypton the subkey) is denied to every principal but its role; kms:PutKeyPolicyis denied to every principal but the automation role.
They hold whatever any IAM policy in the account allows, and they bind the account root too.
Why aws:PrincipalArn, and not NotPrincipal. An earlier version of this
page rejected Deny statements altogether, and the reasoning still holds for the
form it considered. Deny with NotPrincipal binds a principal's unique ID: if
the excepted role is ever deleted and recreated, the new role is a different
principal, everything is denied, and the key is bricked.
These statements use Principal: "*" with an ArnNotEquals condition on
aws:PrincipalArn instead. That compares the role's ARN as a string, so a role
recreated under the same name and path is excepted again. Revised 2026-10-15.
Break-glass¶
The account root keeps kms:*, so the key can never be orphaned. Recovery goes
through the automation role:
- Policy changes come only from the automation role, through a reviewed plan and apply. Applying this module must run as that role. KMS refuses a policy that would stop its caller changing it again, so an apply under any other role fails rather than locking anyone out.
- If the automation role is deleted, recreate it with the same name and path, and it can apply again.
- If the reader or certifier role is deleted, recreate it the same way to restore decryption or certification.
- Administrators can still disable the key, cancel a deletion, and manage aliases and tags without the automation role.
Why resources = "*"¶
Every statement uses resources = ["*"], which security scanners flag. In a
key policy that wildcard resolves to the single key the policy is attached
to. Key policies are resource-based, not identity-based, so there is no
"arbitrary resources" reading available.
The module carries checkov:skip annotations with that justification inline
rather than suppressing the checks globally, so a reviewer sees the reasoning
at the statement rather than in a config file somewhere else.
Administrator deletion rights¶
kms:ScheduleKeyDeletion is withheld from administrators unless
allow_administrator_key_deletion is set. AWS's named control for accidental
key deletion is the key policy, and deleting an asymmetric encryption key is
the one mistake here that cannot be undone.
kms:DeleteAlias is granted regardless, because aliases are cheap to recreate.
kms:CancelKeyDeletion is always granted because cancelling is protective
rather than destructive.
[!NOTE] Administrator actions are enumerated explicitly rather than by wildcard, and
kms:PutKeyPolicyis not among them, so an administrator cannot rewrite the policy to grant themselves deletion. The sibling signing module'skms:Put*would have permitted exactly that; a wildcard granting the power to rewrite the grant is not a guardrail.The control is still not absolute: the account root holds
kms:*, which covers deletion, and the automation role can change the policy through a reviewed plan/apply.
A caveat about testing¶
These policies are reviewed but, at the time of writing, unexercised against
real AWS. Nothing in the module's gate can catch a policy that is too narrow:
tofu validate checks shape, and checkov and trivy check for grants that are
too broad. A role that cannot perform the operation it exists for passes all
three.
One such defect was found by reading rather than by testing (see roles and trust). Expect the first real use to be the first genuine exercise of these policies, and treat a permissions error as a likely module bug rather than a KMS limitation.
See also¶
- IAM permissions reference: the exact statements.
- Rotation, retention and deletion