How to set a Deletion Policy on a Resource in AWS CDK

avatar
Borislav Hadzhiev

Last updated: Jan 26, 2024
4 min

banner

# Deletion Policy and Removal Policy

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.

However, the default behavior in CDK is that if we don't specify a Removal Policy and delete the stack, the resources are retained, but orphaned from the stack.

# Setting a Removal Policy in CDK

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.

The code for this article is available on GitHub

I'll cdk deploy a small application, consisting of an S3 bucket and a Dynamodb table in order to demo the behavior.

lib/cdk-starter-stack.ts
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, }); } }
If you still use CDK version 1, switch to the cdk-v1 branch in the GitHub repository.

In the code sample:

  1. I defined an S3 bucket and set its 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.
lib/cdk-starter-stack.ts
const s3Bucket = new s3.Bucket(this, id, { removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteObjects: true, });
  1. I defined a DynamoDB table and set its removalPolicy prop to RETAIN. This causes the resources to be retained in the account but orphaned from the stack.
There is a third option for a removal policy - 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.

cloudformation stack

Let's test what happens if I comment out the lines that provision the Dynamodb table in my CDK code.

lib/cdk-starter-stack.ts
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.

shell
npx aws-cdk diff

I can see that if I run the deploy command at this point, the Dynamodb table will become orphaned.

orphaned table

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:

lib/cdk-starter-stack.ts
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.

shell
npx aws-cdk diff

The output looks like:

replace resource

We can see that if we deploy our stack at the current state, the Dynamodb table will be replaced.

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.

shell
npx aws-cdk deploy

If I open the Dynamodb console, we can see that both my-cdk-stack-* tables remain in the account:

tables after update

However, the table with the Partition key of todoId is orphaned and detached from the CloudFormation stack.

# Using applyRemovalPolicy in AWS CDK

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);

# Discussion

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.

shell
npx aws-cdk diff

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev