home / skills / giuseppe-trisciuoglio / developer-kit / 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-dynamodb

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
35.9 KB
---
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

Overview

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.

How this skill works

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.

When to use it

  • Provision new DynamoDB tables with consistent infrastructure-as-code
  • Add or modify GSIs and LSIs to support new access patterns
  • Switch between PAY_PER_REQUEST and PROVISIONED billing modes
  • Enable streams, TTL, encryption, or point-in-time recovery for production safety
  • Implement auto-scaling for provisioned capacity and predictable cost control
  • Share table names/ARNs across stacks using exports and imports

Best practices

  • Model Parameters for table names, billing mode, and capacity so templates are environment-agnostic
  • Use AWS-specific parameter types (e.g., AWS::DynamoDB::Table::Arn) for validation and safer imports
  • Prefer PAY_PER_REQUEST for unpredictable workloads; use PROVISIONED + auto-scaling for cost control at scale
  • Define Index AttributeDefinitions and KeySchema explicitly; avoid adding GSIs unexpectedly in production
  • Enable PointInTimeRecovery and SSE (KMS) for production tables
  • Export TableName/Arn/StreamArn for cross-stack consumers; keep nested stacks for modularity

Example use cases

  • Create a session table with TTL for automatic cleanup of expired tokens
  • Deploy a transactional order table with GSIs for alternative queries and LSI for sorted local queries
  • Provision a high-throughput table with PROVISIONED mode and Application Auto Scaling targets
  • Enable DynamoDB Streams to trigger Lambda processors via exported StreamArn
  • Build a reusable nested stack that creates tables and a separate stack for auto-scaling policies

FAQ

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.