Fixing AWS KMS AccessDenied on CreateGrant: Resolving Grantee Principal Permission Errors
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10–20 mins
TL;DR
- What broke: The IAM principal calling
CreateGrantis denied because either the KMS key policy does not grantkms:CreateGrantto that caller, the requiredkms:GrantIsForAWSResourcecondition is absent, or the grantee principal ARN is malformed/cross-account without explicit trust. - How to fix it: Add
kms:CreateGrantto the key policy for the calling principal; attachkms:GrantIsForAWSResourcecondition when the grant is for an AWS service; validate the grantee principal ARN resolves to an existing IAM entity. - Fast path: Use our Client-Side Sandbox above to paste your key policy and get the refactored statement auto-generated without sending your ARNs to a third-party server.
The Incident (What Does the Error Mean?)
Raw error output:
An error occurred (AccessDenied) when calling the CreateGrant operation:
User: arn:aws:iam::123456789012:role/MyServiceRole is not authorized
to perform: kms:CreateGrant on resource:
arn:aws:kms:us-east-1:123456789012:key/mrk-abc123def456
with an explicit deny
Immediate consequence: The service role — typically an ECS task role, Lambda execution role, or EC2 instance profile — cannot delegate KMS key usage to downstream AWS services (e.g., RDS, S3 SSE-KMS, Secrets Manager). Encryption operations downstream fail silently or hard-crash depending on the service. In RDS or EBS contexts, this blocks volume attachment or database startup entirely. Production is down.
The Attack Vector / Blast Radius
kms:CreateGrant is a privileged escalation vector. A grant allows a grantee principal to use a KMS key for specific operations (Decrypt, GenerateDataKey, Encrypt) without being named in the key policy. This is why AWS enforces it tightly.
Why misconfiguration here is dangerous in both directions:
- Too permissive: If you add
kms:CreateGrantwithout thekms:GrantIsForAWSResourcecondition, any principal with that permission can issue grants to arbitrary external principals, including attacker-controlled IAM roles in other accounts. This is a privilege escalation path — an attacker who compromises your service role can grant your KMS key to their own infrastructure. - Too restrictive (current failure): The legitimate AWS service (e.g.,
rds.amazonaws.com) cannot receive a grant, so it cannot perform envelope encryption. Cascading failures: encrypted RDS instances won't start, EBS volumes won't attach, Secrets Manager rotations fail.
Blast radius: Every resource encrypted under this KMS key that relies on service-linked grant delegation is offline until resolved.
How to Fix It (The Solution)
Root Cause Checklist — Check in Order
- Key policy missing
kms:CreateGrantfor the calling role — most common. kms:GrantIsForAWSResourcecondition missing — AWS services require this condition to be present before they accept grants.- Grantee principal ARN does not exist or is malformed — KMS validates the principal at grant creation time.
- Cross-account grant without resource-based trust — the key policy in the owning account must explicitly allow the external account's principal.
Basic Fix — Key Policy Statement
{
"Version": "2012-10-17",
"Statement": [
- {
- "Sid": "AllowServiceRoleKeyUsage",
- "Effect": "Allow",
- "Principal": {
- "AWS": "arn:aws:iam::123456789012:role/MyServiceRole"
- },
- "Action": [
- "kms:Encrypt",
- "kms:Decrypt",
- "kms:GenerateDataKey"
- ],
- "Resource": "*"
- }
+ {
+ "Sid": "AllowServiceRoleKeyUsageAndGrant",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:aws:iam::123456789012:role/MyServiceRole"
+ },
+ "Action": [
+ "kms:Encrypt",
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ "kms:CreateGrant",
+ "kms:ListGrants",
+ "kms:RevokeGrant"
+ ],
+ "Resource": "*",
+ "Condition": {
+ "Bool": {
+ "kms:GrantIsForAWSResource": "true"
+ }
+ }
+ }
]
}
kms:GrantIsForAWSResource: true is non-negotiable. Without it, you are allowing unrestricted grant issuance.
Enterprise Best Practice — Least Privilege with Principal and Operation Constraints
{
"Sid": "AllowServiceRoleGrant",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/MyServiceRole"
},
- "Action": "kms:CreateGrant",
- "Resource": "*"
+ "Action": [
+ "kms:CreateGrant",
+ "kms:ListGrants",
+ "kms:RevokeGrant"
+ ],
+ "Resource": "*",
+ "Condition": {
+ "Bool": {
+ "kms:GrantIsForAWSResource": "true"
+ },
+ "StringEquals": {
+ "kms:ViaService": "rds.us-east-1.amazonaws.com"
+ },
+ "ArnLike": {
+ "aws:PrincipalArn": "arn:aws:iam::123456789012:role/MyServiceRole"
+ }
+ }
}
kms:ViaService locks grant creation to a specific AWS service. If your key is shared across services, use a StringLike with a wildcard: "rds.*.amazonaws.com". This prevents the service role from being used to issue grants outside its intended service boundary.
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov — Block Key Policies Missing kms:GrantIsForAWSResource
Checkov rule CKV_AWS_7 checks KMS key rotation but does not natively check grant conditions. Write a custom check or use OPA:
# OPA/Conftest policy — deny CreateGrant without GrantIsForAWSResource condition
deny[msg] {
stmt := input.Statement[_]
stmt.Action[_] == "kms:CreateGrant"
not stmt.Condition.Bool["kms:GrantIsForAWSResource"]
msg := sprintf(
"KMS key policy statement '%v' allows kms:CreateGrant without kms:GrantIsForAWSResource condition.",
[stmt.Sid]
)
}
2. Terraform — Enforce Condition in aws_kms_key Policy
# In your aws_kms_key policy document, always include:
condition {
test = "Bool"
variable = "kms:GrantIsForAWSResource"
values = ["true"]
}
Run terraform validate + checkov -d . in your PR pipeline. Block merge on any KMS policy statement containing kms:CreateGrant without the condition.
3. AWS Config Rule
Enable kms-cmk-not-scheduled-for-deletion and write a custom AWS Config rule using boto3 to evaluate key policies on change events (PutKeyPolicy) and alert if CreateGrant is present without kms:GrantIsForAWSResource.
4. IAM Access Analyzer
Enable IAM Access Analyzer with KMS key findings. It will flag any key policy that allows kms:CreateGrant to external principals or without restrictive conditions as a HIGH finding automatically.