Skip to content

feat(dynamodb): add cross-account global table replication support#36895

Merged
mergify[bot] merged 14 commits intoaws:mainfrom
LeeroyHannigan:lhnng-multi
Feb 16, 2026
Merged

feat(dynamodb): add cross-account global table replication support#36895
mergify[bot] merged 14 commits intoaws:mainfrom
LeeroyHannigan:lhnng-multi

Conversation

@LeeroyHannigan
Copy link
Copy Markdown
Contributor

Reason for this change

DynamoDB global tables currently only support same-account replication across regions. Customers need the ability to replicate tables across AWS accounts for multi-tenant architectures, organizational boundaries, and disaster recovery scenarios.

Description of changes

Added support for cross-account DynamoDB global table replication through a new TableV2MultiAccountReplica construct and automatic permission management:

Core Changes:

  • Added TableV2MultiAccountReplica construct for creating replicas from source tables in different accounts
  • Added settingsReplicationMode property to control replication of table settings (indexes, streams, encryption)
  • Implemented automatic resource policy and KMS permissions for cross-account replication
  • Added TableGrants class with multiAccountReplicationTo() and multiAccountReplicationFrom() methods

Permission Management:

  • Source table resource policy grants dynamodb:AssociateTableReplica to replica account
  • Both tables grant DynamoDB replication service read/write permissions with aws:SourceAccount conditions
  • Automatic KMS key permissions when encryption is enabled

Design Decisions:

  • Used aws:SourceAccount condition instead of aws:SourceArn to avoid circular dependencies
  • Only pass encryptedResource when encryption key exists to avoid unnecessary policy statements
  • Validate that source and replica are in different regions
  • Require explicit table names for cross-account replicas when source uses generated names

Describe any new or updated permissions being added

DynamoDB Table Resource Policies:

  • dynamodb:AssociateTableReplica - Granted to replica account on source table
  • dynamodb:ReadDataForReplication, dynamodb:WriteDataForReplication, dynamodb:ReplicateSettings - Granted to replication.dynamodb.amazonaws.com service principal with aws:SourceAccount condition

KMS Key Permissions (when encryption enabled):

  • kms:Decrypt, kms:DescribeKey, kms:Encrypt, kms:ReEncrypt*, kms:GenerateDataKey* - Granted to replication.dynamodb.amazonaws.com service principal

Description of how you validated changes

Unit Tests:

  • Added tests for TableV2MultiAccountReplica construct
  • Added tests for cross-account replication permission grants
  • Added tests for KMS permission grants
  • Added tests for validation (same-region check, table name requirements)

Integration Tests:

  • Created integ.dynamodb-v2.cross-account-replica.ts - Tests full cross-account replication with both tables in same CDK app
  • Created integ.dynamodb-v2.cross-account-replica-imported.ts - Tests replication with imported source table from different account
  • Manually deployed and verified bidirectional replication works with SettingsReplicationMode.ALL

Checklist

@aws-cdk-automation aws-cdk-automation requested a review from a team February 5, 2026 21:16
@github-actions github-actions Bot added valued-contributor [Pilot] contributed between 6-12 PRs to the CDK p2 labels Feb 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 5, 2026

⚠️ Experimental Feature: This security report is currently in experimental phase. Results may include false positives and the rules are being actively refined.
Please try merge from main to avoid findings unrelated to the PR.


TestsPassed ✅SkippedFailed
Security Guardian Results120 ran120 passed
TestResult
No test annotations available

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 5, 2026

⚠️ Experimental Feature: This security report is currently in experimental phase. Results may include false positives and the rules are being actively refined.
Please try merge from main to avoid findings unrelated to the PR.


TestsPassed ✅SkippedFailed
Security Guardian Results with resolved templates120 ran120 passed
TestResult
No test annotations available

@aws-cdk-automation aws-cdk-automation added the pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. label Feb 5, 2026
Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts Outdated
Comment on lines +737 to +869

// Validate replicaSourceTable if provided
if (props.replicaSourceTable) {
const sourceStack = Stack.of(props.replicaSourceTable);
const sourceRegion = sourceStack.splitArn(props.replicaSourceTable.tableArn, ArnFormat.SLASH_RESOURCE_NAME).region;
if (sourceRegion === this.region) {
throw new ValidationError(
`The region in \`replicaSourceArn\` (${sourceRegion}) cannot be the same as the stack region (${this.region}).`,
this,
);
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, you don't set the replica properties here. Only set them on the replica class.

import { IKey } from '../../aws-kms';
import { Grant } from '../../aws-iam';
import type { IKey } from '../../aws-kms';
>>>>>>> internal/main
Copy link
Copy Markdown
Member

@Abogical Abogical Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed.

Suggested change
>>>>>>> internal/main

Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-v2-base.ts Outdated
Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts Outdated
Comment thread packages/aws-cdk-lib/aws-dynamodb/README.md
Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts
@mergify mergify Bot dismissed Abogical’s stale review February 6, 2026 19:56

Pull request has been modified.

Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-v2.ts
Copy link
Copy Markdown
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mergify mergify Bot dismissed Abogical’s stale review February 16, 2026 11:10

Pull request has been modified.

}

this.policyResource.addToResourcePolicy(new iam.PolicyStatement({
sid: 'AllowMultiAccountReplicaAssociation',
Copy link
Copy Markdown
Member

@Abogical Abogical Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be a conflict in the SID if the table has multiple cross-account replicas. Multiple policies will be generated with the same SID's, leading to a deployment conflict. Can the SID include the destination account and table name to make sure it is deduplicated and unique?

Suggested change
sid: 'AllowMultiAccountReplicaAssociation',
sid: `AllowMultiAccountReplicaAssociation-${arnComponents.account}`,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}));

this.policyResource.addToResourcePolicy(new iam.PolicyStatement({
sid: 'AllowReplicationServiceReadWrite',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion as above.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hasn't been addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread packages/aws-cdk-lib/aws-dynamodb/lib/table-grants.ts
@mergify mergify Bot dismissed Abogical’s stale review February 16, 2026 14:34

Pull request has been modified.

}));

this.policyResource.addToResourcePolicy(new iam.PolicyStatement({
sid: 'AllowReplicationServiceReadWrite',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hasn't been addressed.

@mergify mergify Bot dismissed Abogical’s stale review February 16, 2026 14:52

Pull request has been modified.

Copy link
Copy Markdown
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a unit test error, see: https://github.com/aws/aws-cdk/actions/runs/22067324790/job/63762511491?pr=36895#step:9:3500

aws-cdk-lib: FAI
aws-cdk-lib: L aws-
aws-cdk-lib: dynamodb/tes
aws-cdk-lib: t/table-v
aws-cdk-lib: 2.test.t
aws-cdk-lib: s (22.319
aws-cdk-lib:  s)
aws-cdk-lib:   
aws-cdk-lib: ● grantMulti
aws-cdk-lib: AccountRepl
aws-cdk-lib: icationTo
aws-cdk-lib:  adds req
aws-cdk-lib: uired resour
aws-cdk-lib: ce policy 
aws-cdk-lib: statement
aws-cdk-lib: s
aws-cdk-lib:     E
aws-cdk-lib: xpected valu
aws-cdk-lib: e   undefi
aws-cdk-lib: ned
aws-cdk-lib:     R
aws-cdk-lib: eceived:
aws-cdk-lib:    
aws-cdk-lib:    undefine
aws-cdk-lib: d
aws-cdk-lib:     Me
aws-cdk-lib: ssage:
aws-cdk-lib:      
aws-cdk-lib:  Template ha
aws-cdk-lib: s 1 resou
aws-cdk-lib: rces with
aws-cdk-lib:  type AWS
aws-cdk-lib: ::Dynamo
aws-cdk-lib: DB::GlobalTa
aws-cdk-lib: ble, but n
aws-cdk-lib: one matc
aws-cdk-lib: h as ex
aws-cdk-lib: pec
aws-cdk-lib: ted.
aws-cdk-lib:     
aws-cdk-lib: The 1 close
aws-cdk-lib: st match
aws-cdk-lib: es:
aws-cdk-lib:     T
aws-cdk-lib: ableCD117FA1
aws-cdk-lib:  :: {
aws-cdk-lib:   
aws-cdk-lib:     "DeletionPolicy
aws-cdk-lib: ": "Retain",
aws-cdk-lib:       "Properties": {
aws-cdk-lib:         "AttributeDefinitions": [ { ... } ],
aws-cdk-lib:         "BillingMode": "PAY_PER_REQUEST",
aws-cdk-lib:         "KeySchema": [ { ... } ],
aws-cdk-lib:         "Replicas": [
aws-cdk-lib:           {
aws-cdk-lib:             "Region": "us-east-2",
aws-cdk-lib:             "ResourcePolicy": {
aws-cdk-lib:               "Policy
aws-cdk-lib: D
aws-cdk-lib: ocument": {
aws-cdk-lib:                 "Statement": [
aws-cdk-lib:                   {
aws-cdk-lib:                     "Action": "dynamodb:AssociateTableReplica",
aws-cdk-lib:                     "Effect": "Allow",
aws-cdk-lib:                     "Principal": { "AWS": { "Fn::Join": [ ... ] } },
aws-cdk-lib:                     "Resource": "*",
aws-cdk-lib:     !!               Expected AllowMultiAccountReplicaAssociation but received AllowMultiAccountReplicaAssociation-222222222222
aws-cdk-lib:                     "Sid": "AllowMultiAccountReplicaAssociation-222222222222"
aws-cdk-lib:                   },
aws-cdk-lib:                   {
aws-cdk-lib:                     "Action": [ ... ],
aws-cdk-lib:                     "Condition": { "StringEquals": { "aws:SourceAccount": [ "111111111111", "222222222222" ] } },
aws-cdk-lib:                     "Effect": "Allow",
aws-cdk-lib:                     "Principal": { "Service": "replication.dynamodb.amazonaws.com" },
aws-cdk-lib:                     "Resource": "*",
aws-cdk-lib:     !!               Expected AllowReplicationServiceReadWrite but received AllowReplicationServiceReadWrite-222222222222
aws-cdk-lib:                     "Sid": "AllowReplicationServiceReadWrite-2222222
aws-cdk-lib: 22222"
aws-cdk-lib:                   }
aws-cdk-lib:                 ],
aws-cdk-lib:                 "Version": "2012-10-17"
aws-cdk-lib:               }
aws-cdk-lib:             }
aws-cdk-lib:           }
aws-cdk-lib:         ]
aws-cdk-lib:       },
aws-cdk-lib:       "Type": "AWS::DynamoDB::GlobalTable",
aws-cdk-lib:       "UpdateReplacePolicy": "Retain"
aws-cdk-lib:     }
aws-cdk-lib:     Difference:
aws-cdk-lib:     Compared values have no visual difference.
aws-cdk-lib:       106 |     const matchError = hasResourceProperties(this.template, type, props);
aws-cdk-lib:       107 |     if (matchError) {
aws-cdk-lib:     > 108 |       throw new AssertionError(matchError);
aws-cdk-lib:           |             ^
aws-cdk-lib:       109 |     }
aws-cdk-lib:       110 |   }
aws-cdk-lib:       111 |
aws-cdk-lib:       at Template.hasResourceProperties (assertions/lib/template.ts:108:13)
aws-cdk-lib:       at Object.<anonymous> (aws-dynamodb/test/table-v2.test.ts:3739:29)
aws-cdk-lib:   ● TableV2MultiAccountReplica creates replica with permissions
aws-cdk-lib:     Expected value   undefined
aws-cdk-lib:     Received:
aws-cdk-lib:       undefined
aws-cdk-lib:     Message:
aws-cdk-lib:       Template has 1 resources with type AWS::DynamoDB::GlobalTable, but none match as expected.
aws-cdk-lib:     The 1 closest matches:
aws-cdk-lib:     SourceTable70380C26 :: {
aws-cdk-lib:       "DeletionPolicy": "Retain
aws-cdk-lib: ",
aws-cdk-lib:       "Properties": {
aws-cdk-lib:         "AttributeDefinitions": [ { ... } ],
aws-cdk-lib:         "BillingMode": "PAY_PER_REQUEST",
aws-cdk-lib:         "KeySchema": [ { ... } ],
aws-cdk-lib:         "Replicas": [
aws-cdk-lib:           {
aws-cdk-lib:             "Region": "us-east-2",
aws-cdk-lib:             "ResourcePolicy": {
aws-cdk-lib:               "PolicyDocument": {
aws-cdk-lib:                 "Statement": [
aws-cdk-lib:     !!             Could not match arrayWith pattern 0. This is the closest match
aws-cdk-lib:                   {
aws-cdk-lib:                     "Action": "dynamodb:AssociateTableReplica",
aws-cdk-lib:                     "Effect": "Allow",
aws-cdk-lib:                     "Principal": { "AWS": { "Fn::Join": [ ... ] } },
aws-cdk-lib:                     "Resource": "*",
aws-cdk-lib:     !!               Expected type string but received object
aws-cdk-lib:                     "Sid": {
aws-cdk-lib:                       "Fn::Join": [ ... ]
aws-cdk-lib:                     }
aws-cdk-lib:                   },
aws-cdk-lib:                   { ... }
aws-cdk-lib:                 ],
aws-cdk-lib:                 "Version": "2012-10-17"
aws-cdk-lib:               }
aws-cdk-lib:             }
aws-cdk-lib:           }
aws-cdk-lib:         ],
aws-cdk-lib:         "TableName": "sourcestackstacksourcetable4d2d74602f5cc7577a18"
aws-cdk-lib:       },
aws-cdk-lib:       "Type": "AWS::DynamoDB::GlobalTable",
aws-cdk-lib:       "UpdateReplacePolicy": "Retain"
aws-cdk-lib:     }
aws-cdk-lib:     Difference:
aws-cdk-lib:     Compared values have no visual difference.
aws-cdk-lib:       106 |     const matchError = hasResourceProperties(this.template, type, props);
aws-cdk-lib:       107 |     if (matchError) {
aws-cdk-lib:     > 108 |       throw new AssertionError(matchError);
aws-cdk-lib:           |             ^
aws-cdk-lib:       109 |     }
aws-cdk-lib:       110 |   }
aws-cdk-lib:       111 |
aws-cdk-lib:       at Template.hasResourceProperties (assertions/lib/template.ts:108:13)
aws-cdk-lib:       at Object.<anonymous> (aws-dynamodb/test/table-v2.test.ts:3901:35)
aws-cdk-lib: Test Suites: 1 failed, 821 passed, 822 total
aws-cdk-lib: Tests:       2 failed, 3 skipped, 16921 passed, 16926 total
aws-cdk-lib: Snapshots:   13 passed, 13 total
aws-cdk-lib: Time:        252.958 s
aws-cdk-lib: Ran all test suites.
aws-cdk-lib: 
aws-cdk-lib: error Command failed with exit code 1.

@mergify mergify Bot dismissed Abogical’s stale review February 16, 2026 15:49

Pull request has been modified.

Abogical
Abogical previously approved these changes Feb 16, 2026
@Abogical
Copy link
Copy Markdown
Member

@Mergifyio update

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Feb 16, 2026

update

❌ Mergify doesn't have permission to update

Details

For security reasons, Mergify can't update this pull request. Try updating locally.
GitHub response: refusing to allow a GitHub App to create or update workflow .github/workflows/integration-test-deployment.yml without workflows permission

@mergify mergify Bot dismissed Abogical’s stale review February 16, 2026 16:02

Pull request has been modified.

@Abogical Abogical added the priority-pr push the PR to priority squash queue label Feb 16, 2026
@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Feb 16, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Feb 16, 2026

Merge Queue Status

Rule: priority-squash


  • Entered queue2026-02-16 16:45 UTC
  • Checks passed · in-place
  • Merged2026-02-16 17:15 UTC · at f4b0f06d89572c6ece908a9cbe36d1fadb66ec7b

This pull request spent 29 minutes 53 seconds in the queue, including 29 minutes 43 seconds running CI.

Required conditions to merge

@mergify
Copy link
Copy Markdown
Contributor

mergify Bot commented Feb 16, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify Bot merged commit 353a9bb into aws:main Feb 16, 2026
22 of 23 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Feb 16, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

p2 pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. priority-pr push the PR to priority squash queue valued-contributor [Pilot] contributed between 6-12 PRs to the CDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants