home / skills / giuseppe-trisciuoglio / developer-kit / aws-cloudformation-dynamodb
/plugins/developer-kit-aws/skills/aws-cloudformation/aws-cloudformation-dynamodb
This skill helps you design production-ready DynamoDB infrastructure with CloudFormation, covering tables, keys, indexes, auto-scaling, encryption, TTL,
npx playbooks add skill giuseppe-trisciuoglio/developer-kit --skill aws-cloudformation-dynamodbReview the files below or copy the command above to add this skill to your agents.
---
name: aws-cloudformation-dynamodb
description: Provides AWS CloudFormation patterns for DynamoDB tables, GSIs, LSIs, auto-scaling, and streams. Use when creating DynamoDB tables with CloudFormation, configuring primary keys, local/global secondary indexes, capacity modes (on-demand/provisioned), point-in-time recovery, encryption, TTL, and implementing template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references.
category: aws
tags: [aws, cloudformation, dynamodb, nosql, database, serverless, tables, indexes, scaling, infrastructure, iaac]
version: 1.0.0
allowed-tools: Read, Write, Bash
---
# AWS CloudFormation DynamoDB
## Overview
Create production-ready NoSQL database infrastructure using AWS CloudFormation templates. This skill covers DynamoDB tables, primary keys, secondary indexes (GSI/LSI), capacity modes, auto-scaling, encryption, TTL, streams, and best practices for parameters, outputs, and cross-stack references.
## When to Use
Use this skill when:
- Creating new DynamoDB tables with CloudFormation
- Configuring primary keys (partition key, sort key)
- Creating Global Secondary Indexes (GSI) and Local Secondary Indexes (LSI)
- Setting up capacity modes (on-demand or provisioned)
- Implementing auto-scaling with Application Auto Scaling
- Enabling point-in-time recovery and backup
- Configuring encryption at rest and in transit
- Setting up TTL for automatic data expiration
- Enabling DynamoDB Streams for change data capture
- Organizing templates with Parameters, Outputs, Mappings, Conditions
- Implementing cross-stack references with export/import
- Using Transform for macros and reuse
## Instructions
Follow these steps to create DynamoDB tables with CloudFormation:
1. **Define Table Parameters**: Specify table name and billing mode
2. **Configure Primary Key**: Set partition key and optional sort key
3. **Add Secondary Indexes**: Create GSIs for alternative access patterns
4. **Configure Encryption**: Enable encryption using KMS keys
5. **Set Up TTL**: Define timestamp attribute for automatic deletion
6. **Enable Streams**: Configure stream for change data capture
7. **Add Auto Scaling**: Implement Application Auto Scaling for provisioned capacity
8. **Create Backup**: Enable point-in-time recovery
For complete examples, see the [EXAMPLES.md](references/examples.md) file.
## Examples
The following examples demonstrate common DynamoDB patterns:
### Example 1: Table with GSI
```yaml
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-table"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
- AttributeName: gsi-pk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: gsi
KeySchema:
- AttributeName: gsi-pk
KeyType: HASH
Projection:
ProjectionType: ALL
```
### Example 2: Table with Auto Scaling
```yaml
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 100
MinCapacity: 5
ResourceId: !Sub "table/${DynamoDBTable}"
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: dynamodb:table:ReadCapacityUnits
ServiceNamespace: dynamodb
```
### Example 3: Table with TTL
```yaml
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-session-table"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: sessionId
AttributeType: S
KeySchema:
- AttributeName: sessionId
KeyType: HASH
TimeToLiveSpecification:
AttributeName: expiresAt
Enabled: true
```
For complete production-ready examples, see [EXAMPLES.md](references/examples.md).
## CloudFormation Template Structure
### Base Template with Standard Format
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table with GSI and auto-scaling
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Table Configuration
Parameters:
- TableName
- BillingMode
- PrimaryKeyName
- PrimaryKeyType
- Label:
default: Capacity Settings
Parameters:
- ReadCapacityUnits
- WriteCapacityUnits
- MinReadCapacity
- MaxReadCapacity
- TargetUtilizationPercent
Parameters:
TableName:
Type: String
Default: my-dynamodb-table
Description: Name of the DynamoDB table
BillingMode:
Type: String
Default: PAY_PER_REQUEST
AllowedValues:
- PAY_PER_REQUEST
- PROVISIONED
Description: Billing mode for the table
PrimaryKeyName:
Type: String
Default: pk
Description: Name of the partition key attribute
PrimaryKeyType:
Type: String
Default: S
AllowedValues:
- S (String)
- N (Number)
- B (Binary)
Description: Type of the partition key attribute
ReadCapacityUnits:
Type: Number
Default: 5
Description: Read capacity units (required for PROVISIONED mode)
WriteCapacityUnits:
Type: Number
Default: 5
Description: Write capacity units (required for PROVISIONED mode)
MinReadCapacity:
Type: Number
Default: 5
Description: Minimum read capacity for auto-scaling
MaxReadCapacity:
Type: Number
Default: 100
Description: Maximum read capacity for auto-scaling
TargetUtilizationPercent:
Type: Number
Default: 70
Description: Target utilization percentage for auto-scaling
Mappings:
CapacityConfig:
dev:
ReadCapacity: 5
WriteCapacity: 5
MinRead: 5
MaxRead: 20
MinWrite: 5
MaxWrite: 20
staging:
ReadCapacity: 10
WriteCapacity: 10
MinRead: 10
MaxRead: 50
MinWrite: 10
MaxWrite: 50
production:
ReadCapacity: 25
WriteCapacity: 25
MinRead: 25
MaxRead: 200
MinWrite: 25
MaxWrite: 200
Conditions:
IsProvisioned: !Equals [!Ref BillingMode, PROVISIONED]
IsDev: !Equals [!Ref Environment, dev]
Resources:
# DynamoDB Table
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Ref TableName
BillingMode: !Ref BillingMode
AttributeDefinitions:
- AttributeName: !Ref PrimaryKeyName
AttributeType: !Ref PrimaryKeyType
- AttributeName: sk
AttributeType: S
- AttributeName: gsi_pk
AttributeType: S
- AttributeName: gsi_sk
AttributeType: S
KeySchema:
- AttributeName: !Ref PrimaryKeyName
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: GSI
KeySchema:
- AttributeName: gsi_pk
KeyType: HASH
- AttributeName: gsi_sk
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput: !If
- IsProvisioned
- ReadCapacityUnits: !FindInMap [CapacityConfig, !Ref Environment, ReadCapacity]
WriteCapacityUnits: !FindInMap [CapacityConfig, !Ref Environment, WriteCapacity]
- !Ref AWS::NoValue
ProvisionedThroughput: !If
- IsProvisioned
- ReadCapacityUnits: !FindInMap [CapacityConfig, !Ref Environment, ReadCapacity]
WriteCapacityUnits: !FindInMap [CapacityConfig, !Ref Environment, WriteCapacity]
- !Ref AWS::NoValue
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
SSESpecification:
SSEEnabled: true
SSEType: AES256
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Project
Value: !Ref ProjectName
Outputs:
TableName:
Description: Name of the DynamoDB table
Value: !Ref MyDynamoDBTable
Export:
Name: !Sub "${AWS::StackName}-TableName"
TableArn:
Description: ARN of the DynamoDB table
Value: !GetAtt MyDynamoDBTable.Arn
Export:
Name: !Sub "${AWS::StackName}-TableArn"
TableStreamArn:
Description: ARN of the DynamoDB table stream
Value: !GetAtt MyDynamoDBTable.StreamArn
Export:
Name: !Sub "${AWS::StackName}-TableStreamArn"
```
## Best Practices for Parameters
### AWS-Specific Parameter Types
```yaml
Parameters:
# AWS-specific types for validation
TableName:
Type: String
Description: Name of the DynamoDB table
TableArn:
Type: AWS::DynamoDB::Table::Arn
Description: ARN of existing DynamoDB table
TableStreamArn:
Type: AWS::DynamoDB::Table::StreamArn
Description: Stream ARN of DynamoDB table
KMSKeyArn:
Type: AWS::KMS::Key::Arn
Description: KMS key for server-side encryption
IAMRoleArn:
Type: AWS::IAM::Role::Arn
Description: IAM role for DynamoDB access
```
### Parameter Constraints
```yaml
Parameters:
TableName:
Type: String
Default: my-table
Description: DynamoDB table name
ConstraintDescription: Must be 3-255 characters, alphanumeric and underscores
MinLength: 3
MaxLength: 255
AllowedPattern: "[a-zA-Z0-9_]+"
ReadCapacityUnits:
Type: Number
Default: 5
Description: Read capacity units
MinValue: 1
MaxValue: 40000
ConstraintDescription: Must be between 1 and 40000
WriteCapacityUnits:
Type: Number
Default: 5
Description: Write capacity units
MinValue: 1
MaxValue: 40000
ConstraintDescription: Must be between 1 and 40000
BillingMode:
Type: String
Default: PAY_PER_REQUEST
Description: Billing mode
AllowedValues:
- PAY_PER_REQUEST
- PROVISIONED
```
## Outputs and Cross-Stack References
### Export/Import Patterns
```yaml
# Stack A - Database Stack
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table infrastructure stack
Resources:
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-table"
BillingMode: PROVISIONED
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
SSESpecification:
SSEEnabled: true
Outputs:
TableName:
Description: Name of the DynamoDB table
Value: !Ref MyDynamoDBTable
Export:
Name: !Sub "${AWS::StackName}-TableName"
TableArn:
Description: ARN of the DynamoDB table
Value: !GetAtt MyDynamoDBTable.Arn
Export:
Name: !Sub "${AWS::StackName}-TableArn"
TableStreamArn:
Description: Stream ARN for Lambda triggers
Value: !GetAtt MyDynamoDBTable.StreamArn
Export:
Name: !Sub "${AWS::StackName}-TableStreamArn"
```
```yaml
# Stack B - Application Stack (imports from Stack A)
AWSTemplateFormatVersion: 2010-09-09
Description: Application stack using DynamoDB table
Parameters:
DatabaseStackName:
Type: String
Default: database-stack
Description: Name of the database stack
Resources:
# Lambda function that uses DynamoDB
DataProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-processor"
Runtime: python3.11
Handler: handler.handler
Code:
S3Bucket: !Ref CodeBucket
S3Key: lambda/processor.zip
Environment:
Variables:
TABLE_NAME: !ImportValue
!Sub "${DatabaseStackName}-TableName"
Policies:
- DynamoDBReadPolicy:
TableName: !ImportValue
!Sub "${DatabaseStackName}-TableName"
- DynamoDBWritePolicy:
TableName: !ImportValue
!Sub "${DatabaseStackName}-TableName"
# Lambda trigger from DynamoDB Streams
StreamProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-stream-processor"
Runtime: python3.11
Handler: stream_handler.handler
Code:
S3Bucket: !Ref CodeBucket
S3Key: lambda/stream-processor.zip
EventSourceMapping:
EventSourceArn: !ImportValue
!Sub "${DatabaseStackName}-TableStreamArn"
FunctionName: !Ref StreamProcessorFunction
StartingPosition: LATEST
```
### Nested Stacks for Modularity
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Main stack with nested DynamoDB stacks
Resources:
# Nested stack for tables
TablesStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/dynamodb-tables.yaml
TimeoutInMinutes: 15
Parameters:
Environment: !Ref Environment
TableNamePrefix: !Ref TableNamePrefix
# Nested stack for auto-scaling
AutoScalingStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/bucket/dynamodb-autoscaling.yaml
TimeoutInMinutes: 15
Parameters:
Environment: !Ref Environment
TableName: !GetAtt TablesStack.Outputs.MainTableName
ReadCapacityUnits: !GetAtt TablesStack.Outputs.ReadCapacity
WriteCapacityUnits: !GetAtt TablesStack.Outputs.WriteCapacity
```
## DynamoDB Tables with Advanced Configurations
### Table with GSI and LSI
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table with multiple GSIs and LSIs
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- production
Resources:
# DynamoDB Table with indexes
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-orders"
BillingMode: PROVISIONED
AttributeDefinitions:
# Primary key attributes
- AttributeName: customer_id
AttributeType: S
- AttributeName: order_date
AttributeType: S
# GSI attributes
- AttributeName: status
AttributeType: S
- AttributeName: order_total
AttributeType: N
- AttributeName: created_at
AttributeType: S
# LSI attributes
- AttributeName: ship_date
AttributeType: S
KeySchema:
- AttributeName: customer_id
KeyType: HASH
- AttributeName: order_date
KeyType: RANGE
# Global Secondary Indexes
GlobalSecondaryIndexes:
# GSI for status-based queries
- IndexName: StatusIndex
KeySchema:
- AttributeName: status
KeyType: HASH
- AttributeName: order_date
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
# GSI for total-based queries
- IndexName: TotalIndex
KeySchema:
- AttributeName: status
KeyType: HASH
- AttributeName: order_total
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
# Local Secondary Indexes
LocalSecondaryIndexes:
- IndexName: ShipDateIndex
KeySchema:
- AttributeName: customer_id
KeyType: HASH
- AttributeName: ship_date
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 20
WriteCapacityUnits: 20
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
SSESpecification:
SSEEnabled: true
Tags:
- Key: Environment
Value: !Ref Environment
- Key: DataClassification
Value: confidential
```
### On-Demand Capacity Table
```yaml
Resources:
# On-demand capacity table
EventsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-events"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: event_id
AttributeType: S
- AttributeName: event_type
AttributeType: S
- AttributeName: timestamp
AttributeType: S
KeySchema:
- AttributeName: event_id
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: TypeTimestampIndex
KeySchema:
- AttributeName: event_type
KeyType: HASH
- AttributeName: timestamp
KeyType: RANGE
Projection:
ProjectionType: ALL
StreamSpecification:
StreamViewType: KEYS_ONLY
SSESpecification:
SSEEnabled: true
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
```
## Auto-Scaling Configuration
### Application Auto Scaling for Provisioned Tables
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table with auto-scaling
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- production
Resources:
# DynamoDB Table
MyTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-table"
BillingMode: PROVISIONED
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
- AttributeName: gsi_pk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
GlobalSecondaryIndexes:
- IndexName: GSI
KeySchema:
- AttributeName: gsi_pk
KeyType: HASH
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
# Scalable Target for Table Read Capacity
ReadScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: !FindInMap [CapacityConfig, !Ref Environment, MaxReadCapacity]
MinCapacity: !FindInMap [CapacityConfig, !Ref Environment, MinReadCapacity]
ResourceId: !Sub "table/${MyTable}"
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: dynamodb:table:ReadCapacityUnits
ServiceNamespace: dynamodb
# Scalable Target for Table Write Capacity
WriteScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: !FindInMap [CapacityConfig, !Ref Environment, MaxWriteCapacity]
MinCapacity: !FindInMap [CapacityConfig, !Ref Environment, MinWriteCapacity]
ResourceId: !Sub "table/${MyTable}"
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: dynamodb:table:WriteCapacityUnits
ServiceNamespace: dynamodb
# Scalable Target for GSI Read Capacity
GSIScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 100
MinCapacity: 5
ResourceId: !Sub "table/${MyTable}/index/GSI"
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: dynamodb:index:ReadCapacityUnits
ServiceNamespace: dynamodb
# Scaling Policy for Read Capacity
ReadScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: !Sub "${AWS::StackName}-read-scaling"
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ReadScalingTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: DynamoDBReadCapacityUtilization
TargetValue: 70
ScaleInCooldown: 60
ScaleOutCooldown: 60
# Scaling Policy for Write Capacity
WriteScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: !Sub "${AWS::StackName}-write-scaling"
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref WriteScalingTarget
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: DynamoDBWriteCapacityUtilization
TargetValue: 70
ScaleInCooldown: 60
ScaleOutCooldown: 60
# Auto Scaling IAM Role
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-dynamodb-autoscaling"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: application-autoscaling.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/DynamoDBAutoscaleRole
Mappings:
CapacityConfig:
dev:
MinReadCapacity: 5
MaxReadCapacity: 20
MinWriteCapacity: 5
MaxWriteCapacity: 20
staging:
MinReadCapacity: 10
MaxReadCapacity: 50
MinWriteCapacity: 10
MaxWriteCapacity: 50
production:
MinReadCapacity: 25
MaxReadCapacity: 200
MinWriteCapacity: 25
MaxWriteCapacity: 200
```
## DynamoDB Streams and Lambda Integration
### Table with Stream for Lambda Trigger
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table with stream for Lambda trigger
Resources:
# DynamoDB Table with Stream
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-orders"
BillingMode: PROVISIONED
AttributeDefinitions:
- AttributeName: order_id
AttributeType: S
- AttributeName: customer_id
AttributeType: S
- AttributeName: status
AttributeType: S
KeySchema:
- AttributeName: order_id
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: CustomerIndex
KeySchema:
- AttributeName: customer_id
KeyType: HASH
- AttributeName: status
KeyType: RANGE
Projection:
ProjectionType: ALL
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
# Lambda Function for processing stream
StreamProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-stream-processor"
Runtime: python3.11
Handler: handler.process_event
Code:
S3Bucket: !Ref CodeBucket
S3Key: lambda/stream-processor.zip
Timeout: 300
Role: !GetAtt LambdaExecutionRole.Arn
# Event Source Mapping
StreamEventSource:
Type: AWS::Lambda::EventSourceMapping
Properties:
EventSourceArn: !GetAtt OrdersTable.StreamArn
FunctionName: !Ref StreamProcessorFunction
StartingPosition: TRIM_HORIZON
BatchSize: 100
MaximumBatchingWindowInSeconds: 60
DestinationConfig:
OnFailure:
Destination: !GetAtt DeadLetterQueue.Arn
Enabled: true
# Dead Letter Queue for failed events
DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "${AWS::StackName}-stream-dlq"
MessageRetentionPeriod: 86400
# Lambda Execution Role
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-lambda-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-dynamodb-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:GetRecords
- dynamodb:GetShardIterator
- dynamodb:DescribeStream
- dynamodb:ListStreams
Resource: !GetAtt OrdersTable.StreamArn
- Effect: Allow
Action:
- sqs:SendMessage
Resource: !GetAtt DeadLetterQueue.Arn
```
## TTL Configuration
### Table with Time-to-Live
```yaml
Resources:
# Table with TTL for automatic data expiration
SessionsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-sessions"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: session_id
AttributeType: S
- AttributeName: user_id
AttributeType: S
KeySchema:
- AttributeName: session_id
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: UserIndex
KeySchema:
- AttributeName: user_id
KeyType: HASH
Projection:
ProjectionType: ALL
StreamSpecification:
StreamViewType: NEW_IMAGE
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true
SSESpecification:
SSEEnabled: true
# TTL for 24-hour expiration (example)
SessionCleanupFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-session-cleanup"
Runtime: python3.11
Handler: handler.cleanup
Code:
S3Bucket: !Ref CodeBucket
S3Key: lambda/session-cleanup.zip
Role: !GetAtt LambdaExecutionRole.Arn
# Scheduled rule for TTL cleanup
SessionCleanupRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub "${AWS::StackName}-session-cleanup"
ScheduleExpression: "rate(1 hour)"
State: ENABLED
Targets:
- Id: !Ref SessionCleanupFunction
Arn: !GetAtt SessionCleanupFunction.Arn
```
## Encryption and Security
### Table with Customer Managed KMS Key
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB table with customer-managed encryption
Resources:
# KMS Key for encryption
DynamoDBKMSKey:
Type: AWS::KMS::Key
Properties:
KeyName: !Sub "${AWS::StackName}-dynamodb-key"
Description: KMS key for DynamoDB encryption
KeyPolicy:
Version: "2012-10-17"
Statement:
- Sid: Enable IAM policies
Effect: Allow
Principal:
AWS: !GetAtt IAMRole.Arn
Action:
- kms:*
Resource: "*"
- Sid: Allow DynamoDB to use the key
Effect: Allow
Principal:
Service: dynamodb.amazonaws.com
Action:
- kms:Encrypt
- kms:Decrypt
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
Resource: "*"
# DynamoDB Table with customer-managed encryption
SensitiveDataTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-sensitive-data"
BillingMode: PROVISIONED
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: category
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
SSESpecification:
SSEEnabled: true
SSEType: KMS
KMSMasterKeyId: !Ref DynamoDBKMSKey
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
# IAM Role for accessing encrypted table
IAMRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-data-access-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-data-access"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:Query
- dynamodb:Scan
Resource: !GetAtt SensitiveDataTable.Arn
- Effect: Allow
Action:
- kms:Decrypt
- kms:GenerateDataKey
Resource: !GetAtt DynamoDBKMSKey.Arn
```
## Conditions and Transform
### Conditions for Environment-Specific Resources
```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: DynamoDB with conditional resources
Parameters:
Environment:
Type: String
Default: dev
AllowedValues:
- dev
- staging
- production
EnableEncryption:
Type: String
Default: true
AllowedValues:
- true
- false
Conditions:
IsProduction: !Equals [!Ref Environment, production]
IsDev: !Equals [!Ref Environment, dev]
EnableEncryption: !Equals [!Ref EnableEncryption, true]
EnablePITR: !Not [!Equals [!Ref Environment, dev]]
EnableStream: !Or [!Equals [!Ref Environment, staging], !Equals [!Ref Environment, production]]
Resources:
# Main table
MyTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-table"
BillingMode: !If [IsProduction, PROVISIONED, PAY_PER_REQUEST]
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
- AttributeName: sk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
- AttributeName: sk
KeyType: RANGE
ProvisionedThroughput: !If
- IsProduction
- ReadCapacityUnits: 25
WriteCapacityUnits: 25
- !Ref AWS::NoValue
StreamSpecification: !If
- EnableStream
- StreamViewType: NEW_AND_OLD_IMAGES
- !Ref AWS::NoValue
SSESpecification: !If
- EnableEncryption
- SSEEnabled: true
SSEType: AES256
- !Ref AWS::NoValue
PointInTimeRecoverySpecification: !If
- EnablePITR
- PointInTimeRecoveryEnabled: true
- !Ref AWS::NoValue
```
### Transform for SAM Template
```yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: Using SAM Transform for DynamoDB
Globals:
Function:
Timeout: 30
Runtime: python3.11
Environment:
Variables:
TABLE_NAME: !Ref DataTable
Resources:
# SAM Simple Table (creates table with on-demand capacity)
DataTable:
Type: AWS::Serverless::SimpleTable
Properties:
TableName: !Sub "${AWS::StackName}-data"
PrimaryKey:
Name: id
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
SSESpecification:
SSEEnabled: true
# Lambda function with DynamoDB access
DataHandler:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-handler"
Handler: handler.handler
CodeUri: lambda/
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DataTable
Events:
Api:
Type: Api
Properties:
Path: /data
Method: post
```
## Best Practices
### Security
- Enable server-side encryption (SSE) for all tables
- Use customer-managed KMS keys for sensitive data
- Enable point-in-time recovery for production tables
- Use IAM policies with minimum necessary permissions
- Enable VPC endpoints for private table access
- Configure AWS CloudTrail for auditing
### Performance
- Choose partition keys with uniform distribution
- Use GSIs for alternative access patterns
- Monitor consumed capacity and throttled requests
- Use auto-scaling for variable workloads
- Consider on-demand capacity for unpredictable traffic
- Implement proper data modeling for query patterns
### Monitoring
- Enable CloudWatch metrics with 1-minute granularity
- Create alarms for throttled requests
- Monitor table and index capacity utilization
- Use AWS DynamoDB Accelerator (DAX) for read-heavy workloads
- Implement proper error handling and retries
### Cost Optimization
- Use on-demand capacity when appropriate
- Right-size provisioned capacity based on metrics
- Use auto-scaling to handle peak loads
- Consider TTL for automatic data cleanup
- Archive old data to S3 with Data Pipeline or Glue
## CloudFormation Stack Management Best Practices
### Stack Policies
```yaml
Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-table"
# Stack policy to protect table from deletion
StackPolicy:
Type: AWS::CloudFormation::StackPolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: "*"
Action: "Update:*"
Resource: "*"
- Effect: Deny
Principal: "*"
Action:
- Update:Delete
Resource:
- LogicalId: DynamoDBTable
```
### Drift Detection
```bash
# Detect drift on a stack
aws cloudformation detect-drift --stack-name my-dynamodb-stack
# Get resource drift status
aws cloudformation describe-stack-resource-drifts \
--stack-name my-dynamodb-stack
```
## Related Resources
- [DynamoDB Documentation](https://docs.aws.amazon.com/dynamodb/)
- [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/)
- [DynamoDB Best Practices](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
- [Application Auto Scaling](https://docs.aws.amazon.com/autoscaling/application/userguide/what-is-application-auto-scaling.html)
## Constraints and Warnings
### Resource Limits
- **Table Size Limits**: Maximum 40 GB per item; no limit on number of items per table
- **Partition Key Limits**: Composite keys have specific size limits (attributes combined must be under KB limit)
- **GSI Limits**: Maximum 20 Global Secondary Indexes per table
- **LSI Limits**: Maximum 5 Local Secondary Indexes per table (same partition key as base table)
### Throughput Constraints
- **Read/Write Capacity**: Exceeding provisioned capacity results in throttling
- **Auto Scaling Limits**: Auto scaling takes time to adjust; instant spikes cause throttling
- **On-Demand Mode**: On-demand tables have unlimited capacity but may throttle if table exceeds account limits
- **Partition Key Design**: Poor partition key distribution causes hot partitions and throttling
### Operational Constraints
- **Table Creation**: Creating tables with GSIs takes longer than single-table creation
- **Index Updates**: Adding GSIs to existing tables requires rebuilding the entire table
- **Stream Limits**: DynamoDB Streams have a 24-hour retention limit; extended retention requires Lambda or Firehose
- **TTL Processing**: TTL deletes are not instantaneous; may take up to 48 hours
### Security Constraints
- **Encryption**: Once enabled, encryption at rest cannot be disabled
- **Point-in-Time Recovery**: PITR cannot be disabled once enabled on a table
- **Global Tables**: Cross-region replication requires specific IAM permissions
- **Conditional Writes**: Condition expressions that fail due to resource constraints still consume write capacity
### Cost Considerations
- **Provisioned Capacity**: Pay for provisioned capacity even if unused
- **On-Demand Pricing**: On-demand mode can be significantly more expensive for predictable workloads
- **GSI Costs**: Each GSI incurs separate read/write capacity costs
- **Storage Costs**: Encrypting data adds storage overhead; consider this when estimating costs
- **Data Transfer**: Inter-region replication for global tables incurs data transfer costs
## Additional Files
For complete details on resources and their properties, see:
- [REFERENCE.md](references/reference.md) - Detailed reference guide for all DynamoDB CloudFormation resources
- [EXAMPLES.md](references/examples.md) - Complete production-ready examples
This skill provides production-ready AWS CloudFormation patterns for creating and managing DynamoDB tables. It covers primary keys, GSIs/LSIs, capacity modes, auto-scaling, streams, encryption, TTL, and template structuring for reusable stacks. Use it to standardize DynamoDB infrastructure across environments and enable safe cross-stack integrations.
The skill delivers CloudFormation templates and patterns that define DynamoDB resources, index definitions, stream configurations, TTL, server-side encryption, and point-in-time recovery. Templates include Parameters, Mappings, Conditions, and Outputs to support environment-specific settings, provisioned vs on-demand billing, and Application Auto Scaling for capacity. It also shows export/import patterns and nested-stack examples for modular deployments.
Can I mix PAY_PER_REQUEST and provisioned indexes in one template?
Yes. The table billing mode and index throughput are managed separately; use Conditions to include ProvisionedThroughput only when PROVISIONED is selected.
How do I safely update GSIs in production?
Add GSIs incrementally, monitor replication and consumed capacity, and avoid deleting or renaming GSIs during peak traffic. Test index builds in staging before production.