Skip to content

DynamoDB tables should have AWS KMS encryption enabled

DynamoDB tables are encrypted at rest by default with AWS-owned keys, but those keys do not appear in your AWS account, cannot be audited through CloudTrail, and cannot be rotated or revoked by you. Switching to an AWS KMS key (either the aws/dynamodb managed key or a customer-managed CMK) gives you visibility into key usage via CloudTrail, lets you define key policies, and lets you disable or schedule deletion of the key to render data unreadable.

For regulated workloads, auditors frequently require proof that encryption keys are under customer control. AWS-owned keys do not satisfy that requirement because you cannot demonstrate independent key management.

Retrofit consideration

Changing the encryption key type on an existing DynamoDB table triggers a re-encryption process. For large tables this can take significant time and may briefly increase read/write costs. Tables with global table replicas require the KMS key to be available in every replica region.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "dynamodb_table" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "hipaa.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "gdpr.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "nist80053.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "nist800171.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "cisacyberessentials.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "nydfs23.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "eugmpannex11.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "cfrpart11.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "rbicybersecurity.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "hipaasecurity2003.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "nistcsfv11.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "nist80053rev4.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

module "dynamodb_table" {
  source  = "pcidssv321.compliance.tf/terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

If you use terraform-aws-modules/dynamodb-table/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "dynamodb_table" {
  source  = "terraform-aws-modules/dynamodb-table/aws"
  version = ">=5.0.0"

  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  hash_key = "id"
  name     = "abc123"

  server_side_encryption_enabled     = true
  server_side_encryption_kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}

Use AWS provider resources directly. See docs for the resources involved: aws_dynamodb_table.

resource "aws_dynamodb_table" "this" {
  attribute {
    name = "id"
    type = "S"
  }

  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "id"
  name         = "pofix-abc123"

  server_side_encryption {
    enabled     = true
    kms_key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
  }
}

What this control checks

The control checks that aws_dynamodb_table includes a server_side_encryption block with enabled = true. Without kms_key_arn, DynamoDB uses the AWS-managed key aws/dynamodb. To use a customer-managed key, set kms_key_arn to the ARN of your aws_kms_key resource. A table that omits the server_side_encryption block entirely, or sets enabled = false, fails this control, even though DynamoDB still encrypts data at rest (with an AWS-owned key) by default. If you reference a customer-managed key, the key policy must grant dynamodb.amazonaws.com the kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, kms:DescribeKey, and kms:CreateGrant actions.

Common pitfalls

  • Omitting server_side_encryption defaults to AWS-owned key

    Without a server_side_encryption block in aws_dynamodb_table, Terraform does not configure KMS encryption. The table silently falls back to the AWS-owned key, which won't satisfy any control requiring customer-visible KMS.

  • Global tables require KMS keys in every replica region

    Each replica in an aws_dynamodb_table with replica blocks needs a KMS key available in its own region. Omit the replica KMS settings and encryption behavior becomes provider-dependent. Replicas can end up non-compliant for controls that require customer-visible KMS keys without any obvious error during terraform apply.

  • KMS key policy missing DynamoDB grants

    Get the key policy wrong and DynamoDB will throw AccessDeniedException on the first table operation. The policy must grant kms:CreateGrant with a GrantIsForAWSResource condition to dynamodb.amazonaws.com, along with the full set of encrypt and decrypt actions.

  • Terraform plan shows replacement on key change

    Switching encryption keys on an existing table triggers an in-place re-encryption, not a resource replacement. Duration scales with table size, so don't assume the change is complete just because terraform apply finished. Check the UpdateTable API status to confirm re-encryption is done before proceeding.

Audit evidence

An auditor expects AWS Config rule results (such as dynamodb-table-encrypted-kms) showing all DynamoDB tables as compliant. The DescribeTable API output for each table should show SSEDescription.SSEType as KMS and SSEDescription.Status as ENABLED, with KMSMasterKeyArn pointing to an active key. CloudTrail will contain Encrypt and Decrypt events tied to DynamoDB operations when customer-visible KMS keys are in use.

For customer-managed keys, the auditor may also review the KMS key policy and rotation configuration to confirm the key is under organizational control and meets rotation requirements.

Framework-specific interpretation

PCI DSS v4.0: Requirement 3.5 mandates strong cryptography for PAN at rest and documented key management procedures. KMS encryption on DynamoDB satisfies both, with key management handled through KMS key policies and automatic or manual rotation.

HIPAA Omnibus Rule 2013: The HIPAA Security Rule treats encryption as an addressable implementation specification under 45 CFR 164.312(a)(2)(iv). Using a KMS key you control gives you the CloudTrail audit trail and key management evidence auditors ask for when PHI is stored in DynamoDB.

GDPR: Article 32 requires appropriate technical measures to protect personal data, including encryption. KMS encryption on DynamoDB tables is a concrete safeguard you can document. If a breach occurs, encrypted data may reduce notification obligations under Article 34 when the data is unintelligible to unauthorized parties.

NIST SP 800-53 Rev 5: SC-12 and SC-28 are the relevant controls. SC-28 requires protecting data at rest; SC-12 requires documented key lifecycle management, rotation, and audit trails. KMS-managed keys satisfy both.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: dynamodb_table_encrypted_with_kms

  • AWS Config Managed Rule: DYNAMODB_TABLE_ENCRYPTED_KMS

  • Checkov Check: CKV_AWS_119

  • Powerpipe Control: aws_compliance.control.dynamodb_table_encrypted_with_kms

  • Prowler Check: dynamodb_tables_kms_cmk_encryption_enabled

  • KICS Query: ce089fd4-1406-47bd-8aad-c259772bb294

  • Trivy Check: AWS-0025

Last reviewed: 2026-03-09