KMS AccessDeniedException: Fixing Key Policy and Grant Misconfigurations
2026-04-08 · 8 min read
You are migrating an application to a new AWS account and suddenly S3 reads start failing with this error:
An error occurred (AccessDeniedException) when calling the Decrypt operation:
The ciphertext refers to a customer master key that does not exist, does not
exist in this region, or you are not allowed to access.
Or perhaps your EBS volume snapshot copy fails with:
An error occurred (AccessDeniedException) when calling the CreateGrant
operation: User: arn:aws:iam::123456789012:role/MyRole is not authorized
to perform: kms:CreateGrant on resource: arn:aws:kms:us-east-1:...
KMS AccessDeniedException is one of the most confusing AWS errors because KMS has a dual authorization model. Unlike most AWS services where IAM policies alone control access, KMS requires authorization from both the key policy and IAM policies. Miss either one and you get an AccessDeniedException with a message that does not tell you which layer blocked you.
After years of debugging KMS issues for clients, I have found that the root cause falls into one of five categories. Here is how to diagnose and fix each one.
Understanding the KMS Authorization Model
Before diving into troubleshooting, you need to understand how KMS authorization differs from standard IAM. KMS uses a two-layer model:
- Key Policy — The resource-based policy attached directly to the KMS key. This is the primary authorization mechanism.
- IAM Policies — Standard IAM policies attached to the calling principal (user, role, or group).
The critical point: a KMS key policy can either grant access directly or delegate access decisions to IAM. If the key policy does not include the root account statement that enables IAM policies, then IAM policies alone cannot grant access — no matter what permissions they contain.
Root Cause 1: Key Policy Does Not Enable IAM Policies
This is the single most common cause of KMS AccessDeniedException. When you create a KMS key through the console, AWS automatically adds this statement to the key policy:
{
"Sid": "Enable IAM policies",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
}
This statement does not grant access to anyone directly. It tells KMS to evaluate IAM policies for authorization decisions. Without it, only principals explicitly named in the key policy can use the key.
Diagnosis
Retrieve the key policy and check for the root account statement:
aws kms get-key-policy \
--key-id arn:aws:kms:us-east-1:123456789012:key/mrk-abc123 \
--policy-name default \
--output text | python3 -m json.tool
Look for a statement with "Principal": {"AWS": "arn:aws:iam::ACCOUNT_ID:root"}. If it is missing, IAM policies are being ignored for this key.
Solution
Add the root account statement to the key policy. You need to be an existing key administrator to do this:
# First, get the current policy
aws kms get-key-policy \
--key-id mrk-abc123 \
--policy-name default \
--output text > /tmp/key-policy.json
# Edit the policy to add the root statement, then apply it
aws kms put-key-policy \
--key-id mrk-abc123 \
--policy-name default \
--policy file:///tmp/key-policy.json
Root Cause 2: Missing Specific KMS Permissions in IAM
Even when the key policy enables IAM policies, you need the correct KMS actions. Different operations require different permissions, and this is where many teams get it wrong.
Here is a mapping of common operations to required KMS permissions:
| Operation | Required KMS Permissions |
|---|---|
| Read encrypted S3 object | kms:Decrypt |
| Write encrypted S3 object | kms:GenerateDataKey, kms:Decrypt |
| Copy encrypted EBS snapshot | kms:CreateGrant, kms:Decrypt, kms:ReEncryptFrom |
| Start EC2 from encrypted AMI | kms:CreateGrant, kms:Decrypt |
| Read encrypted RDS snapshot | kms:Decrypt, kms:DescribeKey |
Diagnosis
Check what KMS permissions the calling role has:
# Simulate the KMS action to see if IAM allows it
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/MyRole \
--action-names kms:Decrypt kms:GenerateDataKey kms:CreateGrant kms:DescribeKey \
--resource-arns arn:aws:kms:us-east-1:123456789012:key/mrk-abc123 \
--query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}'
Solution
Add the required KMS permissions to the IAM policy. Here is a policy that covers the most common encryption operations:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowKMSOperations",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext",
"kms:DescribeKey",
"kms:ReEncryptFrom",
"kms:ReEncryptTo"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123"
}
]
}
For operations that require grants (EBS, RDS, etc.), also add:
{
"Sid": "AllowKMSGrants",
"Effect": "Allow",
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
Root Cause 3: Cross-Account KMS Access Not Configured
Accessing a KMS key in another AWS account requires configuration on both sides — the key policy in the key's account and the IAM policy in the calling account. Missing either side causes AccessDeniedException.
Diagnosis
Identify whether cross-account access is involved by comparing the key's account with the calling role's account:
# Get the key's account from its ARN
aws kms describe-key \
--key-id arn:aws:kms:us-east-1:111111111111:key/mrk-abc123 \
--query 'KeyMetadata.{Account:AWSAccountId,KeyId:KeyId,KeyState:KeyState}'
If the account ID in the key ARN differs from your caller's account, you need cross-account setup.
Solution
In the key owner's account, the key policy must allow the external account:
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222222222222:root"
},
"Action": [
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey"
],
"Resource": "*"
}
In the calling account, the IAM policy must allow the same actions on the cross-account key ARN. Both sides are required.
Root Cause 4: KMS Key Is Disabled or Pending Deletion
A KMS key that is disabled or scheduled for deletion will reject all cryptographic operations with AccessDeniedException. The error message does not always clearly indicate the key state.
Diagnosis
Check the key state:
aws kms describe-key \
--key-id mrk-abc123 \
--query 'KeyMetadata.{KeyState:KeyState,DeletionDate:DeletionDate,Enabled:Enabled,Description:Description}'
The KeyState field will be one of: Enabled, Disabled, PendingDeletion, PendingImport, or Unavailable.
Solution
If the key is disabled, re-enable it:
aws kms enable-key --key-id mrk-abc123
If the key is pending deletion, cancel the deletion (if still within the waiting period):
aws kms cancel-key-deletion --key-id mrk-abc123
aws kms enable-key --key-id mrk-abc123
Root Cause 5: Missing KMS Grants for AWS Services
Several AWS services use KMS grants rather than IAM policies for encryption operations. When EBS encrypts a volume, RDS encrypts a snapshot, or Lambda decrypts environment variables, the service creates a KMS grant on your behalf. If the calling principal lacks kms:CreateGrant permission, these service operations fail.
Diagnosis
List existing grants on the key to see if the expected service grant exists:
aws kms list-grants \
--key-id mrk-abc123 \
--query 'Grants[*].{
GrantId:GrantId,
Grantee:GranteePrincipal,
Operations:Operations,
RetiringPrincipal:RetiringPrincipal
}'
Solution
Ensure the calling principal has kms:CreateGrant permission with the kms:GrantIsForAWSResource condition. This limits grant creation to only AWS services, following the principle of least privilege.
You can also create a grant manually for testing:
aws kms create-grant \
--key-id mrk-abc123 \
--grantee-principal arn:aws:iam::123456789012:role/MyRole \
--operations Decrypt GenerateDataKey \
--retiring-principal arn:aws:iam::123456789012:role/MyRole
Systematic Debugging Approach
When you encounter a KMS AccessDeniedException, follow this checklist in order:
# 1. Identify the key
aws kms describe-key --key-id KEY_ID \
--query 'KeyMetadata.{State:KeyState,Account:AWSAccountId,Manager:KeyManager}'
# 2. Check the key policy
aws kms get-key-policy --key-id KEY_ID --policy-name default --output text
# 3. Check IAM permissions of the caller
aws iam simulate-principal-policy \
--policy-source-arn CALLER_ARN \
--action-names kms:Decrypt kms:GenerateDataKey \
--resource-arns KEY_ARN
# 4. Check existing grants
aws kms list-grants --key-id KEY_ID
# 5. If cross-account, verify both sides
Prevention Best Practices
Use AWS-Managed Keys When Possible
If you do not need cross-account access or custom key policies, use AWS-managed keys (alias aws/s3, aws/ebs, etc.). They handle authorization automatically through IAM policies alone.
Centralize Key Management
Maintain a key management strategy that documents which keys are used by which services and which principals have access. Use tags to track key ownership:
aws kms tag-resource \
--key-id mrk-abc123 \
--tags \
TagKey=Owner,TagValue=platform-team \
TagKey=Environment,TagValue=production \
TagKey=Application,TagValue=payment-service
Monitor Key Usage With CloudTrail
All KMS API calls are logged in CloudTrail. Set up alerts for AccessDeniedException events to catch misconfigurations early:
aws logs create-metric-filter \
--log-group-name CloudTrail/DefaultLogGroup \
--filter-name KMSAccessDenied \
--filter-pattern '{ $.eventSource = "kms.amazonaws.com" && $.errorCode = "AccessDeniedException" }' \
--metric-transformations \
metricName=KMSAccessDeniedCount,metricNamespace=Security,metricValue=1
Test Key Access Before Deploying
Add a validation step that verifies KMS access before deploying resources that depend on encryption:
# Test decrypt access
aws kms decrypt \
--key-id mrk-abc123 \
--ciphertext-blob fileb://test-encrypted.bin \
--query 'KeyId' 2>&1 || echo "KMS access check failed"
Need Help Securing Your AWS Encryption?
KMS misconfigurations are a top cause of deployment failures and data access issues in AWS environments. We have helped dozens of organizations untangle their KMS key policies and establish encryption best practices. If you are struggling with AccessDeniedException errors or need to design a cross-account encryption strategy, we can help.
Need help with your AWS infrastructure?
Book a free 30-minute consultation to discuss your challenges.