KMS Key Rotation in Terraform: enable_key_rotation and Key Policies

Last reviewed: 2026-05-27 · 8 min read

Rost Mironenko
Rost Mironenko·Founder, ArchGuard

5+ years AWS engineering · Open-source contributor

Last reviewed: 2026-05-27

KMS key rotation in Terraform requires two things: creating a customer-managed key with enable_key_rotation = true, and writing a key policy that restricts which principals can use, manage, and grant access to the key. A KMS key without rotation enabled is a finding in the AWS Well-Architected Security pillar (SEC08-BP03). A KMS key without a restrictive policy is a finding in SEC03.

Step 1 — Create a KMS key with rotation enabled

The AWS KMS automatic key rotation documentation notes that rotation creates new key material annually while retaining the ability to decrypt data encrypted with any previous version. The key ARN does not change after rotation.

kms.tf✗ Before
resource "aws_kms_key" "bad" {  description = "App encryption key"  # enable_key_rotation not set — defaults to false}
kms.tf✓ After
resource "aws_kms_key" "main" {  description             = "${var.env} application encryption key"  deletion_window_in_days = 30  enable_key_rotation     = true  tags = {    Environment = var.env    ManagedBy   = "terraform"  }}resource "aws_kms_alias" "main" {  name          = "alias/${var.env}-app"  target_key_id = aws_kms_key.main.key_id}

Step 2 — Write a restrictive key policy

The default KMS key policy grants the AWS account root principal full access to the key. This means any IAM principal in the account can be granted KMS access through IAM policies alone. A restrictive key policy requires that KMS access is both allowed by the key policy and by IAM — defence in depth at the key level.

kms.tf✓ After
data "aws_iam_policy_document" "kms_key_policy" {  # Required: allow root to manage key (prevents lockout)  statement {    sid    = "EnableRootAccess"    effect = "Allow"    principals {      type        = "AWS"      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]    }    actions   = ["kms:*"]    resources = ["*"]  }  # Key administrators: can manage the key but not encrypt/decrypt  statement {    sid    = "AllowKeyAdministration"    effect = "Allow"    principals {      type        = "AWS"      identifiers = var.key_admin_role_arns    }    actions = [      "kms:Create*",      "kms:Describe*",      "kms:Enable*",      "kms:List*",      "kms:Put*",      "kms:Update*",      "kms:Revoke*",      "kms:Disable*",      "kms:Get*",      "kms:Delete*",      "kms:ScheduleKeyDeletion",      "kms:CancelKeyDeletion",    ]    resources = ["*"]  }  # Key users: can encrypt and decrypt  statement {    sid    = "AllowKeyUse"    effect = "Allow"    principals {      type        = "AWS"      identifiers = var.key_user_role_arns    }    actions = [      "kms:Decrypt",      "kms:DescribeKey",      "kms:Encrypt",      "kms:GenerateDataKey*",      "kms:ReEncrypt*",    ]    resources = ["*"]  }}resource "aws_kms_key_policy" "main" {  key_id = aws_kms_key.main.id  policy = data.aws_iam_policy_document.kms_key_policy.json}

Separate administrator and user statements follow the principle of least privilege at the key level

Step 3 — Attach the key to services that encrypt data

Reference the KMS alias ARN (not the raw key ID) in service configurations. The alias ARN is stable and human-readable in the AWS console. The services below each require an additional IAM grant in the key policy allowing the service principal to use the key.

encrypted-resources.tf
# RDS encrypted with customer-managed KMS keyresource "aws_db_instance" "main" {  identifier        = "${var.env}-db"  engine            = "postgres"  instance_class    = "db.t4g.medium"  storage_encrypted = true  kms_key_id        = aws_kms_key.main.arn  # ... other settings}# Secrets Manager secret encrypted with customer-managed KMS keyresource "aws_secretsmanager_secret" "db_password" {  name       = "${var.env}/db/password"  kms_key_id = aws_kms_alias.main.arn}# EBS volume encrypted with customer-managed KMS keyresource "aws_ebs_volume" "data" {  availability_zone = "${var.region}a"  size              = 100  encrypted         = true  kms_key_id        = aws_kms_key.main.arn}

Multi-region KMS keys: when to use them

Multi-region keys replicate the same key material across AWS regions. They are required when your workload replicates encrypted data: S3 cross-region replication, DynamoDB Global Tables, or disaster recovery scenarios where a secondary region must decrypt data encrypted in the primary. For single-region workloads, multi-region keys add cost and complexity without benefit.

kms-multiregion.tf
# Primary key in us-east-1resource "aws_kms_key" "primary" {  provider = aws.us_east_1  description         = "Multi-region primary key"  multi_region        = true  enable_key_rotation = true}# Replica in eu-west-1resource "aws_kms_replica_key" "eu" {  provider = aws.eu_west_1  description             = "Multi-region replica"  primary_key_arn         = aws_kms_key.primary.arn  deletion_window_in_days = 30  enabled                 = true}

Cost tradeoffs

KMS pricing at a glance (as of 2026)

  • Customer-managed key (CMK)$1.00/month per key
  • Multi-region key replica$1.00/month per replica
  • API requests (Encrypt, Decrypt, GenerateDataKey)$0.03 per 10,000 requests
  • S3 Bucket Key (bucket_key_enabled = true)Reduces KMS API calls by up to 99%

Source: AWS KMS pricing

Related Security pillar articles

Frequently asked questions

What does enable_key_rotation = true do in Terraform?

It enables automatic annual key rotation for the KMS customer-managed key. AWS generates new key material every year. Data encrypted with the old key material remains decryptable — KMS retains previous versions. Only customer-managed keys (CMKs) support automatic rotation; AWS-managed keys rotate automatically on a 3-year schedule.

Does KMS key rotation change the key ARN?

No. The key ARN and key ID remain the same after rotation. AWS creates new underlying key material but the key identifier is unchanged. Applications and Terraform resources that reference the key ARN do not need to be updated.

What is a KMS key policy and why does it matter?

A KMS key policy is a resource-based policy attached to the key itself. Unlike IAM policies, KMS key policies are the primary access control mechanism — if the key policy does not explicitly allow an IAM principal, IAM policies alone cannot grant access. The default key policy grants full access to the account root; production key policies should restrict this.

When should I use multi-region KMS keys in Terraform?

Use multi-region keys when your workload replicates data across AWS regions — for example, S3 cross-region replication or DynamoDB Global Tables. A multi-region key allows the same key material to decrypt data in multiple regions. For single-region workloads, single-region keys are simpler and sufficient.

Get an automated KMS configuration review

ArchGuard reviews your Terraform for KMS and encryption-at-rest gaps across the AWS Well-Architected Security pillar and delivers a branded PDF in 24 hours.

See how it works