How to Scope an AWS KMS Key Policy to One IAM Role

An overly broad AWS KMS key policy - one that grants Decrypt to the account root - makes IAM least-privilege meaningless: any role in the account with kms:Decrypt in its policy can decrypt your data. Scoping the key policy to a specific role ARN is the second layer that actually enforces the boundary.

Security Engineerawskmskey-policy

Why KMS key policies override IAM

AWS evaluates KMS access as an AND of two checks: the key policy must say yes AND the IAM policy must say yes. But a key policy with "Principal": {"AWS": "arn:aws:iam::123456789012:root"} effectively delegates that yes/no decision entirely to IAM - any principal in the account that has kms:Decrypt in its IAM policy gets through.

The result: one careless IAM grant elsewhere blows open access to every key that uses the broad root principal. The fix is a key policy that names the specific role ARN in the Decrypt statement.

Read the current policy

KEY=$(cat /workspace/.key-id)
aws kms get-key-policy \
  --key-id "$KEY" \
  --policy-name default \
  --query Policy \
  --output text | python3 -m json.tool

A too-broad policy looks like this - the Decrypt statement lists the account root, not a specific role:

{
  "Statement": [
    {
      "Sid": "Enable IAM User Permissions",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
      "Action": "kms:*",
      "Resource": "*"
    }
  ]
}

Write the scoped policy

A correct two-statement policy keeps the required admin statement and adds a tightly scoped Decrypt/Encrypt grant for the consuming role only:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Enable IAM User Permissions",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
      "Action": "kms:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowDataPipelineDecrypt",
      "Effect": "Allow",
      "Principal": {"AWS": "arn:aws:iam::123456789012:role/data-pipeline"},
      "Action": ["kms:Decrypt", "kms:Encrypt"],
      "Resource": "*"
    }
  ]
}

The root statement (Statement 1) is required - removing it permanently locks admins out of the key. It cannot be recovered without AWS Support.

Apply the policy

aws kms put-key-policy \
  --key-id "$KEY" \
  --policy-name default \
  --policy file:///tmp/scoped.json

Verify with another get-key-policy call to confirm the root-only Decrypt is gone and only the role ARN remains in the second statement.

Going further with conditions

Production policies add Condition blocks for a tighter scope:

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What does granting kms:Decrypt to the account root actually mean?

It delegates the Decrypt decision to IAM - any principal in the account whose IAM policy includes kms:Decrypt can use the key. The key policy itself provides no additional restriction, making it equivalent to no key-level control.

Why must I keep the root allow-all statement in the KMS key policy?

Without a statement granting kms:* to the account root, no IAM principal - not even account admins - can manage the key. AWS cannot recover access to a key whose policy locks out all principals. Always keep the root admin statement.

How do I scope an AWS KMS key policy to one role?

Write a two-statement policy: Statement 1 keeps the required root kms:* admin grant; Statement 2 grants only kms:Decrypt (and kms:Encrypt if needed) with Principal set to the specific role ARN. Apply it with aws kms put-key-policy.

What is an AWS KMS key policy?

A KMS key policy is the resource-based policy attached to a KMS key that says which principals can use or manage it. It is the primary access control for the key - without an allow in the key policy, even an IAM admin cannot use it.

What is the difference between an IAM policy and a KMS key policy?

An IAM policy attaches to a user or role and grants permissions across services; a KMS key policy attaches to the key itself and is authoritative for that key. KMS access requires an allow in the key policy.

Keep learning

Close a SQL Injection in a Search EndpointSecurity projectNeutralize a Stored XSS in CommentsSecurity projectRemove Hardcoded Credentials From Source CodeSecurity projectSecurity roadmapStep by step to hiredSecurity interview questionsSTAR answersAll Security projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →