AWS Database Blog
Zero-downtime DynamoDB construct migration: from Table to TableV2 with cdk orphan
When you define an Amazon DynamoDB table using the AWS Cloud Development Kit (AWS CDK), you have two construct options: Table and TableV2. Both create fully functional DynamoDB tables, and for single AWS Region workloads, Table is feature-rich and works well. However, when your application must go global with replicas, TableV2 becomes the better choice. It maps directly to the AWS::DynamoDB::GlobalTable AWS CloudFormation resource, which means replicas are managed natively by CloudFormation with per-replica configuration, drift detection, and no custom resource AWS Lambda functions.
The challenge is migrating. If you swap Table for TableV2 in your CDK code, CloudFormation interprets this as a resource replacement. It removes your existing table and creates a new one. Previously, avoiding this required a careful, multi-step manual process through the CloudFormation console. With cdk orphan, you can now perform this migration directly from the CLI.
In this post, we show you how to use the new cdk orphan command to safely migrate a DynamoDB table from the Table construct to TableV2 with zero downtime. Your data stays intact, streams keep flowing, and your application remains available throughout the process.
Why start with TableV2
The Table construct has served CDK users well and continues to be fully supported. For single-Region tables, it provides everything you need: on-demand or provisioned capacity, global secondary indexes, DynamoDB Streams, and more.
The difference becomes apparent when you need replicas. The Table construct uses a Lambda-backed custom resource to manage replicas, and the replicationRegions property only accepts a list of Region strings. You can’t configure properties like contributor insights, point-in-time recovery, or table class on a per-replica basis. These settings apply uniformly across all replicas.
TableV2 addresses this by mapping directly to AWS::DynamoDB::GlobalTable. It works for single-Region tables too, so starting with TableV2 future-proofs your infrastructure. If your application must go global, you can add replicas without changing constructs.
The following table summarizes the key differences when replicas are involved:
| Capability | Table (with replicas) | TableV2 |
|---|---|---|
| CloudFormation resource | AWS::DynamoDB::Table + custom resource | AWS::DynamoDB::GlobalTable |
| Replica management | Lambda-backed custom resource | Native CloudFormation |
| Per-replica configuration (PITR, table class, contributor insights, capacity) | Not supported | Supported |
| Drift detection for replicas | Not supported | Supported |
| Customer-managed KMS per replica | Not supported | Supported |
The migration challenge
Consider a CDK stack with a DynamoDB table that stores order data, with a Lambda function consuming its stream through an event source mapping. This is a common pattern for event-driven architectures.
Whether your table is single-Region or already has replicas configured through the Table construct’s replicationRegions property, the migration path is the same. The cdk orphan workflow works in both cases. If you already have replicas, they remain intact throughout the process.
Your business is growing and you must switch to TableV2, either to add your first replica, or to gain per-replica configuration and native CloudFormation management for replicas that you already have. But if you replace the construct:
Running cdk diff reveals the problem:
CloudFormation plans to delete the existing AWS::DynamoDB::Table and create a new AWS::DynamoDB::GlobalTable. Your data, stream ARN, and active event source mappings would be lost.
Solution overview
The cdk orphan command solves this by detaching a resource from its CloudFormation stack without deleting it. Combined with cdk import, you can re-import the same physical resource under a new construct. The workflow has three steps:
- Orphan – Detach the existing table from the stack. CloudFormation removes the resource from its state, but the table continues to serve traffic.
- Update your CDK code – Replace the
Tableconstruct withTableV2, pointing to the same physical table. - Import – Import the existing table into the stack under the new
TableV2construct.
Throughout this process, the DynamoDB table remains fully operational. Reads, writes, and stream processing continue uninterrupted.
Prerequisites
To follow along with this walkthrough, you need:
- An AWS account.
- AWS CDK v2.x installed with the
cdk orphancommand available. - Node.js 20.x or later.
- A bootstrapped CDK environment with bootstrap stack version 32 or later. The
cdk orphancommand requires additional AWS Identity and Access Management (IAM) permissions added in this version. If your environment is already bootstrapped, re-runcdk bootstrapto update.
Walkthrough
Set up the project
Before we begin, we must create a new CDK project and install dependencies. Create a directory, initialize a TypeScript CDK app, and then replace the generated stack code with our example:
Replace the contents of lib/orders-app-stack.ts with the following code. This creates a DynamoDB table with streams enabled and a Lambda function that processes stream events through an event source mapping:
Step 1: Deploy the initial stack
Bootstrap your environment (or update to version 32 if already bootstrapped):
Then deploy the stack:
After the deployment completes, you should see the stack outputs:
Verify that the table is created:
Now, add some data to the table. We will verify that this data is still accessible after the migration:
Step 2: Orphan the table
Before orphaning, we recommend creating an on-demand backup as a precaution:
Use cdk orphan to detach the table from the CloudFormation stack:
The --unstable=orphan flag is required because this command is currently in developer preview. This means the API might change in future releases before it becomes stable.
If you have multiple tables to migrate, you can orphan them in a single command by passing multiple construct paths:
The command performs three deployments behind the scenes:
- Resolve references – Temporarily adds CloudFormation outputs to capture Ref and Fn::GetAtt values that other resources depend on (such as the table name and stream ARN referenced by the Lambda event source mapping).
- Decouple – Replaces all cross-resource references with their resolved literal values, sets DeletionPolicy: Retain, and removes DependsOn entries. This isolates the resource from the rest of the stack.
- Remove – Removes the resource from the template entirely. CloudFormation drops it from its state, but because the deletion policy was set to Retain, the physical table remains untouched.
After the orphan completes, the command outputs a resource mapping that you will use for the import step:
Save this value. You will need it in Step 4.
Step 3: Update your CDK code
Replace the Table construct with TableV2 in lib/orders-app-stack.ts. The construct shape is slightly different, so you will need to adjust the properties to suit TableV2:
The Lambda function and event source mapping remain unchanged. Note that we haven’t added replicas yet. We first import the existing table under TableV2, then add replicas in a subsequent deployment.
Step 4: Import the table
Use cdk import with the resource mapping from Step 2 to bring the existing table into the stack under the new TableV2 construct:
CloudFormation imports the existing physical table and associates it with the new AWS::DynamoDB::GlobalTable resource in the template. No data is moved or modified.
After the import completes, cdk import will detect drift between the imported resource and your template and prompt you to deploy. Accept the prompt to reconcile the template with the actual resource state, updating the Lambda event source mapping and IAM policies to reference the imported table’s attributes.
Step 5: Add replicas
Now that your table is managed by TableV2, you can add replicas. Update your CDK code:
Deploy:
CloudFormation natively creates the replica with no custom resource Lambda, full drift detection, and per-replica configuration from day one.
Verifying the migration
After the migration, verify that everything is intact. First, confirm that the table exists and has the replica:
Then verify the data that we inserted before the migration is still accessible:
You should see the item returned with CurrentStatus: SHIPPED, confirming that your data survived the migration. The stream ARN is preserved, and the Lambda event source mapping continues to process events. The table is now managed by TableV2 with a replica in eu-west-1.
Considerations
Keep the following in mind when performing this migration:
- Import before adding new replicas – Import the table as a
TableV2first, then add new replicas in a subsequent deployment. If your table already has replicas, include them in yourTableV2definition at import time so CloudFormation recognizes them. - Stream consumers – If you have Lambda functions consuming the DynamoDB stream, the stream ARN is preserved during the migration. Event source mappings continue to work without modification.
- Test in a non-production environment first – Validate the full workflow in a development or staging environment before performing the migration on production tables.
Cleaning up
If you created resources to follow along with this walkthrough, delete them to avoid ongoing charges:cdk destroy OrdersAppStack
Conclusion
Migrating from the CDK Table construct to TableV2 no longer requires extensive manual operations. It has always been possible to perform these steps manually through the CloudFormation console, but the process is time consuming and adds operational overhead. With cdk orphan, you can safely detach a DynamoDB table from its CloudFormation stack, update your CDK code to use TableV2, and re-import the same physical table, all while your application continues to serve traffic.
If you’re starting a new project, we recommend using TableV2 from the beginning. It works for single-Region tables and positions you to add replicas with native CloudFormation support whenever your application must go global.
To learn more, see the TableV2 construct documentation and the AWS CDK CLI reference.