AWSCDK::DynamoDB

61 types

Amazon DynamoDB Construct Library

The DynamoDB construct library has two table constructs - Table and TableV2. TableV2 is the preferred construct for all use cases, including creating a single table or a table with multiple replicas.

Table API documentation

Here is a minimal deployable DynamoDB table using TableV2:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

By default, TableV2 will create a single table in the main deployment region referred to as the primary table. The properties of the primary table are configurable via TableV2 properties. For example, consider the following DynamoDB table created using the TableV2 construct defined in a Stack being deployed to us-west-2:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    contributor_insights_specification: {
        enabled: true,
    },
    table_class: AWSCDK::DynamoDB::TableClass::STANDARD_INFREQUENT_ACCESS,
    point_in_time_recovery_specification: {
        point_in_time_recovery_enabled: true,
    },
})

The above TableV2 definition will result in the provisioning of a single table in us-west-2 with properties that match the properties set on the TableV2 instance.

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GlobalTables.html

Replicas

The TableV2 construct can be configured with replica tables. This will enable you to work with your table as a global table. To do this, the TableV2 construct must be defined in a Stack with a defined region. The main deployment region must not be given as a replica because this is created by default with the TableV2 construct. The following is a minimal example of defining TableV2 with replicas. This TableV2 definition will provision three copies of the table - one in us-west-2 (primary deployment region), one in us-east-1, and one in us-east-2.

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

Alternatively, you can add new replicas to an instance of the TableV2 construct using the add_replica method:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    replicas: [{region: "us-east-1"}],
})

global_table.add_replica({region: "us-east-2", deletion_protection: true})

The following properties are configurable on a per-replica basis, but will be inherited from the TableV2 properties if not specified:

The following example shows how to define properties on a per-replica basis:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    contributor_insights_specification: {
        enabled: true,
    },
    point_in_time_recovery_specification: {
        point_in_time_recovery_enabled: true,
    },
    replicas: [
        {
            region: "us-east-1",
            table_class: AWSCDK::DynamoDB::TableClass::STANDARD_INFREQUENT_ACCESS,
            point_in_time_recovery_specification: {
                point_in_time_recovery_enabled: false,
            },
        },
        {
            region: "us-east-2",
            contributor_insights_specification: {
                enabled: false,
            },
        },
    ],
})

To obtain an ITableV2 reference to a specific replica table, call the replica method on an instance of the TableV2 construct and pass the replica region as an argument:

require 'aws-cdk-lib'

user = nil # AWSCDK::IAM::User


class FooStack < AWSCDK::Stack
  attr_reader :global_table

  def initialize(scope, id, props)
    super(scope, id, props)

    @global_table = AWSCDK::DynamoDB::TableV2.new(self, "GlobalTable", {
        partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
        replicas: [
            {region: "us-east-1"},
            {region: "us-east-2"},
        ],
    })
  end
end

class BarStack < AWSCDK::Stack
  def initialize(scope, id, props)
    super(scope, id, props)

    # user is given grantWriteData permissions to replica in us-east-1
    props[:replica_table].grant_write_data(user)
  end
end

app = AWSCDK::App.new

foo_stack = FooStack.new(app, "FooStack", {env: {region: "us-west-2"}})
bar_stack = BarStack.new(app, "BarStack", {
    replica_table: foo_stack.global_table.replica("us-east-1"),
    env: {region: "us-east-1"},
})

Note: You can create an instance of the TableV2 construct with as many replicas as needed as long as there is only one replica per region. After table creation you can add or remove replicas, but you can only add or remove a single replica in each update.

Multi-Account Global Tables

Multi-account global tables extend DynamoDB replication across AWS accounts, providing enhanced security, governance, and fault isolation. Each replica resides in a separate AWS account, enabling account-level isolation and alignment with organizational structures.

Creating Multi-Account Replicas

For tables defined in the same CDK application, use the TableV2MultiAccountReplica construct:

require 'aws-cdk-lib'


app = AWSCDK::App.new

# Source table in Account A
source_stack = AWSCDK::Stack.new(app, "SourceStack", {
    env: {region: "us-east-2", account: "111111111111"},
})

source_table = AWSCDK::DynamoDB::TableV2.new(source_stack, "SourceTable", {
    table_name: "MyMultiAccountTable",
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    global_table_settings_replication_mode: AWSCDK::DynamoDB::GlobalTableSettingsReplicationMode::ALL,
})

# Replica stack in Account B
replica_stack = AWSCDK::Stack.new(app, "ReplicaStack", {
    env: {region: "us-east-1", account: "222222222222"},
})

# Create replica - permissions are automatically configured
replica = AWSCDK::DynamoDB::TableV2MultiAccountReplica.new(replica_stack, "ReplicaTable", {
    table_name: "MyMultiAccountTable",
    replica_source_table: source_table,
    global_table_settings_replication_mode: AWSCDK::DynamoDB::GlobalTableSettingsReplicationMode::ALL,
})

The TableV2MultiAccountReplica construct:

Note: Permissions are automatically configured when both tables are in the same CDK app. For imported source tables, see "Working with Imported Tables" below.

Adding Replicas to Existing Tables

If the source table already exists in AWS, you cannot use automatic cross-stack references. The replica will issue a warning, and you must manually configure permissions:

require 'aws-cdk-lib'


app = AWSCDK::App.new

# Source table in Account A
source_stack = AWSCDK::Stack.new(app, "SourceStack", {
    env: {region: "us-east-1", account: "111111111111"},
})

# Region us-west-2
source_table = AWSCDK::DynamoDB::TableV2.new(source_stack, "SourceTable", {
    table_name: "MyMultiAccountTable",
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    global_table_settings_replication_mode: AWSCDK::DynamoDB::GlobalTableSettingsReplicationMode::ALL,
})
# After replica is deployed, update source stack with the ARN
source_table.grants.("arn:aws:dynamodb:us-east-1:222222222222:table/MyMultiAccountTable")

Working with Imported Tables

When importing a source table, the replica will issue a warning since it cannot automatically configure permissions on the imported table:

require 'aws-cdk-lib'


app = AWSCDK::App.new

replica_stack = AWSCDK::Stack.new(app, "ReplicaStack", {
    env: {region: "us-east-1", account: "222222222222"},
})

# Import source table
imported_source = AWSCDK::DynamoDB::TableV2.from_table_arn(replica_stack, "ImportedSource", "arn:aws:dynamodb:us-east-2:111111111111:table/MyMultiAccountTable")

# Create replica - will issue a warning about missing source permissions
replica = AWSCDK::DynamoDB::TableV2MultiAccountReplica.new(replica_stack, "ReplicaTable", {
    table_name: "MyMultiAccountTable",
    replica_source_table: imported_source,
    global_table_settings_replication_mode: AWSCDK::DynamoDB::GlobalTableSettingsReplicationMode::ALL,
})

Warning: The replica will emit a warning indicating that you must manually configure permissions on the actual source table.

Then configure permissions on the actual source table using the replica ARN as shown in "Adding Replicas to Existing Tables" above.

Key Considerations

Multi-Region Strong Consistency (MRSC)

By default, DynamoDB global tables provide eventual consistency across regions. For applications requiring strong consistency across regions, you can configure Multi-Region Strong Consistency (MRSC) using the multi_region_consistency property.

MRSC global tables can be configured in two ways:

Region Sets

MRSC global tables must be deployed within the same region set. The supported region sets are:

Three Replicas Configuration

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

mrsc_table = AWSCDK::DynamoDB::TableV2.new(stack, "MRSCTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    multi_region_consistency: AWSCDK::DynamoDB::MultiRegionConsistency::STRONG,
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

Two Replicas + Witness Configuration

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

mrsc_table = AWSCDK::DynamoDB::TableV2.new(stack, "MRSCTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    multi_region_consistency: AWSCDK::DynamoDB::MultiRegionConsistency::STRONG,
    replicas: [
        {region: "us-east-1"},
    ],
    witness_region: "us-east-2",
})

Important Considerations

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html#V2globaltables_HowItWorks.consistency-modes-mrsc

Billing

The TableV2 construct can be configured with on-demand or provisioned billing:

Note: write_capacity can only be configured using autoscaled capacity.

The following example shows how to configure TableV2 with on-demand billing:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.on_demand,
})

The following example shows how to configure TableV2 with on-demand billing with optional maximum throughput configured:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.on_demand({
        max_read_request_units: 100,
        max_write_request_units: 115,
    }),
})

When using provisioned billing, you must also specify read_capacity and write_capacity. You can choose to configure read_capacity with fixed capacity or autoscaled capacity, but write_capacity can only be configured with autoscaled capacity. The following example shows how to configure TableV2 with provisioned billing:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.provisioned({
        read_capacity: AWSCDK::DynamoDB::Capacity.fixed(10),
        write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 15}),
    }),
})

When using provisioned billing, you can configure the read_capacity on a per-replica basis:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.provisioned({
        read_capacity: AWSCDK::DynamoDB::Capacity.fixed(10),
        write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 15}),
    }),
    replicas: [
        {
            region: "us-east-1",
        },
        {
            region: "us-east-2",
            read_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 20, target_utilization_percent: 50}),
        },
    ],
})

When changing the billing for a table from provisioned to on-demand or from on-demand to provisioned, seed_capacity must be configured for each autoscaled resource:

global_table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.provisioned({
        read_capacity: AWSCDK::DynamoDB::Capacity.fixed(10),
        write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 10, seed_capacity: 20}),
    }),
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html

Warm Throughput

Warm throughput refers to the number of read and write operations your DynamoDB table can instantaneously support.

This optional configuration allows you to pre-warm your table or index to handle anticipated throughput, ensuring optimal performance under expected load.

The Warm Throughput configuration settings are automatically replicated across all Global Table replicas.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "id", type: AWSCDK::DynamoDB::AttributeType::STRING},
    warm_throughput: {
        read_units_per_second: 15000,
        write_units_per_second: 20000,
    },
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/warm-throughput.html

Encryption

All user data stored in a DynamoDB table is fully encrypted at rest. When creating an instance of the TableV2 construct, you can select the following table encryption options:

The following is an example of how to configure TableV2 with encryption using an AWS owned key:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    encryption: AWSCDK::DynamoDB::TableEncryptionV2.dynamo_owned_key,
})

The following is an example of how to configure TableV2 with encryption using an AWS managed key:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    encryption: AWSCDK::DynamoDB::TableEncryptionV2.aws_managed_key,
})

When configuring TableV2 with encryption using customer managed keys, you must specify the KMS key for the primary table as the table_key. A map of replica_key_arns must be provided containing each replica region and the associated KMS key ARN:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

table_key = AWSCDK::KMS::Key.new(stack, "Key")
replica_key_arns = {
    "us-east-1" => "arn:aws:kms:us-east-1:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6",
    "us-east-2" => "arn:aws:kms:us-east-2:123456789012:key/h90bkasj-bs1j-92wp-s2ka-bh857d60bkj8",
}

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    encryption: AWSCDK::DynamoDB::TableEncryptionV2.customer_managed_key(table_key, replica_key_arns),
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

Note: When encryption is configured with customer managed keys, you must have a key already created in each replica region.

Further reading: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-mgmt

Secondary Indexes

Secondary indexes allow efficient access to data with attributes other than the primary_key. DynamoDB supports two types of secondary indexes:

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html

Global Secondary Indexes

TableV2 can be configured with global_secondary_indexes by providing them as a TableV2 property:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    global_secondary_indexes: [
        {
            index_name: "gsi",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
        },
    ],
})

Multi-attribute Keys

Global secondary indexes support multi-attribute keys, allowing you to specify multiple partition keys and/or multiple sort keys. This enables more flexible query patterns for complex data models.

Key Constraints:

Example with multi-attribute partition and sort keys:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    global_secondary_indexes: [
        {
            index_name: "multi-attribute-gsi",
            partition_keys: [
                {name: "gsi_pk1", type: AWSCDK::DynamoDB::AttributeType::STRING},
                {name: "gsi_pk2", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
            ],
            sort_keys: [
                {name: "gsi_sk1", type: AWSCDK::DynamoDB::AttributeType::STRING},
                {name: "gsi_sk2", type: AWSCDK::DynamoDB::AttributeType::BINARY},
            ],
        },
    ],
})

You can also add a global_secondary_index using the add_global_secondary_index method:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    global_secondary_indexes: [
        {
            index_name: "gsi1",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
        },
    ],
})

table.add_global_secondary_index({
    index_name: "gsi2",
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

# Add a GSI with multi-attribute keys
table.add_global_secondary_index({
    index_name: "multi-attribute-gsi2",
    partition_keys: [
        {name: "multi-attribute_pk1", type: AWSCDK::DynamoDB::AttributeType::STRING},
        {name: "multi-attribute_pk2", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
    ],
    sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

You can configure read_capacity and write_capacity on a global_secondary_index when an TableV2 is configured with provisioned billing. If TableV2 is configured with provisioned billing but read_capacity or write_capacity are not configured on a global_secondary_index, then they will be inherited from the capacity settings specified with the billing configuration:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    billing: AWSCDK::DynamoDB::Billing.provisioned({
        read_capacity: AWSCDK::DynamoDB::Capacity.fixed(10),
        write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 10}),
    }),
    global_secondary_indexes: [
        {
            index_name: "gsi1",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
            read_capacity: AWSCDK::DynamoDB::Capacity.fixed(15),
        },
        {
            index_name: "gsi2",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
            write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({min_capacity: 5, max_capacity: 20}),
        },
    ],
})

All global_secondary_indexes for replica tables are inherited from the primary table. You can configure contributor_insights_specification and read_capacity for each global_secondary_index on a per-replica basis:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    contributor_insights_specification: {
        enabled: true,
    },
    billing: AWSCDK::DynamoDB::Billing.provisioned({
        read_capacity: AWSCDK::DynamoDB::Capacity.fixed(10),
        write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({max_capacity: 10}),
    }),
    # each global secondary index will inherit contributor insights as true
    global_secondary_indexes: [
        {
            index_name: "gsi1",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
            read_capacity: AWSCDK::DynamoDB::Capacity.fixed(15),
        },
        {
            index_name: "gsi2",
            partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
            write_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({min_capacity: 5, max_capacity: 20}),
        },
    ],
    replicas: [
        {
            region: "us-east-1",
            global_secondary_index_options: {
                gsi1: {
                    read_capacity: AWSCDK::DynamoDB::Capacity.autoscaled({min_capacity: 1, max_capacity: 10}),
                },
            },
        },
        {
            region: "us-east-2",
            global_secondary_index_options: {
                gsi2: {
                    contributor_insights_specification: {
                        enabled: false,
                    },
                },
            },
        },
    ],
})

Local Secondary Indexes

TableV2 can only be configured with local_secondary_indexes when a sort_key is defined as a TableV2 property.

You can provide local_secondary_indexes as a TableV2 property:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
    local_secondary_indexes: [
        {
            index_name: "lsi",
            sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
        },
    ],
})

Alternatively, you can add a local_secondary_index using the add_local_secondary_index method:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
    local_secondary_indexes: [
        {
            index_name: "lsi1",
            sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
        },
    ],
})

table.add_local_secondary_index({
    index_name: "lsi2",
    sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
})

Streams

Each DynamoDB table produces an independent stream based on all its writes, regardless of the origination point for those writes. DynamoDB supports two stream types:

DynamoDB Streams

A dynamo_stream can be configured as a TableV2 property. If the TableV2 instance has replica tables, then all replica tables will inherit the dynamo_stream setting from the primary table. If replicas are configured, but dynamo_stream is not configured, then the primary table and all replicas will be automatically configured with the NEW_AND_OLD_IMAGES stream view type.

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(self, "GlobalTable", {
    partition_key: {name: "id", type: AWSCDK::DynamoDB::AttributeType::STRING},
    dynamo_stream: AWSCDK::DynamoDB::StreamViewType::OLD_IMAGE,
    # tables in us-west-2, us-east-1, and us-east-2 all have dynamo stream type of OLD_IMAGES
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html

Kinesis Streams

A kinesis_stream can be configured as a TableV2 property. Replica tables will not inherit the kinesis_stream configured for the primary table and should added on a per-replica basis.

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

stream1 = AWSCDK::Kinesis::Stream.new(stack, "Stream1")
stream2 = AWSCDK::Kinesis::Stream.from_stream_arn(stack, "Stream2", "arn:aws:kinesis:us-east-2:123456789012:stream/my-stream")

global_table = AWSCDK::DynamoDB::TableV2.new(self, "GlobalTable", {
    partition_key: {name: "id", type: AWSCDK::DynamoDB::AttributeType::STRING},
    kinesis_stream: stream1,
     # for table in us-west-2
    replicas: [
        {region: "us-east-1"},
        {
            region: "us-east-2",
            kinesis_stream: stream2,
        },
    ],
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/kds.html

Keys

When an instance of the TableV2 construct is defined, you must define its schema using the partition_key (required) and sort_key (optional) properties.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    sort_key: {name: "sk", type: AWSCDK::DynamoDB::AttributeType::NUMBER},
})

Contributor Insights

Enabling contributor_insight_specification for TableV2 will provide information about the most accessed and throttled or throttled only items in a table or global_secondary_index. DynamoDB delivers this information to you via CloudWatch Contributor Insights rules, reports, and graphs of report data.

By default, Contributor Insights for DynamoDB monitors all requests, including both the most accessed and most throttled items. To limit the scope to only the most accessed or only the most throttled items, use the optional mode parameter.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    contributor_insights_specification: {
        enabled: true,
        mode: AWSCDK::DynamoDB::ContributorInsightsMode::ACCESSED_AND_THROTTLED_KEYS,
    },
})

When you use Table, you can enable contributor insights for a table or specific global secondary index by setting contributor_insights_specification parameter enabled to true.

table = AWSCDK::DynamoDB::Table.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    contributor_insights_specification: {
         # for a table
        enabled: true,
        mode: AWSCDK::DynamoDB::ContributorInsightsMode::THROTTLED_KEYS,
    },
})

table.add_global_secondary_index({
    contributor_insights_specification: {
         # for a specific global secondary index
        enabled: true,
    },
    index_name: "gsi",
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/contributorinsights_HowItWorks.html

Deletion Protection

deletion_protection determines if your DynamoDB table is protected from deletion and is configurable as a TableV2 property. When enabled, the table cannot be deleted by any user or process.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    deletion_protection: true,
})

You can also specify the removal_policy as a property of the TableV2 construct. This property allows you to control what happens to tables provisioned using TableV2 during stack deletion. By default, the removal_policy is RETAIN which will cause all tables provisioned using TableV2 to be retained in the account, but orphaned from the stack they were created in. You can also set the removal_policy to DESTROY which will delete all tables created using TableV2 during stack deletion:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    # applies to all replicas, i.e., us-west-2, us-east-1, us-east-2
    removal_policy: AWSCDK::RemovalPolicy::DESTROY,
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

deletion_protection is configurable on a per-replica basis. If the removal_policy is set to DESTROY, but some replicas have deletion_protection enabled, then only the replicas without deletion_protection will be deleted during stack deletion:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    removal_policy: AWSCDK::RemovalPolicy::DESTROY,
    deletion_protection: true,
    # only the replica in us-east-1 will be deleted during stack deletion
    replicas: [
        {
            region: "us-east-1",
            deletion_protection: false,
        },
        {
            region: "us-east-2",
            deletion_protection: true,
        },
    ],
})

Point-in-Time Recovery

point_in_time_recovery_specifcation provides automatic backups of your DynamoDB table data which helps protect your tables from accidental write or delete operations.

You can also choose to set recovery_period_in_days to a value between 1 and 35 which dictates how many days of recoverable data is stored. If no value is provided, the recovery period defaults to 35 days.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    point_in_time_recovery_specification: {
        point_in_time_recovery_enabled: true,
        recovery_period_in_days: 4,
    },
})

Table Class

You can configure a TableV2 instance with table classes:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    table_class: AWSCDK::DynamoDB::TableClass::STANDARD_INFREQUENT_ACCESS,
})

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.TableClasses.html

Tags

You can add tags to a TableV2 in several ways. By adding the tags to the construct itself it will apply the tags to the primary table.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    tags: [{key: "primaryTableTagKey", value: "primaryTableTagValue"}],
})

You can also add tags to replica tables by specifying them within the replica table properties.

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    replicas: [
        {
            region: "us-west-1",
            tags: [{key: "replicaTableTagKey", value: "replicaTableTagValue"}],
        },
    ],
})

Referencing Existing Global Tables

To reference an existing DynamoDB table in your CDK application, use the TableV2.fromTableName, TableV2.fromTableArn, or TableV2.fromTableAttributes factory methods:

user = nil # AWSCDK::IAM::User


table = AWSCDK::DynamoDB::TableV2.from_table_arn(self, "ImportedTable", "arn:aws:dynamodb:us-east-1:123456789012:table/my-table")
# now you can call methods on the referenced table
table.grant_read_write_data(user)

If you intend to use the table_stream_arn (including indirectly, for example by creating an aws-cdk-lib/aws-lambda-event-sources.DynamoEventSource on the referenced table), you must use the TableV2.fromTableAttributes method and the table_stream_arn property must be populated.

To grant permissions to indexes for a referenced table you can either set grant_index_permissions to true, or you can provide the indexes via the global_indexes or local_indexes properties. This will enable grant* methods to also grant permissions to all table indexes.

Resource Policy

Using resource_policy you can add a resource policy to a table in the form of a PolicyDocument:

    // resource policy document
    const policy = new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          actions: ['dynamodb:GetItem'],
          principals: [new iam.AccountRootPrincipal()],
          resources: ['*'],
        }),
      ],
    });

    // table with resource policy
    new dynamodb.TableV2(this, 'TableTestV2-1', {
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      removalPolicy: RemovalPolicy.DESTROY,
      resourcePolicy: policy,
    });

Adding Resource Policy Statements Dynamically

You can also add resource policy statements to a table after it's created using the add_to_resource_policy method. Following the same pattern as KMS, resource policies use wildcard resources to avoid circular dependencies:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

# Standard resource policy (recommended approach)
table.add_to_resource_policy(AWSCDK::IAM::PolicyStatement.new({
    actions: ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query"],
    principals: [AWSCDK::IAM::AccountRootPrincipal.new],
    resources: ["*"],
}))

# Allow specific service access
table.add_to_resource_policy(AWSCDK::IAM::PolicyStatement.new({
    actions: ["dynamodb:Query"],
    principals: [AWSCDK::IAM::ServicePrincipal.new("lambda.amazonaws.com")],
    resources: ["*"],
}))

Scoped Resource Policies (Advanced)

For scoped resource policies that reference specific table ARNs, you must specify an explicit table name:

require 'aws-cdk-lib'


# Table with explicit name enables scoped resource policies
table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    table_name: "my-explicit-table-name",
     # Required for scoped resources
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

# Now you can use scoped resources
table.add_to_resource_policy(AWSCDK::IAM::PolicyStatement.new({
    actions: ["dynamodb:GetItem"],
    principals: [AWSCDK::IAM::AccountRootPrincipal.new],
    resources: [
        AWSCDK::Fn.sub("arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-table-name"),
        AWSCDK::Fn.sub("arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/my-explicit-table-name/index/*"),
    ],
}))

Important Limitations:

TableV2 doesn’t support creating a replica and adding a resource-based policy to that replica in the same stack update in Regions other than the Region where you deploy the stack update. To incorporate a resource-based policy into a replica, you'll need to initially deploy the replica without the policy, followed by a subsequent update to include the desired policy.

Grant Methods and Resource Policies

Grant methods like grant_read_data(), grant_write_data(), and grant_read_write_data() automatically add permissions to resource policies when used with same-account principals (like AccountRootPrincipal). This happens transparently:

# Adds to IAM user's policy (not resource policy)
user = nil # AWSCDK::IAM::User
table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
})

# Automatically adds to table's resource policy (same account)
table.grant_read_data(AWSCDK::IAM::AccountRootPrincipal.new)
table.grant_read_data(user)

How it works:

This behavior follows the same pattern as other AWS services like KMS and S3, where grants intelligently choose between resource policies and identity policies based on the principal type.

To avoid wildcards in resource policies: If you need scoped resource ARNs instead of wildcards, use add_to_resource_policy() directly with an explicit table name instead of grant methods. See the "Scoped Resource Policies (Advanced)" section above for details.

Stream Resource Policy

You can attach a resource policy to a DynamoDB stream using stream_resource_policy. This applies per-replica, so you can set different policies for the primary table and each replica:

stream_policy = AWSCDK::IAM::PolicyDocument.new({
    statements: [
        AWSCDK::IAM::PolicyStatement.new({
            actions: ["dynamodb:DescribeStream", "dynamodb:GetRecords", "dynamodb:GetShardIterator"],
            principals: [AWSCDK::IAM::AccountRootPrincipal.new],
            resources: ["*"],
        }),
    ],
})

AWSCDK::DynamoDB::TableV2.new(self, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    dynamo_stream: AWSCDK::DynamoDB::StreamViewType::NEW_AND_OLD_IMAGES,
    stream_resource_policy: stream_policy,
    replicas: [
        {
            region: "us-west-2",
            stream_resource_policy: stream_policy,
        },
    ],
})

You can also add stream resource policy statements dynamically using add_to_stream_resource_policy:

table = AWSCDK::DynamoDB::TableV2.new(self, "Table", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    dynamo_stream: AWSCDK::DynamoDB::StreamViewType::NEW_AND_OLD_IMAGES,
})

table.add_to_stream_resource_policy(AWSCDK::IAM::PolicyStatement.new({
    actions: ["dynamodb:DescribeStream", "dynamodb:GetRecords", "dynamodb:GetShardIterator"],
    principals: [AWSCDK::IAM::AccountRootPrincipal.new],
    resources: ["*"],
}))

Note: add_to_stream_resource_policy applies to the primary table's stream only. To set a stream resource policy on a replica, pass stream_resource_policy in the replica props.

Grants

Using any of the grant* methods on an instance of the TableV2 construct will only apply to the primary table, its indexes, and any associated encryption_key. As an example, grant_read_data used below will only apply the table in us-west-2:

require 'aws-cdk-lib'

user = nil # AWSCDK::IAM::User


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

table_key = AWSCDK::KMS::Key.new(stack, "Key")
replica_key_arns = {
    "us-east-1" => "arn:aws:kms:us-east-1:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6",
    "us-east-2" => "arn:aws:kms:us-east-2:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6",
}

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    encryption: AWSCDK::DynamoDB::TableEncryptionV2.customer_managed_key(table_key, replica_key_arns),
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

# grantReadData only applies to the table in us-west-2 and the tableKey
global_table.grant_read_data(user)

The replica method can be used to grant to a specific replica table:

require 'aws-cdk-lib'

user = nil # AWSCDK::IAM::User


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

table_key = AWSCDK::KMS::Key.new(stack, "Key")
replica_key_arns = {
    "us-east-1" => "arn:aws:kms:us-east-1:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6",
    "us-east-2" => "arn:aws:kms:us-east-2:123456789012:key/g24efbna-az9b-42ro-m3bp-cq249l94fca6",
}

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    encryption: AWSCDK::DynamoDB::TableEncryptionV2.customer_managed_key(table_key, replica_key_arns),
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

# grantReadData applies to the table in us-east-2 and the key arn for the key in us-east-2
global_table.replica("us-east-2").grant_read_data(user)

Metrics

You can use metric* methods to generate metrics for a table that can be used when configuring an Alarm or Graphs. The metric* methods only apply to the primary table provisioned using the TableV2 construct. As an example, metric_consumed_read_capacity_units used below is only for the table in us-west-2:

require 'aws-cdk-lib'


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack", {env: {region: "us-west-2"}})

global_table = AWSCDK::DynamoDB::TableV2.new(stack, "GlobalTable", {
    partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
    replicas: [
        {region: "us-east-1"},
        {region: "us-east-2"},
    ],
})

# metric is only for the table in us-west-2
metric = global_table.metric_consumed_read_capacity_units

AWSCDK::CloudWatch::Alarm.new(self, "Alarm", {
    metric: metric,
    evaluation_periods: 1,
    threshold: 1,
})

The replica method can be used to generate a metric for a specific replica table:

require 'aws-cdk-lib'


class FooStack < AWSCDK::Stack
  attr_reader :global_table

  def initialize(scope, id, props)
    super(scope, id, props)

    @global_table = AWSCDK::DynamoDB::TableV2.new(self, "GlobalTable", {
        partition_key: {name: "pk", type: AWSCDK::DynamoDB::AttributeType::STRING},
        replicas: [
            {region: "us-east-1"},
            {region: "us-east-2"},
        ],
    })
  end
end

class BarStack < AWSCDK::Stack
  def initialize(scope, id, props)
    super(scope, id, props)

    # metric is only for the table in us-east-1
    metric = props[:replica_table].metric_consumed_read_capacity_units

    AWSCDK::CloudWatch::Alarm.new(self, "Alarm", {
        metric: metric,
        evaluation_periods: 1,
        threshold: 1,
    })
  end
end

app = AWSCDK::App.new
foo_stack = FooStack.new(app, "FooStack", {env: {region: "us-west-2"}})
bar_stack = BarStack.new(app, "BarStack", {
    replica_table: foo_stack.global_table.replica("us-east-1"),
    env: {region: "us-east-1"},
})

import from S3 Bucket

You can import data in S3 when creating a Table using the Table construct. To import data into DynamoDB, it is required that your data is in a CSV, DynamoDB JSON, or Amazon Ion format within an Amazon S3 bucket. The data may be compressed using ZSTD or GZIP formats, or you may choose to import it without compression. The data source can be a single S3 object or multiple S3 objects sharing a common prefix.

Further reading: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataImport.HowItWorks.html

use CSV format

The InputFormat.csv method accepts delimiter and header_list options as arguments. If delimiter is not specified, , is used by default. And if header_list is specified, the first line of CSV is treated as data instead of header.

require 'aws-cdk-lib'

bucket = nil # AWSCDK::S3::IBucket


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack")

AWSCDK::DynamoDB::Table.new(stack, "Table", {
    partition_key: {
        name: "id",
        type: AWSCDK::DynamoDB::AttributeType::STRING,
    },
    import_source: {
        compression_type: AWSCDK::DynamoDB::InputCompressionType::GZIP,
        input_format: AWSCDK::DynamoDB::InputFormat.csv({
            delimiter: ",",
            header_list: ["id", "name"],
        }),
        bucket: bucket,
        key_prefix: "prefix",
    },
})

use DynamoDB JSON format

Use the InputFormat.dynamoDBJson() method to specify the input_format property. There are currently no options available.

require 'aws-cdk-lib'

bucket = nil # AWSCDK::S3::IBucket


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack")

AWSCDK::DynamoDB::Table.new(stack, "Table", {
    partition_key: {
        name: "id",
        type: AWSCDK::DynamoDB::AttributeType::STRING,
    },
    import_source: {
        compression_type: AWSCDK::DynamoDB::InputCompressionType::GZIP,
        input_format: AWSCDK::DynamoDB::InputFormat.dynamo_db_json,
        bucket: bucket,
        key_prefix: "prefix",
    },
})

use Amazon Ion format

Use the InputFormat.ion() method to specify the input_format property. There are currently no options available.

require 'aws-cdk-lib'

bucket = nil # AWSCDK::S3::IBucket


app = AWSCDK::App.new
stack = AWSCDK::Stack.new(app, "Stack")

AWSCDK::DynamoDB::Table.new(stack, "Table", {
    partition_key: {
        name: "id",
        type: AWSCDK::DynamoDB::AttributeType::STRING,
    },
    import_source: {
        compression_type: AWSCDK::DynamoDB::InputCompressionType::GZIP,
        input_format: AWSCDK::DynamoDB::InputFormat.ion,
        bucket: bucket,
        key_prefix: "prefix",
    },
})

API Reference

Classes 13

BillingRepresents how capacity is managed and how you are charged for read and write throughput f CapacityRepresents the amount of read and write operations supported by a DynamoDB table. CfnGlobalTableThe `AWS::DynamoDB::GlobalTable` resource enables you to create and manage a Version 2019. CfnTableThe `AWS::DynamoDB::Table` resource creates a DynamoDB table. For more information, see [C InputFormatThe format of the source data. StreamGrantsA set of permissions to grant on a Table Stream. TableProvides a DynamoDB table. TableBase TableBaseV2Base class for a DynamoDB table. TableEncryptionV2Represents server-side encryption for a DynamoDB table. TableGrantsA set of permissions to grant on a Table. TableV2A DynamoDB Table. TableV2MultiAccountReplicaA multi-account replica of a DynamoDB table.

Interfaces 35

AttributeRepresents an attribute for describing the key schema for the table and indexes. AutoscaledCapacityOptionsOptions used to configure autoscaled capacity. CfnGlobalTablePropsProperties for defining a `CfnGlobalTable`. CfnTablePropsProperties for defining a `CfnTable`. ContributorInsightsSpecificationReference to ContributorInsightsSpecification. CsvOptionsThe options for imported source files in CSV format. EnableScalingPropsProperties for enabling DynamoDB capacity scaling. GlobalSecondaryIndexPropsProperties for a global secondary index. GlobalSecondaryIndexPropsV2Properties used to configure a global secondary index. ImportSourceSpecificationProperties for importing data from the S3. IScalableTableAttributeInterface for scalable attributes. ITableAn interface that represents a DynamoDB Table - either created with the CDK, or an existin ITableV2Represents an instance of a DynamoDB table. KeySchemaA description of a key schema of an LSI, GSI or Table. LocalSecondaryIndexPropsProperties for a local secondary index. MaxThroughputPropsProperties used to configure maximum throughput for an on-demand table. OperationsMetricOptionsOptions for configuring metrics that considers multiple operations. PointInTimeRecoverySpecificationReference to PointInTimeRecovey Specification for continuous backups. ReplicaGlobalSecondaryIndexOptionsOptions used to configure global secondary indexes on a replica table. ReplicaTablePropsProperties used to configure a replica table. SchemaOptionsRepresents the table schema attributes. SecondaryIndexPropsProperties for a secondary index. StreamGrantsPropsConstruction properties for StreamGrants. SystemErrorsForOperationsMetricOptionsOptions for configuring a system errors metric that considers multiple operations. TableAttributesReference to a dynamodb table. TableAttributesV2Attributes of a DynamoDB table. TableGrantsPropsConstruction properties for TableGrants. TableOptionsProperties of a DynamoDB Table. TableOptionsV2Options used to configure a DynamoDB table. TablePropsProperties for a DynamoDB Table. TablePropsV2Properties used to configure a DynamoDB table. TableV2MultiAccountReplicaPropsProperties for creating a multi-account replica table. ThroughputPropsProperties used to configure provisioned throughput for a DynamoDB table. UtilizationScalingPropsProperties for enabling DynamoDB utilization tracking. WarmThroughputReference to WarmThroughput for a DynamoDB table.

Enums 13

ApproximateCreationDateTimePrecisionThe precision associated with the DynamoDB write timestamps that will be replicated to Kin AttributeTypeData types for attributes within a table. BillingModeDynamoDB's Read/Write capacity modes. CapacityModeCapacity modes. ContributorInsightsModeDynamoDB's Contributor Insights Mode. GlobalTableSettingsReplicationModeThe replication mode for global table settings across multiple accounts. InputCompressionTypeType of compression to use for imported data. MultiRegionConsistencyGlobal table multi-region consistency mode. OperationSupported DynamoDB table operations. ProjectionTypeThe set of attributes that are projected into the index. StreamViewTypeWhen an item in the table is modified, StreamViewType determines what information is writt TableClassDynamoDB's table class. TableEncryptionWhat kind of server-side encryption to apply to this table.