Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.js
*.d.ts
node_modules
cdk.out
cdk.context.json
package-lock.json
111 changes: 111 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Amazon DynamoDB Global Tables — True Multi-Account Replication

This pattern deploys a DynamoDB Global Table that replicates data across two separate AWS accounts using `TableV2MultiAccountReplica`. Writes in Account A are automatically replicated to Account B with sub-second latency.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/dynamodb-global-tables-multi-account-cdk

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details.

## Requirements

* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed (v2.252.0+)
* [Node.js](https://nodejs.org/en/download/) 20.x or later
* Two AWS accounts with credentials configured
* CDK bootstrapped in both accounts/regions

## Architecture

```
┌─────────────────────────────────┐ ┌─────────────────────────────────┐
│ Account A (742460038667) │ │ Account B (843577947854) │
│ us-east-1 │ │ us-west-2 │
│ │ │ │
│ ┌───────────────────────────┐ │ auto │ ┌───────────────────────────┐ │
│ │ DynamoDB GlobalTable │──┼────────▶│ │ DynamoDB Replica (CFN) │ │
│ │ MultiAccountGlobalTable │ │ replicate │ MultiAccountGlobalTable │ │
│ └───────────────────────────┘ │ │ └───────────────────────────┘ │
│ │ │ │
│ Source Stack │ │ Replica Stack │
└─────────────────────────────────┘ └─────────────────────────────────┘
```

## How it works

1. **Source Stack** (Account A): Creates a DynamoDB Global Table with on-demand billing and point-in-time recovery
2. **Replica Stack** (Account B): Uses `TableV2MultiAccountReplica` to join the Global Table as a replica in a different account and region
3. DynamoDB automatically handles replication — writes in either account propagate to the other with sub-second latency
4. CDK automatically configures the resource policies needed for cross-account replication

**Note:** The replica must be in a different region from the source. This is a DynamoDB Global Tables requirement.

## Deployment Instructions

1. Clone and install:
```bash
cd serverless-patterns/dynamodb-global-tables-multi-account-cdk
npm install
```

2. Bootstrap CDK in both accounts (if not already done):
```bash
npx cdk bootstrap aws://ACCOUNT_A/us-east-1 --profile source-profile
npx cdk bootstrap aws://ACCOUNT_B/us-west-2 --profile replica-profile
```

3. Deploy the source stack first:
```bash
npx cdk deploy DynamoDbMultiAccountSourceStack \
-c sourceAccount=ACCOUNT_A \
-c sourceRegion=us-east-1 \
-c replicaAccount=ACCOUNT_B \
-c replicaRegion=us-west-2 \
--profile source-profile
```

4. Deploy the replica stack:
```bash
npx cdk deploy DynamoDbMultiAccountReplicaStack \
-c sourceAccount=ACCOUNT_A \
-c sourceRegion=us-east-1 \
-c replicaAccount=ACCOUNT_B \
-c replicaRegion=us-west-2 \
--profile replica-profile
```

## Testing

```bash
# Write an item in Account A (source)
aws dynamodb put-item \
--table-name MultiAccountGlobalTable \
--item '{"PK":{"S":"user#123"},"SK":{"S":"profile"},"name":{"S":"test"}}' \
--region us-east-1 --profile source-profile

# Read the item from Account B (replica) — should appear within ~1 second
aws dynamodb get-item \
--table-name MultiAccountGlobalTable \
--key '{"PK":{"S":"user#123"},"SK":{"S":"profile"}}' \
--region us-west-2 --profile replica-profile
```

## Cleanup

Destroy in reverse order:
```bash
npx cdk destroy DynamoDbMultiAccountReplicaStack \
-c sourceAccount=ACCOUNT_A -c sourceRegion=us-east-1 \
-c replicaAccount=ACCOUNT_B -c replicaRegion=us-west-2 \
--profile replica-profile

npx cdk destroy DynamoDbMultiAccountSourceStack \
-c sourceAccount=ACCOUNT_A -c sourceRegion=us-east-1 \
-c replicaAccount=ACCOUNT_B -c replicaRegion=us-west-2 \
--profile source-profile
```

---

Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
20 changes: 20 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { SourceTableStack } from '../lib/source-table-stack';
import { ReplicaTableStack } from '../lib/replica-table-stack';

const app = new cdk.App();

const sourceAccount = app.node.tryGetContext('sourceAccount');
const sourceRegion = app.node.tryGetContext('sourceRegion') || 'us-east-1';
const replicaAccount = app.node.tryGetContext('replicaAccount');
const replicaRegion = app.node.tryGetContext('replicaRegion') || 'us-east-1';

const sourceStack = new SourceTableStack(app, 'DynamoDbMultiAccountSourceStack', {
env: { account: sourceAccount, region: sourceRegion },
});

new ReplicaTableStack(app, 'DynamoDbMultiAccountReplicaStack', {
env: { account: replicaAccount, region: replicaRegion },
sourceTable: sourceStack.table,
});
3 changes: 3 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts"
}
41 changes: 41 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"title": "Amazon DynamoDB Global Tables with True Multi-Account Replication",
"description": "Deploy a DynamoDB Global Table that replicates across two separate AWS accounts using TableV2MultiAccountReplica. Deployed with AWS CDK.",
"language": "TypeScript",
"level": "300",
"framework": "AWS CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern creates a DynamoDB Global Table in Account A and a multi-account replica in Account B using TableV2MultiAccountReplica.",
"Writes in either account are automatically replicated to the other with sub-second latency.",
"CDK handles the cross-account resource policies needed for replication.",
"The replica must be in a different region from the source (Global Tables requirement)."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/dynamodb-global-tables-multi-account-cdk",
"templateURL": "serverless-patterns/dynamodb-global-tables-multi-account-cdk",
"projectFolder": "dynamodb-global-tables-multi-account-cdk",
"templateFile": "lib/source-table-stack.ts"
}
},
"resources": {
"bullets": [
{ "text": "DynamoDB Global Tables cross-account replication", "link": "https://aws.amazon.com/blogs/database/amazon-dynamodb-global-tables-now-support-replication-across-aws-accounts/" },
{ "text": "CDK TableV2MultiAccountReplica", "link": "https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_dynamodb.TableV2MultiAccountReplica.html" },
{ "text": "DynamoDB Global Tables documentation", "link": "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GlobalTables.html" }
]
},
"deploy": { "text": ["See README for multi-account deployment steps"] },
"testing": { "text": ["See the README for testing instructions."] },
"cleanup": { "text": ["<code>cdk destroy</code> in reverse order (replica first, then source)"] },
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS",
"linkedin": "nithin-chandran-r"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

export interface ReplicaTableStackProps extends cdk.StackProps {
sourceTable: dynamodb.ITableV2;
}

export class ReplicaTableStack extends cdk.Stack {
public readonly replica: dynamodb.TableV2MultiAccountReplica;

constructor(scope: Construct, id: string, props: ReplicaTableStackProps) {
super(scope, id, props);

this.replica = new dynamodb.TableV2MultiAccountReplica(this, 'ReplicaTable', {
tableName: 'MultiAccountGlobalTable',
replicaSourceTable: props.sourceTable,
globalTableSettingsReplicationMode: dynamodb.GlobalTableSettingsReplicationMode.ALL,
removalPolicy: cdk.RemovalPolicy.DESTROY,
pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
});

new cdk.CfnOutput(this, 'ReplicaTableName', { value: this.replica.tableName });
new cdk.CfnOutput(this, 'ReplicaTableArn', { value: this.replica.tableArn });
}
}
24 changes: 24 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/lib/source-table-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

export class SourceTableStack extends cdk.Stack {
public readonly table: dynamodb.TableV2;

constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

this.table = new dynamodb.TableV2(this, 'SourceTable', {
tableName: 'MultiAccountGlobalTable',
partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
billing: dynamodb.Billing.onDemand(),
pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
globalTableSettingsReplicationMode: dynamodb.GlobalTableSettingsReplicationMode.ALL,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});

new cdk.CfnOutput(this, 'TableName', { value: this.table.tableName });
new cdk.CfnOutput(this, 'TableArn', { value: this.table.tableArn });
}
}
15 changes: 15 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "dynamodb-global-tables-multi-account-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": { "build": "tsc", "cdk": "cdk" },
"devDependencies": {
"aws-cdk": "^2.258.0",
"typescript": "~5.7.0",
"ts-node": "^10.9.2"
},
"dependencies": {
"aws-cdk-lib": "^2.258.0",
"constructs": "^10.0.0"
}
}
17 changes: 17 additions & 0 deletions dynamodb-global-tables-multi-account-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["es2020"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"outDir": "build",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules", "cdk.out"]
}