Delete CloudFormation Stack while Retaining Resources

This post will be useful for you if you want to delete a CloudFormation stack without deleting all or some of its resources, but you don’t want to or cannot use the standard approach with deletion policy which requires updating the stack template.

The Challenge

If you take a look at cloudformation delete-stack AWS CLI command, you’ll find that there’s a parameter --retain-resources which allows retaining or skipping, instead of deleting, resources when the stack is deleted. However, for some reason, it can only be used for resources in DELETE_FAILED state.

The Trick

To leverage the --retain-resources parameter, we need to induce the DELETE_FAILED state for the resources we want to retain. Here’s how to achieve this safely:

  1. Create an IAM role with permissions to delete the stack but without permissions to delete specific resources you want to keep.
  2. Attempt to delete the CloudFormation stack using this IAM role.
  3. The deletion attempt will fail for the resources the role can’t delete, marking them with DELETE_FAILED status.
  4. You can then use the --retain-resources parameter to keep these resources while deleting the stack.

This approach allows you to selectively retain resources without modifying the original stack template.

The Process

With this strategy in mind, here’s the step-by-step process to selectively delete a CloudFormation stack:

  1. Create an IAM role that can delete the stack but lacks permissions to delete the resources you want to retain.
  2. Attempt to delete the stack using this role, which will result in DELETE_FAILED state for the desired resources.
  3. Use AWS CLI to delete the stack again, this time with the --retain-resources "ResourceLogicalId" parameter. Replace “ResourceLogicalId” with the logical ID of the resource(s) you want to keep. You can list multiple resources by separating their IDs with spaces.
  4. If not all desired resources achieve the DELETE_FAILED state on the first attempt, repeat steps 2 and 3. This may be necessary depending on CloudFormation’s deletion logic.

Automation Script

Below is a Bash script that automates this process:

#!/usr/bin/env bash
stack_name="$STACK_NAME"
stack_role_arn="$STACK_ROLE_ARN"

echo "Initial delete attempt"

aws cloudformation delete-stack --stack-name "$stack_name" \
  --role-arn "$stack_role_arn"

sleep 5

for i in {1..3}
do
   echo "Delete iteration attempt $i"
   
   failed_resources=$(aws cloudformation list-stack-resources \
     --stack-name "$stack_name" \
     --query "StackResourceSummaries[?ResourceStatus=='DELETE_FAILED'].LogicalResourceId" \
     --output text)
   
   echo "Skipping failed resources: $failed_resources"
   
   aws cloudformation delete-stack --stack-name "$stack_name" \
   --role-arn "$stack_role_arn" --retain-resources $failed_resources
   
   sleep 5
done

Pay attention that the script expects 2 environment variables to be set: STACK_NAME and STACK_ROLE_ARN.

You can run it like so:

STACK_NAME="your-stack-name" \
STACK_ROLE_ARN="arn:aws:iam::your-account-id:role/stack-role-name" \
./this-script.sh

I’ve used this script on more than 10 different stacks and 2-3 iterations is usually enough. However, if you need more iterations, just modify the for i in {1..3} line.

Lastly, make sure to test this in a test environment first!