In our effort to make our production environment multi-region in AWS, we aimed to transform our DynamoDB single-region tables into global tables. While this may seem like a simple task, similar to the manual way, just go the DynamoDB and under the “Global table” tab create a replica in another region, when working with a CloudFormation stack simply changing the AWS::DynamoDB::Table
resource in the CloudFormation template to AWS::DynamoDB::GlobalTable
was not a viable solution.Unfortunately, this approach would likely result in the deletion of our existing tables and the creation of new global tables, resulting in a complete loss of data. Given the significance of our data, this was not a scenario we could accept.
After conducting some research, we were fortunate to find a straightforward solution to achieve our goal. Our migration plan was as follows:
- We updated our CloudFormation template to include a “DeletionPolicy Retain” [1] for each resource.
The “DeletionPolicy: Retain” ensures that even if the CloudFormation template is deleted, the resources associated with the stack will not be deleted. - We deleted our DynamoDB stack.
As a part of our migration plan, we carefully deleted our DynamoDB stack, resulting in a standalone regular DynamoDB table that was no longer associated with any provisioning too - Under the “global table” tab in the DynamoDB console we created replicas in our failover regions. Our DyanmoDB table now became a global table.
This action is easy and safe — there is no chance of data loss and no downtime (only some lags and slowness on the replicas while being created). - With CDK, we generated a new CloudFormation template [1] for DynamoDB, designed specifically for global tables.
The previous template, which defined a regular table, contained a resource of typeAWS::DynamoDB::Table
In contrast, the new template was structured around theAWS::DynamoDB::GlobalTable
type, and included a “Replicas” property to define all the replicas we had created.
Replicas:
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: us-east-1
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: us-east-2
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: eu-central-1
* [2] Below you can see a full example of the before and after - We created a new CloudFormation stack with the generated CloudFormation template that supports a global table. The new stack use the ”import existing resources” method rather than creating a new resource.
On the CloudFormation console, under “Create Stack” you have the option to choose “with existing resources (import resources). We choose these options, uploaded the template, and filled in the name of the table we want to import.
Great! We successfully linked our DynamoDB global table to a CloudFormation stack that was generated using the AWS Cloud Development Kit (CDK). Every time we make changes to the code with the CDK, which updates the CloudFormation template and results in a deployment, our GlobalTable will reflect these updates.
Side notes:
[1] In order to add a deletion policy, under the type add DeletionPolicy (indented the same in the same tab as the type):
MyDyanmoDBTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
[2] To generate a global DynamoDB table using the CDK we used the “CfnGlobalTable” class, this is the preferred class to use since
the “GlobalTable” class generated a weird yaml with all kind of dependencies.
[3] Before:
MyDyanmoDBTable:
Type: AWS::DynamoDB::Table
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Properties:
KeySchema:
- AttributeName: key
KeyType: HASH
AttributeDefinitions:
- AttributeName: key
AttributeType: S
BillingMode: PAY_PER_REQUEST
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
TableName: MyTable
After:
MyDyanmoDBTable:
Type: AWS::DynamoDB::GlobalTable
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: key
AttributeType: S
KeySchema:
- AttributeName: key
KeyType: HASH
Replicas:
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: us-east-1
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: us-east-2
- PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
Region: eu-central-1
BillingMode: PAY_PER_REQUEST
TableName: MyTable
I hope this article is useful to you! If you require any assistance, please don’t hesitate to reach out to me at sharon@cloudbit.dev. I am always happy to help 🙂