GitHunt
IS

islamelkadi/terraform-aws-dynamodb

Production-ready Terraform module for AWS DynamoDB with multi-framework compliance support, built on real-world enterprise experience as a Forward Deployed Engineer.

Terraform AWS DynamoDB Module

Terraform Security
Terraform Lint & Validation
Terraform Docs

A reusable Terraform module for creating AWS DynamoDB tables with AWS Security Hub compliance (FSBP, CIS, NIST 800-53, NIST 800-171, PCI DSS), flexible security control overrides, and comprehensive configuration options.

Table of Contents

Prerequisites

This module is designed for macOS. The following must already be installed on your machine:

To install the remaining development tools, run:

make bootstrap

This will install/upgrade: tfenv, Terraform (via tfenv), tflint, terraform-docs, checkov, and pre-commit.

Security

Security Controls

This module implements AWS Security Hub compliance with an extensible override system. Security controls are enforced by default when security_controls is provided from the metadata module.

Available Security Control Overrides

Override Flag Description Common Justification
disable_kms_requirement Allows AWS-managed encryption "Development table, no sensitive data"
disable_pitr_requirement Disables point-in-time recovery "Ephemeral data, disposable table"
disable_deletion_protection Allows table deletion "Development table, easy teardown needed"

Security Best Practices

Production Tables:

  • Use KMS customer-managed keys for encryption
  • Enable point-in-time recovery (PITR) for data protection
  • Enable deletion protection to prevent accidental deletion
  • Use on-demand billing or provisioned with auto-scaling
  • Enable DynamoDB Streams for change data capture
  • Set up CloudWatch alarms for throttling and errors

Development Tables:

  • KMS encryption still recommended (minimal cost)
  • PITR optional for cost savings
  • Deletion protection can be disabled with justification

Environment-Based Security Controls

Security controls are automatically applied based on the environment through the terraform-aws-metadata module's security profiles:

Control Dev Staging Prod
KMS customer-managed keys Optional Required Required
Point-in-time recovery Optional Required Required
Deletion protection Disabled Enabled Enabled
DynamoDB Streams Optional Recommended Recommended

For full details on security profiles and how controls vary by environment, see the Security Profiles documentation.

Security Scan Suppressions

This module suppresses certain Checkov security checks that are either not applicable to example/demo code or represent optional features. The following checks are suppressed in .checkov.yaml:

Module Source Versioning (CKV_TF_1, CKV_TF_2)

  • Suppressed because we use semantic version tags (?ref=v1.0.0) instead of commit hashes for better maintainability and readability
  • Semantic versioning is a valid and widely-accepted versioning strategy for stable releases

KMS IAM Policies (CKV_AWS_111, CKV_AWS_356, CKV_AWS_109)

  • Suppressed in example code where KMS modules use flexible IAM policies for demonstration purposes
  • Production deployments should customize KMS policies based on specific security requirements and apply least privilege principles

Features

  • DynamoDB table with on-demand billing mode
  • KMS encryption with customer-managed keys
  • Point-in-time recovery (PITR) for data protection
  • Deletion protection option
  • DynamoDB Streams support
  • TTL (Time To Live) configuration
  • Global and local secondary indexes
  • Security controls integration with extensible override system
  • Terraform validation checks for compliance

Usage Examples

Basic Example

module "dynamodb_table" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  namespace   = "example"
  environment = "prod"
  name        = "events"
  region      = "us-east-1"
  
  # Table schema
  hash_key = "event_id"
  range_key = "timestamp"
  
  attributes = [
    {
      name = "event_id"
      type = "S"
    },
    {
      name = "timestamp"
      type = "N"
    }
  ]
  
  # Encryption
  kms_key_arn = module.kms.key_arn
  
  tags = {
    Project = "CorporateActions"
  }
}

Production Table with Security Controls

module "dynamodb_table" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  security_controls = module.metadata.security_controls
  
  namespace   = "example"
  environment = "prod"
  name        = "corporate-actions-events"
  region      = "us-east-1"
  
  # Table schema
  hash_key  = "event_id"
  range_key = "timestamp"
  
  attributes = [
    {
      name = "event_id"
      type = "S"
    },
    {
      name = "timestamp"
      type = "N"
    },
    {
      name = "cusip"
      type = "S"
    }
  ]
  
  # Global secondary index
  global_secondary_indexes = [
    {
      name            = "cusip-timestamp-index"
      hash_key        = "cusip"
      range_key       = "timestamp"
      projection_type = "ALL"
    }
  ]
  
  # KMS encryption (required by security controls)
  kms_key_arn = module.kms.key_arn
  
  # Point-in-time recovery (required by security controls)
  enable_point_in_time_recovery = true
  
  # Deletion protection (required by security controls)
  deletion_protection_enabled = true
  
  # DynamoDB Streams for change data capture
  stream_enabled   = true
  stream_view_type = "NEW_AND_OLD_IMAGES"
  
  # TTL for automatic data expiration
  ttl_enabled        = true
  ttl_attribute_name = "expires_at"
  
  tags = {
    Project     = "CorporateActions"
    Compliance  = "PCI-DSS"
    DataClass   = "Confidential"
  }
}

Development Table with Overrides

module "dynamodb_table" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  security_controls = module.metadata.security_controls
  
  # Override security controls for development
  security_control_overrides = {
    disable_pitr_requirement    = true
    disable_deletion_protection = true
    justification = "Development table for testing. Data is disposable and recreated from seed scripts. PITR adds unnecessary cost. Deletion protection disabled for easy environment teardown."
  }
  
  namespace   = "example"
  environment = "dev"
  name        = "test-events"
  region      = "us-east-1"
  
  hash_key = "id"
  
  attributes = [
    {
      name = "id"
      type = "S"
    }
  ]
  
  # Still use KMS encryption
  kms_key_arn = module.kms.key_arn
  
  # Overrides allow these to be disabled
  enable_point_in_time_recovery = false
  deletion_protection_enabled    = false
  
  tags = {
    Project     = "CorporateActions"
    Environment = "Development"
  }
}

Table with Streams for Event Processing

module "events_table" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  security_controls = module.metadata.security_controls
  
  namespace   = "example"
  environment = "prod"
  name        = "events"
  region      = "us-east-1"
  
  hash_key  = "event_id"
  range_key = "timestamp"
  
  attributes = [
    {
      name = "event_id"
      type = "S"
    },
    {
      name = "timestamp"
      type = "N"
    }
  ]
  
  kms_key_arn                    = module.kms.key_arn
  enable_point_in_time_recovery  = true
  deletion_protection_enabled    = true
  
  # Enable streams for Lambda processing
  stream_enabled   = true
  stream_view_type = "NEW_AND_OLD_IMAGES"
  
  tags = {
    Project = "CorporateActions"
    Purpose = "EventSourcing"
  }
}

# Lambda to process DynamoDB stream
module "stream_processor" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  namespace   = "example"
  environment = "prod"
  name        = "events-stream-processor"
  region      = "us-east-1"
  
  runtime = "python3.13"
  handler = "index.handler"
  filename = "processor.zip"
  
  # Grant Lambda permission to read from stream
  managed_policy_arns = [
    "arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole"
  ]
}

# Event source mapping
resource "aws_lambda_event_source_mapping" "dynamodb_stream" {
  event_source_arn  = module.events_table.stream_arn
  function_name     = module.stream_processor.function_name
  starting_position = "LATEST"
  
  batch_size = 100
  maximum_batching_window_in_seconds = 5
}

MCP Servers

This module includes two Model Context Protocol (MCP) servers configured in .kiro/settings/mcp.json for use with Kiro:

Server Package Description
aws-docs awslabs.aws-documentation-mcp-server@latest Provides access to AWS documentation for contextual lookups of service features, API references, and best practices.
terraform awslabs.terraform-mcp-server@latest Enables Terraform operations (init, validate, plan, fmt, tflint) directly from the IDE with auto-approved commands for common workflows.

Both servers run via uvx and require no additional installation beyond the bootstrap step.

Usage

# DynamoDB Module Example
# Demonstrates table creation with GSI, TTL, and streams

module "dynamodb_table" {
  source = "github.com/islamelkadi/terraform-aws-dynamodb"
  namespace   = var.namespace
  environment = var.environment
  name        = var.name
  region      = var.region

  # Composite key
  hash_key  = var.hash_key
  range_key = var.range_key

  # Attributes
  attributes = var.attributes

  # Global Secondary Indexes
  global_secondary_indexes = var.global_secondary_indexes

  # TTL configuration
  ttl_enabled        = var.ttl_enabled
  ttl_attribute_name = var.ttl_attribute_name

  # Streams configuration
  stream_enabled   = var.stream_enabled
  stream_view_type = var.stream_view_type

  # Recovery and protection
  enable_point_in_time_recovery = var.enable_point_in_time_recovery
  deletion_protection_enabled   = var.deletion_protection_enabled

  # Encryption
  kms_key_arn = var.kms_key_arn

  tags = var.tags
}

Requirements

Name Version
terraform >= 1.14.3
aws >= 6.34

Providers

Name Version
aws >= 6.34

Modules

Name Source Version
metadata github.com/islamelkadi/terraform-aws-metadata v1.1.0

Resources

Name Type
aws_dynamodb_table.this resource

Inputs

Name Description Type Default Required
attributes List of attribute definitions. Each attribute must have 'name' and 'type' (S, N, or B)
list(object({
name = string
type = string
}))
n/a yes
deletion_protection_enabled Enable deletion protection for the table bool false no
delimiter Delimiter to use between name components string "-" no
enable_point_in_time_recovery Enable point-in-time recovery for the table bool true no
environment Environment name (dev, staging, prod) string n/a yes
global_secondary_indexes List of global secondary index configurations
list(object({
name = string
hash_key = string
range_key = optional(string)
projection_type = optional(string)
non_key_attributes = optional(list(string))
}))
[] no
hash_key Attribute to use as the hash (partition) key string n/a yes
kms_key_arn ARN of KMS key for table encryption string n/a yes
local_secondary_indexes List of local secondary index configurations
list(object({
name = string
range_key = string
projection_type = optional(string)
non_key_attributes = optional(list(string))
}))
[] no
name Name of the DynamoDB table string n/a yes
namespace Namespace (organization/team name) string n/a yes
naming_attributes Additional attributes for naming list(string) [] no
range_key Attribute to use as the range (sort) key. Optional string null no
region AWS region where resources will be created string n/a yes
security_control_overrides Override specific security controls with documented justification
object({
disable_kms_requirement = optional(bool, false)
disable_pitr_requirement = optional(bool, false)
disable_deletion_protection = optional(bool, false)
justification = optional(string, "")
})
{
"disable_deletion_protection": false,
"disable_kms_requirement": false,
"disable_pitr_requirement": false,
"justification": ""
}
no
security_controls Security controls configuration from metadata module
object({
encryption = object({
require_kms_customer_managed = bool
require_encryption_at_rest = bool
require_encryption_in_transit = bool
enable_kms_key_rotation = bool
})
logging = object({
require_cloudwatch_logs = bool
min_log_retention_days = number
require_access_logging = bool
require_flow_logs = bool
})
monitoring = object({
enable_xray_tracing = bool
enable_enhanced_monitoring = bool
enable_performance_insights = bool
require_cloudtrail = bool
})
network = object({
require_private_subnets = bool
require_vpc_endpoints = bool
block_public_ingress = bool
require_imdsv2 = bool
})
compliance = object({
enable_point_in_time_recovery = bool
require_reserved_concurrency = bool
enable_deletion_protection = bool
})
data_protection = object({
require_versioning = bool
require_mfa_delete = bool
require_automated_backups = bool
block_public_access = bool
require_lifecycle_policies = bool
})
})
null no
stream_enabled Enable DynamoDB Streams bool false no
stream_view_type Stream view type (KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, NEW_AND_OLD_IMAGES) string "NEW_AND_OLD_IMAGES" no
tags Additional tags to apply to resources map(string) {} no
ttl_attribute_name Name of the table attribute to use for TTL. Set to null to disable TTL string "ttl" no
ttl_enabled Enable TTL for the table bool true no

Outputs

Name Description
hash_key Hash key attribute name
range_key Range key attribute name
stream_arn DynamoDB table stream ARN
stream_label DynamoDB table stream label
table_arn DynamoDB table ARN
table_id DynamoDB table ID (same as table name)
table_name DynamoDB table name
tags Tags applied to the DynamoDB table

Examples

See example/ for a complete working example with GSI, TTL, and streams.