RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
KMS CreateGrant Grantee Principal Resolver
02. Root Cause Vector

An IAM principal lacks kms:CreateGrant permission or violates key policy conditions, blocking delegation of KMS key usage to a grantee pr...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Security

KMS CreateGrant Grantee Principal Resolver

An IAM principal lacks kms:CreateGrant permission or violates key policy conditions, blocking delegation of KMS key usage to a grantee principal.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(43L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/bash
# TODO: This script is a mess after the PCI migration. Need to refactor but no time.
# Legacy KMS key from old PCI zone, still referenced in prod because of compliance.
export LEGACY_KMS_KEY="arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-90ef-ghij-klmnopqrstuv"
# New IAM role for the lambda, but the trust policy might be off...
export LAMBDA_ROLE_ARN="arn:aws:iam::123456789012:role/prod-lambda-execution-role"
# Dummy secret for the legacy database connection (should be in Secrets Manager, but...)
export DB_URI="postgresql://admin:SuperSecretPassw0rd!@pci-secure-zone.internal:5432/legacy_pci_db"
# Stripe test key left in from a POC - TODO: rotate this out
export STRIPE_API_KEY="sk_test_51Np9XwFakeKey9876543210abcdefghijklm"
# Old SSH key for bastion access (should be deprecated)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArzHc6fFakeKeyBase64DataHereForDemoPurposesOnlyNotReal\n-----END RSA PRIVATE KEY-----"

# Function to create a KMS grant with a grantee principal - this is called from a Lambda handler elsewhere
grant_kms_access() {
    local GRANTEE_PRINCIPAL="$1"
    local KEY_ID="$2"
    # VULNERABILITY: The grantee principal is being resolved incorrectly due to a hardcoded ARN mismatch from the old PCI zone.
    # The script assumes the role ARN format matches, but the lambda role is in a different partition (aws-cn vs aws).
    # This will cause an AccessDenied because the principal doesn't exist in the key's policy context.
    aws kms create-grant \
        --key-id "$KEY_ID" \
        --grantee-principal "$GRANTEE_PRINCIPAL" \
        --operations "Decrypt" "DescribeKey" \
        --region us-east-1 \
        --profile legacy-pci-profile  # This profile might have stale credentials
}

# Main execution - this is triggered by a CloudWatch event in a rush job
# TODO: The event source mapping is using an old ARN from the pre-migration era, causing principal resolution failures.
GRANTEE="arn:aws:iam::123456789012:role/prod-lambda-execution-role"  # This is in aws partition, but key expects aws-cn?
# Actually, the key is in aws-cn due to China region legacy, but we never updated the ARN here.
# Commented out old line: # GRANTEE="arn:aws-cn:iam::123456789012:role/old-pci-lambda-role"
grant_kms_access "$GRANTEE" "$LEGACY_KMS_KEY"

# Red herring: extra config that doesn't get used but clutters the script
export REDUNDANT_AWS_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
export REDUNDANT_AWS_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# TODO: Clean up these unused exports after audit

# End of script - no error handling because "it works on my machine"
echo "Grant creation attempted. Check CloudTrail for AccessDenied errors."
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

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 CreateGrant is denied because either the KMS key policy does not grant kms:CreateGrant to that caller, the required kms:GrantIsForAWSResource condition is absent, or the grantee principal ARN is malformed/cross-account without explicit trust.
  • How to fix it: Add kms:CreateGrant to the key policy for the calling principal; attach kms:GrantIsForAWSResource condition 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.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

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.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

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:CreateGrant without the kms:GrantIsForAWSResource condition, 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

  1. Key policy missing kms:CreateGrant for the calling role — most common.
  2. kms:GrantIsForAWSResource condition missing — AWS services require this condition to be present before they accept grants.
  3. Grantee principal ARN does not exist or is malformed — KMS validates the principal at grant creation time.
  4. 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 infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

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.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does AWS require kms:GrantIsForAWSResource when using CreateGrant for services like RDS or EBS?
The root cause is a critical security boundary. AWS services like RDS operate under internal service principals. The `kms:GrantIsForAWSResource` condition enforces that a `CreateGrant` operation can only bind a key to these trusted, AWS-controlled principals—not to arbitrary IAM users or roles. This prevents a compromised IAM entity from exfiltrating key usage rights by creating grants for external, attacker-controlled principals, which would be a catastrophic credential leak. Understanding this nuance is what separates a functional configuration from a secure one.
Q2.The calling role has kms:CreateGrant in its IAM policy but still gets AccessDenied — why?
You've hit KMS's dual-authorization trap. Your IAM policy is irrelevant if the key's resource policy doesn't also explicitly allow `kms:CreateGrant` for your principal. Manually reconciling IAM policies with decentralized, often legacy key policies is a high-risk, error-prone process. A single misconfigured wildcard in a key policy can create a massive blast radius, granting unintended access. Stop guessing. Drop your broken IAM and key policy manifests into the StackEngine Local Sandbox above to automatically analyze the conflict and compile a precise, zero-trust fix that respects both control planes.
Q3.How do I debug which specific condition is causing the CreateGrant denial?
Manually debugging this with CloudTrail logs and the IAM Policy Simulator is a tactical nightmare for a single error. Now, extrapolate that across a mono-repo with hundreds of services and keys—it becomes a compliance and operational disaster. Manual processes cannot scale, guarantee consistency, or pass audit. For enterprise-scale, automated PR drafting under Maker-Checker review, upgrade to the StackEngine CI/CD Automator (Team Edition). It integrates directly into your CI/CD pipeline to perform on-prem, policy-as-code analysis and automatically generate compliant fixes for your entire infrastructure, turning security gaps into enforced policy.