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
60 changes: 60 additions & 0 deletions dynamodb-cross-account-replication-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Amazon DynamoDB Cross-Account Replication with Global Tables

This pattern deploys a DynamoDB Global Table with cross-account replication and an IAM role for secure cross-account read access.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/dynamodb-cross-account-replication-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
* [Node.js](https://nodejs.org/en/download/) installed
* Two AWS accounts (source and replica)

## Deployment Instructions

1. Clone and navigate to the pattern:
```
cd serverless-patterns/dynamodb-cross-account-replication-cdk
npm install
```
2. Deploy with the replica account ID:
```
cdk deploy --parameters ReplicaAccountId=123456789012 --parameters ReplicaRegion=us-west-2
```

## How it works

- A DynamoDB Global Table is created with a replica in the specified region
- DynamoDB automatically replicates all writes to the replica with sub-second latency
- A cross-account IAM role allows the replica account to assume and read from the table
- Point-in-time recovery is enabled for data protection

## Testing

```bash
# Write an item to the source table
aws dynamodb put-item --table-name $(aws cloudformation describe-stacks \
--stack-name DynamodbCrossAccountReplicationStack \
--query 'Stacks[0].Outputs[?OutputKey==`TableName`].OutputValue' --output text) \
--item '{"PK":{"S":"user#123"},"SK":{"S":"profile"},"name":{"S":"test"}}'

# Read from replica region (same account)
aws dynamodb get-item --table-name <TableName> \
--key '{"PK":{"S":"user#123"},"SK":{"S":"profile"}}' \
--region us-west-2
```

## Cleanup

```
cdk destroy
```

---

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

SPDX-License-Identifier: MIT-0
12 changes: 12 additions & 0 deletions dynamodb-cross-account-replication-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { DynamodbCrossAccountReplicationStack } from '../lib/dynamodb-cross-account-replication-stack';

const app = new cdk.App();
new DynamodbCrossAccountReplicationStack(app, 'DynamodbCrossAccountReplicationStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT || '123456789012',
region: process.env.CDK_DEFAULT_REGION || 'us-east-1',
},
});
3 changes: 3 additions & 0 deletions dynamodb-cross-account-replication-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"
}
40 changes: 40 additions & 0 deletions dynamodb-cross-account-replication-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"title": "Amazon DynamoDB Cross-Account Replication with Global Tables",
"description": "Deploy a DynamoDB Global Table with cross-account replication and IAM role for secure cross-account read access.",
"language": "TypeScript",
"level": "300",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern creates a DynamoDB Global Table that replicates data across AWS accounts and regions.",
"A cross-account IAM role enables the replica account to read from the table securely.",
"DynamoDB handles replication automatically with sub-second latency between regions.",
"Point-in-time recovery is enabled for data protection."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/dynamodb-cross-account-replication-cdk",
"templateURL": "serverless-patterns/dynamodb-cross-account-replication-cdk",
"projectFolder": "dynamodb-cross-account-replication-cdk",
"templateFile": "lib/dynamodb-cross-account-replication-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": "DynamoDB Global Tables Documentation", "link": "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GlobalTables.html" }
]
},
"deploy": { "text": ["cdk deploy --parameters ReplicaAccountId=123456789012"] },
"testing": { "text": ["See the README for testing instructions."] },
"cleanup": { "text": ["cdk destroy"] },
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS, passionate about serverless and AI/ML.",
"linkedin": "nithin-chandran-r"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DynamodbCrossAccountReplicationStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const replicaAccountId = new cdk.CfnParameter(this, 'ReplicaAccountId', {
type: 'String',
description: 'AWS Account ID for the replica table',
});

const table = new dynamodb.TableV2(this, 'SourceTable', {
partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
billing: dynamodb.Billing.onDemand(),
pointInTimeRecovery: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
replicas: [
{
region: 'us-west-2',
},
],
});

// IAM role for cross-account access to the replica
const crossAccountRole = new iam.Role(this, 'CrossAccountReplicaRole', {
assumedBy: new iam.AccountPrincipal(replicaAccountId.valueAsString),
description: 'Allows replica account to read from the global table replica',
});

crossAccountRole.addToPolicy(new iam.PolicyStatement({
actions: [
'dynamodb:GetItem',
'dynamodb:Query',
'dynamodb:Scan',
'dynamodb:BatchGetItem',
],
resources: [
table.tableArn,
`${table.tableArn}/index/*`,
],
}));

new cdk.CfnOutput(this, 'TableName', { value: table.tableName });
new cdk.CfnOutput(this, 'TableArn', { value: table.tableArn });
new cdk.CfnOutput(this, 'CrossAccountRoleArn', { value: crossAccountRole.roleArn });
new cdk.CfnOutput(this, 'ReplicaRegion', { value: 'us-west-2' });
}
}
16 changes: 16 additions & 0 deletions dynamodb-cross-account-replication-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "dynamodb-cross-account-replication-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": { "build": "tsc", "cdk": "cdk" },
"dependencies": {
"aws-cdk-lib": "^2.180.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"typescript": "~5.4.0",
"ts-node": "^10.9.0",
"@types/node": "^20.0.0"
}
}
8 changes: 8 additions & 0 deletions dynamodb-cross-account-replication-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2020", "module": "commonjs", "lib": ["es2020"],
"declaration": true, "strict": true, "outDir": "build",
"rootDir": ".", "skipLibCheck": true, "forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules", "build"]
}