You can’t just delete a CDK stack and expect all its resources to vanish cleanly.

The CDK deployment process creates a CloudFormation stack, and CloudFormation manages most of your resources. When you run cdk destroy, CDK calls CloudFormation to delete the stack. CloudFormation then starts deleting the resources within that stack. The trick is that CloudFormation has a specific order for deletion, and some resources have dependencies that prevent them from being deleted until other resources are gone, or vice-versa. This is where things get stuck.

Here’s how to safely destroy a CDK stack and its resources:

1. Dependencies are King: IAM Roles and Policies

Problem: CloudFormation often gets stuck trying to delete IAM roles or policies that are still in use by other resources, even if those other resources are also marked for deletion. This is a common deadlock.

Diagnosis: Look for CloudFormation stack events showing DELETE_FAILED with messages like "Role 'my-role-name' cannot be deleted because it is in use" or "Policy 'my-policy-name' cannot be deleted because it is in use."

Fix: Manually detach or delete the resources using the IAM roles/policies before running cdk destroy. For example, if an EC2 instance is using a role, stop and terminate the EC2 instance first. If a Lambda function is using a role, delete the Lambda function.

Why it works: By removing the consumers of the IAM role or policy, you ensure that when CloudFormation attempts to delete them, they are no longer referenced, allowing the deletion to proceed.

2. Resource Deletion Policies: Retain vs. Destroy

Problem: By default, many CDK constructs will configure their underlying CloudFormation resources with a DeletionPolicy of Retain. This means that even when the stack is destroyed, the resource itself (e.g., an S3 bucket, an RDS instance) will not be deleted. You’ll see DELETE_FAILED errors, or worse, the stack deletion might appear to succeed but leave orphaned resources behind.

Diagnosis:

  • Check your CDK code for cdk.CfnResource or specific constructs where you might have explicitly set removalPolicy: cdk.RemovalPolicy.RETAIN.
  • Examine CloudFormation stack events for DELETE_FAILED messages related to specific resource types (like S3 buckets, RDS instances, DynamoDB tables) with explanations about why they weren’t deleted.

Fix: Explicitly set the removalPolicy to cdk.RemovalPolicy.DESTROY on your CDK constructs.

from aws_cdk import (
    aws_s3 as s3,
    Stack,
    RemovalPolicy
)
from constructs import Construct

class MyStack(Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Example for an S3 bucket
        bucket = s3.Bucket(self, "MyDataBucket",
                           removalPolicy=RemovalPolicy.DESTROY, # <-- This is key
                           autoDeleteObjects=True) # Also useful for buckets

        # Example for a DynamoDB table
        # table = dynamodb.Table(self, "MyTable",
        #                        removalPolicy=RemovalPolicy.DESTROY, # <-- This is key
        #                        partition_key=dynamodb.Attribute(name="id", type=dynamodb.AttributeType.STRING))

Why it works: RemovalPolicy.DESTROY translates to CloudFormation’s DeletionPolicy: Delete, instructing CloudFormation to delete the resource when the stack is deleted. autoDeleteObjects=True on S3 buckets ensures that objects within the bucket are also deleted before the bucket itself is removed.

3. Orphaned Resources and Manual Cleanup

Problem: Sometimes, due to manual interventions, failed deployments, or complex resource interactions, resources might exist in your AWS account that are no longer managed by CDK or CloudFormation. Attempting to destroy a stack with these orphaned resources can lead to DELETE_FAILED errors if CloudFormation tries to create a resource that already exists, or it can simply leave the stack in a weird state.

Diagnosis:

  • Use the AWS console to inspect resources in the region where your CDK stack was deployed. Pay close attention to resource types that your stack creates (e.g., VPCs, EC2 instances, RDS databases, S3 buckets, Lambda functions, IAM roles).
  • Check the Resources tab of your CloudFormation stack in the AWS console. If there are resources listed that don’t seem to be managed by the stack anymore, or if you see resources in the AWS console that aren’t listed in the stack, you have an issue.

Fix: Manually delete the orphaned resources via the AWS console or AWS CLI before attempting cdk destroy.

Why it works: Removing these external dependencies or conflicting resources ensures that CloudFormation has a clean slate to operate on when it attempts to delete the stack.

4. Resource Deletion Order and Dependencies within CloudFormation

Problem: CloudFormation has a default deletion order, but sometimes custom resources or complex interdependencies can cause issues. For instance, a VPC might be deleted before a security group that references it, or a database might be deleted before a security group allowing access to it.

Diagnosis: Look for DELETE_FAILED events in CloudFormation with messages indicating that a resource cannot be deleted because another resource it depends on is still present or has not been fully deleted.

Fix:

  • Add explicit DependsOn: In your CDK code, you can sometimes add explicit DependsOn attributes to CfnResource constructs to influence deletion order. This is a last resort and can make your CDK code harder to reason about.
  • Break down complex stacks: For very complex stacks, consider splitting them into smaller, more manageable stacks. This limits the scope of potential deletion order issues.
  • Manual intervention: In extreme cases, you might need to manually delete resources in a specific order through the AWS console to break the dependency loop, then allow cdk destroy to clean up the rest.

Why it works: Explicitly defining dependencies or simplifying the stack structure helps CloudFormation understand the required deletion sequence, preventing it from attempting to delete resources that are still required by others.

5. Resource Deletion Protection

Problem: Some AWS resources have deletion protection enabled at the resource level (not to be confused with CloudFormation stack protection). If this is enabled, CloudFormation will fail to delete the resource, and consequently, the stack.

Diagnosis: Check the individual resource’s configuration in the AWS console. For example, RDS instances have a "Deletion protection" setting.

Fix: Manually disable deletion protection on the specific resource through the AWS console or AWS CLI before running cdk destroy.

Why it works: Deletion protection prevents accidental or unintended data loss by requiring explicit action to remove the resource. Disabling it allows CloudFormation to perform its intended deletion.

6. Long-Running Resources and Timeouts

Problem: Some resources take a long time to delete (e.g., large RDS instances, complex VPCs with many subnets and NAT gateways). CloudFormation has a default timeout for stack operations. If deletion takes too long, it might time out, leaving the stack in a DELETE_FAILED state and resources partially deleted or stuck.

Diagnosis: CloudFormation stack events will show a timeout, or the stack status will remain DELETE_IN_PROGRESS for an extended period.

Fix: There isn’t a direct CDK fix for this other than patience. You can monitor the deletion process in the CloudFormation console. If it gets stuck, you might need to manually intervene on the stuck resource (e.g., cancel a database snapshot deletion) and then re-initiate the stack deletion. For very large or complex resources, consider deleting them manually before cdk destroy if you anticipate this problem.

Why it works: Patience or manual intervention allows the resource deletion process to complete naturally, bypassing the CloudFormation timeout.

The Next Hurdle

After you’ve successfully destroyed your CDK stack and all its resources, you might find yourself facing the next challenge: "My local development environment still has stale cached credentials or configurations that are pointing to the now-deleted AWS resources, causing local errors or confusion."

Want structured learning?

Take the full Cdk course →