Last updated: Jan 26, 2024
Reading timeยท4 min
A deletion policy in CloudFormation enables us to specify what should happen to stateful resources (databases, S3 buckets) when a stack gets deleted.
The specified deletion policy also applies in case we delete the resource from our CloudFormation/CDK code.
The Deletion Policy
from CloudFormation is called Removal Policy
in CDK.
The default behavior for CloudFormation is that if we don't specify a "Deletion Policy" and delete the stack then the resources are deleted.
Removal Policy
and delete the stack, the resources are retained, but orphaned from the stack.In order to set a removalPolicy
in CDK, we have to pass the removalPolicy
prop to the construct of the stateful resource, for example, an S3 bucket or
Dynamodb table.
I'll cdk deploy
a small application, consisting of an
S3 bucket and a
Dynamodb table in order to demo the behavior.
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); const s3Bucket = new s3.Bucket(this, id, { // ๐ set a removal policy of DESTROY removalPolicy: cdk.RemovalPolicy.DESTROY, }); const table = new dynamodb.Table(this, 'my-table', { partitionKey: {name: 'todoId', type: dynamodb.AttributeType.NUMBER}, billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // ๐ set a removal policy of RETAIN removalPolicy: cdk.RemovalPolicy.RETAIN, }); } }
cdk-v1
branch in the GitHub repository.In the code sample:
removalPolicy
to DESTROY
. Note that
only empty S3 buckets get deleted with a removal policy set to DESTROY
. If
we want to also delete a bucket that contains objects, we have to also set
the autoDeleteObjects
property to true
.const s3Bucket = new s3.Bucket(this, id, { removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteObjects: true, });
removalPolicy
prop to RETAIN
. This
causes the resources to be retained in the account but orphaned from the
stack.RemovalPolicy.SNAPSHOT
. This option deletes the stateful resource but saves a snapshot of the state right before deletion, so the resource can be re-created later. The SNAPSHOT
policy is only available for some stateful resources like relational databases and EFS volumes.At this point, I've deployed a CloudFormation template, consisting of an S3
bucket with a RemovalPolicy.DESTROY
and a Dynamodb table with a
RemovalPolicy.RETAIN
.
Let's test what happens if I comment out the lines that provision the Dynamodb table in my CDK code.
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); // ...rest // ๐ I've commented out the table with policy RETAIN // const table = new dynamodb.Table(this, 'my-table', { // partitionKey: {name: 'todoId', type: dynamodb.AttributeType.NUMBER}, // billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // removalPolicy: cdk.RemovalPolicy.RETAIN, // }); } }
If I now running the diff command.
npx aws-cdk diff
I can see that if I run the deploy
command at this point, the Dynamodb table
will become orphaned.
The orphan status means that the table will remain in the account, but it will be detached from the stack.
Next, we'll test what happens if we execute an update that requires resource replacement.
I'll uncomment the code that provisions the Dynamodb table and I'll change the name of the partition key:
const table = new dynamodb.Table(this, 'my-table', { - partitionKey: {name: 'todoId', type: dynamodb.AttributeType.NUMBER}, + partitionKey: {name: 'another-one', type: dynamodb.AttributeType.NUMBER}, billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, removalPolicy: cdk.RemovalPolicy.RETAIN, });
Let's run the diff command again.
npx aws-cdk diff
The output looks like:
Since we've set the removal policy of the table to RETAIN
, let's test if the
old table is retained or deleted after an update that requires replacement.
I'll run the deploy command.
npx aws-cdk deploy
If I open the Dynamodb console, we can see that both my-cdk-stack-*
tables
remain in the account:
However, the table with the Partition key of todoId
is orphaned and detached from
the CloudFormation stack.
CFN Resources don't implement the
removalPolicy
property. In order to set a removal policy on CFN resources
and constructs that don't support the removalPolicy
property, we have to call
the applyRemovalPolicy
method after instantiating the construct (credit
Andreas Brunner from the comment section):
const cfnrepo = new cc.CfnRepository(this, 'demo', { repositoryName: 'demorepo', repositoryDescription: 'This is my demo repo', }); cfnrepo.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
If we don't set a removal policy then the default behavior in CDK is to retain but detach the resources from the CloudFormation stack.
This differs from the default behavior for the deletion policy in CloudFormation, which is to delete all resources that don't specify a deletion policy.
A very common source of confusion in CDK is that changing the id
parameter of
a construct or refactoring it to a different scope changes the resource's
CloudFormation logical ID.
This causes the old resource to be deleted and a new resource with the new logical ID to be created. In other words, this causes resource replacement.
If you're interested in reading more about Identifiers in CDK I've written an article on What AWS CDK identifiers are.
To be on the safe side of things, it's always a best practice to run the
cdk diff
command before deploying, especially when updating stateful resources
like databases.
npx aws-cdk diff
You can learn more about the related topics by checking out the following tutorials: