Home / AWS

AWSTroubleshootingAdvanced

Cross-account KMS AccessDenied: shared S3 objects, EBS and RDS snapshots and AMIs that other accounts can't use

Another account can see your shared bucket, snapshot or AMI but fails with AccessDenied or an instance that terminates at launch. Fix the key policy, the consumer's IAM, grants and the AWS managed key trap.

Deepak Bhardwaj4 min read

Applies toAWS KMS customer managed keys, S3 SSE-KMS, EBS snapshots and AMIs, RDS/Aurora snapshots, AWS Organizations

Symptoms

  • A role in account B gets AccessDenied on GetObject from account A's bucket, even though the bucket policy allows it.
  • An AMI shared from account A launches in account B, then terminates immediately with state reason Client.InternalError: Client error on launch.
  • Account B can see a shared RDS snapshot but can't restore or copy it.
  • CloudTrail in account A shows Decrypt, GenerateDataKey or CreateGrant with errorCode: AccessDenied.

Encryption adds a second permission check. Access to the resource (bucket policy, snapshot share) is not enough; the caller also needs access to the KMS key.

Rule 1: AWS managed keys can't be shared

Keys like aws/s3, aws/ebs and aws/rds are AWS managed keys. Their key policy can't be changed, so no other account can use them. Anything encrypted with them can't be used cross-account.

aws kms describe-key --key-id <key-arn-or-alias> \
  --query "KeyMetadata.{manager:KeyManager,state:KeyState,arn:Arn}"

KeyManager: AWS means you must re-encrypt with a customer managed key (CMK) first:

  • S3: copy objects onto themselves with the new key (aws s3 cp s3://b/p s3://b/p --recursive --sse aws:kms --sse-kms-key-id <cmk>), or use S3 Batch Operations for large buckets.
  • EBS snapshot / AMI: aws ec2 copy-snapshot --encrypted --kms-key-id <cmk> or aws ec2 copy-image --encrypted --kms-key-id <cmk>.
  • RDS snapshot: aws rds copy-db-snapshot --kms-key-id <cmk> (or copy-db-cluster-snapshot).

Rule 2: both sides must allow it

Account Needs
A (key owner) Key policy statement allowing account B's principals (or account B root) the needed KMS actions
B (consumer) IAM policy on the role allowing those KMS actions on account A's key ARN

Key policy statement in account A:

{
  "Sid": "AllowAccountBUse",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::222233334444:root" },
  "Action": ["kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey*", "kms:ReEncrypt*", "kms:CreateGrant"],
  "Resource": "*",
  "Condition": { "Bool": { "kms:GrantIsForAWSResource": "true" } }
}

Put kms:CreateGrant with the GrantIsForAWSResource condition in its own statement if you also need Decrypt for direct calls (S3 reads don't use grants; EBS and RDS do).

IAM policy in account B:

{
  "Effect": "Allow",
  "Action": ["kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey*", "kms:ReEncrypt*", "kms:CreateGrant"],
  "Resource": "arn:aws:kms:eu-west-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
}

Tip

Granting the key to account B's root delegates the decision to account B's IAM. Nobody in B gets access until B's IAM also allows it. That is the normal, safe pattern.

Per-service details

S3

  • Bucket policy (A) allows B's role s3:GetObject; key policy (A) allows kms:Decrypt; B's role allows both.
  • Writing from B into A's bucket: B needs kms:GenerateDataKey, and object ownership should be "bucket owner enforced" so A owns the objects.
  • If A uses S3 Bucket Keys, the encryption context is the bucket ARN, not the object ARN; key policy conditions on kms:EncryptionContext:aws:s3:arn must match.

EBS snapshots and AMIs

Launching from a shared encrypted AMI needs B's role to use A's key through EC2, which creates a grant. Missing kms:CreateGrant is the classic cause of "terminated at launch".

Best practice: B copies the shared AMI or snapshot and re-encrypts it with B's own key:

aws ec2 copy-image --source-region eu-west-1 --source-image-id ami-0shared \
  --name app-golden-copy --encrypted --kms-key-id arn:aws:kms:eu-west-1:222233334444:key/<b-key>

Then B no longer depends on A's key staying enabled.

RDS and Aurora snapshots

  • Share the snapshot: aws rds modify-db-snapshot-attribute --db-snapshot-identifier snap1 --attribute-name restore --values-to-add 222233334444.
  • The snapshot must be encrypted with a CMK whose policy allows B.
  • B copies it with its own key, then restores from the copy.

Step-by-step diagnosis

  1. Which key? aws s3api head-object --bucket b --key k --query SSEKMSKeyId, aws ec2 describe-snapshots --snapshot-ids snap-x --query "Snapshots[0].KmsKeyId".
  2. Is it AWS managed? (KeyManager)
  3. In account A's CloudTrail, find the failing call:
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventSource,AttributeValue=kms.amazonaws.com \
  --max-results 50 --query "Events[?contains(CloudTrailEvent, 'AccessDenied')].CloudTrailEvent" --output text | head -c 3000
  1. Read userIdentity (who), eventName (which action) and errorMessage (which policy type denied).
  2. Check for SCPs or permission boundaries in B, and for key policy conditions (kms:ViaService, aws:PrincipalOrgID) that don't match.

Prevent it next time

  • Never use AWS managed keys for anything that may be shared: backups, AMIs, data exchange buckets.
  • Create a "sharing" CMK per data domain with a key policy scoped to your organisation (aws:PrincipalOrgID).
  • Consumers copy and re-encrypt shared artefacts with their own keys.
  • Keep key policies in code and alert on ScheduleKeyDeletion and DisableKey for shared keys.