TerraformPilot

DevOps

Fix Terraform Error: KMS Key AccessDeniedException

Fix terraform KMS AccessDeniedException errors. Update KMS key policies, add IAM permissions for kms:CreateGrant and kms:Decrypt

LLuca Berton1 min read

Quick Answer

#
{
  "Sid": "Allow Terraform to use the key",
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::123456789012:role/terraform" },
  "Action": ["kms:CreateGrant", "kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey*"],
  "Resource": "*"
}

The Error

#
Error: creating EC2 Instance: AccessDeniedException:
User: arn:aws:iam::123456789012:role/terraform is not authorized to
perform: kms:CreateGrant on resource: arn:aws:kms:us-east-1:123456789012:key/mrk-abc123

Or:

Error: creating RDS Cluster: AccessDenied:
The cross-account KMS key access is not allowed

What Causes This

#

KMS access requires both an IAM policy AND a key policy to allow access. Missing either one blocks the operation.

  1. KMS key policy doesn't allow the IAM role — the key needs explicit permission
  2. IAM role lacks kms: permissions — even with key policy, IAM must also allow
  3. Cross-account KMS — extra grants needed for keys in different accounts
  4. Service-linked role needs CreateGrant — EBS, RDS, and EFS use kms:CreateGrant internally

Solution 1: Update KMS Key Policy

#
resource "aws_kms_key" "main" {
  description = "Encryption key for application"
 
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "Root account full access"
        Effect    = "Allow"
        Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }
        Action    = "kms:*"
        Resource  = "*"
      },
      {
        Sid       = "Terraform role access"
        Effect    = "Allow"
        Principal = { AWS = var.terraform_role_arn }
        Action = [
          "kms:CreateGrant",
          "kms:Decrypt",
          "kms:DescribeKey",
          "kms:Encrypt",
          "kms:GenerateDataKey*",
          "kms:ReEncrypt*"
        ]
        Resource = "*"
      }
    ]
  })
}

Solution 2: Add IAM Permissions

#
resource "aws_iam_role_policy" "kms_access" {
  name = "kms-access"
  role = aws_iam_role.terraform.id
 
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "kms:CreateGrant",
          "kms:Decrypt",
          "kms:DescribeKey",
          "kms:Encrypt",
          "kms:GenerateDataKey*",
          "kms:ReEncrypt*",
          "kms:ListGrants",
          "kms:RevokeGrant"
        ]
        Resource = [
          aws_kms_key.main.arn,
          # Or specific key ARN:
          # "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123"
        ]
      }
    ]
  })
}

Which Services Need Which KMS Permissions

#
ServiceRequired KMS Actions
EBS volumesCreateGrant, Decrypt, DescribeKey, GenerateDataKey*
RDS / AuroraCreateGrant, Decrypt, DescribeKey, GenerateDataKey*
S3 SSE-KMSDecrypt, GenerateDataKey*, DescribeKey
Secrets ManagerDecrypt, DescribeKey, GenerateDataKey*
EFSCreateGrant, Decrypt, DescribeKey, GenerateDataKey*
Lambda env varsDecrypt, DescribeKey

Solution 3: Cross-Account KMS Access

#
# Account A (key owner) — key policy
{
  Sid       = "Allow Account B"
  Effect    = "Allow"
  Principal = { AWS = "arn:aws:iam::222222222222:root" }
  Action = [
    "kms:CreateGrant",
    "kms:Decrypt",
    "kms:DescribeKey",
    "kms:GenerateDataKey*"
  ]
  Resource  = "*"
}
 
# Account B (consumer) — IAM policy must ALSO allow
resource "aws_iam_role_policy" "cross_account_kms" {
  policy = jsonencode({
    Statement = [{
      Effect   = "Allow"
      Action   = ["kms:CreateGrant", "kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey*"]
      Resource = "arn:aws:kms:us-east-1:111111111111:key/mrk-abc123"
    }]
  })
}

Debugging

#
# Check who you're running as
aws sts get-caller-identity
 
# Simulate the KMS permission
aws kms describe-key --key-id arn:aws:kms:us-east-1:123456789012:key/mrk-abc123
 
# Check key policy
aws kms get-key-policy --key-id mrk-abc123 --policy-name default
 
# List grants
aws kms list-grants --key-id mrk-abc123

Hands-On Courses

#

Conclusion

#

KMS access requires both a KMS key policy AND IAM policy. Add kms:CreateGrant, kms:Decrypt, kms:GenerateDataKey*, and kms:DescribeKey to both. For cross-account access, both the key policy and the consuming account's IAM policy must explicitly grant access.

#Terraform#Troubleshooting#DevOps#Error Fix#AWS#Security

Share this article