Skip to content
cdkd

cdkd destroy

cdkd destroy deletes the AWS resources of a stack and then removes cdkd's record of that stack. It asks for confirmation first. It also refuses to delete anything that would lose data unless you opted in: a bucket that still holds objects, a database with deletion protection on, a resource whose DeletionPolicy is Retain or Snapshot.

# one stack, with a confirmation prompt
cdkd destroy MyStack

# every stack under a Stage, no prompt
cdkd destroy 'MyStage/*' --yes

# every top-level stack in the CDK app
cdkd destroy --all -f

# turn deletion protection off first
cdkd destroy MyStack --remove-protection

# skip the DeletionPolicy: Snapshot snapshots
cdkd destroy MyStack --skip-final-snapshot

# no CDK app needed, reads state only
cdkd state destroy MyStack --yes

cdkd decides what to delete from the stack's state record: the state.json file in the state bucket that lists every resource cdkd deployed for the stack. cdkd state destroy reads only that record, so it works without the CDK app. These pages apply to both commands unless a section says otherwise.

Preview what a destroy will remove

There is no --dry-run. Run the command without --yes instead: cdkd prints every resource it would delete and waits for your answer. If you answer n, cdkd has locked nothing and deleted nothing.

$ cdkd destroy MyStack

Resources to be deleted (3):
  - Queue (AWS::SQS::Queue)
  - Handler (AWS::Lambda::Function)
  - Uploads (AWS::S3::Bucket)

Are you sure you want to destroy stack MyStack and delete all 3 resources? (Y/n): y
  ✓ Handler (AWS::Lambda::Function) deleted
  ✓ Queue (AWS::SQS::Queue) deleted
  ⊘ Uploads (AWS::S3::Bucket) retained — DeletionPolicy: Retain

✓ Stack MyStack destroyed (2 deleted, 1 retained, 0 errors)

The output above is trimmed to the lines you act on. In this run the bucket has DeletionPolicy: Retain, so cdkd left it in AWS and stopped tracking it.

To see the same list without starting a destroy, run cdkd state resources MyStack.

What a destroy does, in order

What cdkd destroy does, in ordercdkd destroy selects the stacks; a stack that reads another stack's outputs goes before the stack it reads from. It then runs the checks that refuse before anything changes: terminationProtection, an output another stack still imports, a damaged state record, and a missing --yes on a non-interactive stdin. It lists the resources and asks to confirm, takes the stack lock, and deletes the resources, dependents first. A resource with DeletionPolicy Retain is left in AWS and its record dropped; one with Snapshot gets a final snapshot and is then deleted. When every delete is confirmed, state.json and lock.json are removed and the run exits 0, with the event history kept unless --purge-events is passed. When a delete failed or was skipped, or the run was interrupted, state.json is kept with what is left and the run exits 2.every delete confirmedfailed, skipped or Ctrl-CSelect stacksA stack that reads anotherstack's outputs goes firstChecks that refusebefore anything changesterminationProtectionAn output another stack still importsA damaged state recordNo --yes on a non-interactive stdinList the resources and ask to confirmTake the stack lock, delete resourcesDependents firstRetain: left in AWS, record droppedSnapshot: final snapshot taken, then deletedExit 0state.json andlock.json removedEvent historykept unless--purge-eventsExit 2state.json keptwith what is leftRe-run to finishWhat cdkd destroy does, in ordercdkd destroy selects the stacks; a stack that reads another stack's outputs goes before the stack it reads from. It then runs the checks that refuse before anything changes: terminationProtection, an output another stack still imports, a damaged state record, and a missing --yes on a non-interactive stdin. It lists the resources and asks to confirm, takes the stack lock, and deletes the resources, dependents first. A resource with DeletionPolicy Retain is left in AWS and its record dropped; one with Snapshot gets a final snapshot and is then deleted. When every delete is confirmed, state.json and lock.json are removed and the run exits 0, with the event history kept unless --purge-events is passed. When a delete failed or was skipped, or the run was interrupted, state.json is kept with what is left and the run exits 2.every delete confirmedfailed,skippedor Ctrl-CSelect stacksA stack that reads anotherstack's outputs goes firstChecks that refusebefore anything changesterminationProtectionAn output another stack still importsA damaged state recordNo --yes on a non-interactive stdinList the resources and ask to confirmTake the stack lock, delete resourcesDependents firstRetain: left in AWS, record droppedSnapshot: final snapshot taken, then deletedExit 0state.json andlock.json removedEvent history keptunless --purge-eventsExit 2state.json keptwith what is leftRe-run to finish

Each resource ends in one of four ways, and the summary line counts them:

Summary line The resource in AWS Its entry in the state record
N deleted Gone Removed
N retained Kept, because of DeletionPolicy: Retain Removed
N skipped May still exist Kept
N errors May still exist Kept

A run with skipped resources or errors exits 2 and keeps state.json, cut down to the resources that are left. Running the same command again continues from there. A failed delete is retried by the re-run. A skipped resource needs its state entry repaired first.

What is left after a clean destroy

A destroy that exits 0 can still leave these behind:

  • Resources with DeletionPolicy: Retain. The policy asks cdkd to keep them. cdkd no longer tracks them, so delete them in AWS when you are done.
  • Final snapshots of DeletionPolicy: Snapshot resources. AWS bills for them. Delete them in AWS, or pass --skip-final-snapshot next time. See final snapshots.
  • The stack's deployment-event history. cdkd keeps it so you can look back at what happened. Remove it with --purge-events.
  • Resources an earlier rollback kept. A rollback leaves a DeletionPolicy: Retain resource in AWS and notes it in the state record. The destroy lists these resources before the prompt and then stops tracking them. Delete them in AWS.
  • Stacks inside a CDK Stage, when you passed --all. --all selects top-level stacks only. Use cdkd destroy 'MyStage/*' or '**'.

What can stop a destroy

Each of these refuses or fails the destroy until you act. The linked section says what to do.

What stops it What you see Read
No --yes and no terminal NON_INTERACTIVE_CONFIRM, exit 1 Non-interactive runs
terminationProtection: true on the stack, in the code or at its last deploy STACK_TERMINATION_PROTECTION, exit 2 --remove-protection
Another stack imports one of its outputs STACK_HAS_ACTIVE_IMPORTS Use Another Stack's Outputs
A bucket or ECR repository that still holds data That resource fails, exit 2 Non-empty buckets and repositories
Deletion protection on a resource That resource fails with the AWS error, exit 2 --remove-protection
A Snapshot resource cdkd cannot snapshot That resource is refused, exit 2 When cdkd refuses to delete
A nested stack named directly Refused, exit 2 Nested stacks
Another --state-prefix records the same stack Refused before any prompt or delete The same stack name under another state prefix
A damaged state record STATE_RESOURCES_MALFORMED, exit 1 A damaged state record
Another command wrote the stack's state while the confirmation prompt waited STATE_CHANGED, exit 1, nothing deleted; re-run the destroy —
An unfinished cdkd refactor move involves the stack REFACTOR_UNFINISHED, exit 1 If a refactor is interrupted

For terminationProtection, the other way through is to set it to false in your CDK code and redeploy. For an imported output, destroy the importing stack first.

The same stack name under another state prefix

cdkd destroy and cdkd state destroy read the stack's marker in the stack registry before any prompt or delete. They refuse when the marker names another --state-prefix that holds the stack, because that record may name the same resources.

Decide which record you are keeping, and drop the other with the command the refusal prints. cdkd state orphan never deletes a resource:

cdkd state orphan MyStack --stack-region us-east-1 --state-prefix team-a

Then run the destroy again.

A successful destroy removes the marker with the record. A resource the destroy keeps (RemovalPolicy.RETAIN) is listed in retained.json beside the record's key. The next deploy starts a new lifetime of the stack, whose generated names differ, so it creates new resources beside the kept ones (as CloudFormation does) and warns once, naming cdkd import for adopting them. cdkd state orphan empties that list.

If S3 denies access to the marker, the destroy warns and falls back to listing the bucket's prefixes.

Options

Flag Default Description
[stacks...] — Stack name(s) to destroy: CDK display paths with * / **, or exact physical names.
--stack <name> — A single stack name, as an alternative to the positional argument.
--all off Destroy every top-level stack in the CDK app (Stage stacks: '**').
-y, --yes off Answer the confirmation prompts automatically.
-f, --force off Same as --yes on cdkd destroy.
--remove-protection off Turn deletion protection off before each delete.
--skip-final-snapshot off Delete DeletionPolicy: Snapshot resources without the final snapshot (data loss).
--purge-events off After a clean destroy, also delete the stack's deployment-event history.
--allow-unsupported-types <types> — Comma-separated resource types to attempt through Cloud Control although cdkd reports them unsupported.
--resource-warn-after <duration> 5m Warn when one resource delete runs long. Repeatable, and accepts TYPE=DURATION.
--resource-timeout <duration> 30m Abort one resource delete that runs long. Repeatable, and accepts TYPE=DURATION.
-a, --app <command> cdk.json / CDKD_APP CDK app command, or a pre-synthesized cloud assembly directory.
--output <path> cdk.out Synthesis output directory.
--state-bucket <bucket> CDKD_STATE_BUCKET / cdk.json S3 bucket holding the state records.
--state-prefix <prefix> cdkd S3 key prefix for state files.
--profile <profile> — AWS profile.
--role-arn <arn> CDKD_ROLE_ARN IAM role to assume for AWS API calls.
-c, --context <key=value...> — Context values, repeatable.
--verbose off Verbose logging.

The two --resource-* flags share their syntax with cdkd deploy; see per-resource timeout.

Stack selection

You name stacks the same way as in every other command. The full pattern syntax is in stack selection.

You pass It selects
Nothing, in an app with a single stack That stack
MyStage/Api The stack at that CDK display path
MyStage-Api The stack with that exact physical CloudFormation name
'MyStage/*' Every stack one level under MyStage
'**' Every stack in the app, Stage stacks included
--all Every top-level stack, as cdk destroy --all does

When you select more than one stack, cdkd destroys a stack that reads another stack's outputs before the stack it reads from.

--all does not reach into a CDK Stage. It leaves the stacks inside a Stage running and names them in one warning line. If every stack of the app is inside a Stage, --all is refused; use '**'.

A name that matches nothing is reported with a warning, as in the AWS CDK CLI.

Display paths and wildcards need the CDK app to synthesize. When it does not, see Destroying without a working CDK app.

Nested stacks

A nested stack cannot be destroyed by name. Destroy its parent, which deletes the nested stack with it.

cdkd destroy '<child>' is refused because the parent would still list the nested stack as one of its resources. The parent's next deploy would then try to create the nested stack's resources again.

If you do want to remove only the child and leave the parent pointing at nothing, use cdkd state destroy '<child>'.

Edge cases

  • A name that belongs to another app. A stack that is in the state bucket but is not a stack of this app (a nested stack, or another app's stack that shares the bucket) gets its own warning and is skipped. This applies when you pass it beside names that did match.

Confirmation

cdkd destroy lists the resources and asks before it deletes anything. The default answer is yes (Y/n). --yes / -y and --force / -f skip the prompt.

cdkd destroy MyStack --yes   # CI

Without a terminal (a piped, redirected or CI run) cdkd cannot ask, so it refuses with NON_INTERACTIVE_CONFIRM and exit 1 unless you passed --yes. Piping y into the command does not work.

cdkd destroy: confirmation prompts covers the wording of the prompt, what a refused run has and has not done, and the other commands that follow the same rule. No refused prompt leaves a partial change or a held lock behind.

--remove-protection: bypass deletion protection on destroy

Without this flag, cdkd sends the delete as it is and AWS rejects it for a resource that has deletion protection on. With the flag, cdkd turns the protection off on each resource and then deletes it. You do not need to edit the CDK code, redeploy, or use the console.

cdkd destroy MyStack --remove-protection
cdkd destroy --all --remove-protection -y
cdkd state destroy MyStack --remove-protection -y   # without the CDK app

Under this flag the prompt says how many resources will lose their protection, and its default answer changes to no (y/N).

The flag turns off two kinds of protection:

  • terminationProtection on the stack. cdkd logs a warning that names the stack and continues. The protection counts when the CDK code sets it, and when the stack was last DEPLOYED with it: as in CloudFormation, code that dropped the setting protects nothing until it is redeployed. Each deploy records the setting in the stack's state, which is how cdkd state destroy, with no CDK app, refuses a protected stack too.
  • Deletion protection on a resource of one of the types below.
Resource type Protection field Call that turns it off
AWS::Logs::LogGroup DeletionProtectionEnabled PutLogGroupDeletionProtection
AWS::RDS::DBInstance DeletionProtection ModifyDBInstance with ApplyImmediately
AWS::RDS::DBCluster DeletionProtection ModifyDBCluster with ApplyImmediately
AWS::DocDB::DBCluster DeletionProtection ModifyDBCluster with ApplyImmediately (DocDB)
AWS::Neptune::DBCluster DeletionProtection ModifyDBCluster with ApplyImmediately (Neptune)
AWS::Neptune::DBInstance DeletionProtection ModifyDBInstance with ApplyImmediately (Neptune)
AWS::DynamoDB::Table DeletionProtectionEnabled UpdateTable, then a wait until ACTIVE
AWS::DynamoDB::GlobalTable DeletionProtectionEnabled UpdateTable, then a wait until ACTIVE
AWS::EC2::Instance DisableApiTermination ModifyInstanceAttribute
AWS::ElasticLoadBalancingV2::LoadBalancer attribute deletion_protection.enabled ModifyLoadBalancerAttributes
AWS::Cognito::UserPool DeletionProtection (ACTIVE / INACTIVE) UpdateUserPool
AWS::AutoScaling::AutoScalingGroup DeletionProtection UpdateAutoScalingGroup
AWS::EMR::Cluster Instances.TerminationProtected SetTerminationProtection
AWS::DSQL::Cluster DeletionProtectionEnabled Cloud Control UpdateResource
AWS::NeptuneGraph::Graph DeletionProtection Cloud Control UpdateResource
AWS::SMSVOICE::ProtectConfiguration DeletionProtectionEnabled Cloud Control UpdateResource
AWS::VerifiedPermissions::PolicyStore DeletionProtection ({Mode: ENABLED|DISABLED}) Cloud Control UpdateResource
AWS::EKS::Cluster DeletionProtection Cloud Control UpdateResource
AWS::RDS::GlobalCluster DeletionProtection Cloud Control UpdateResource
AWS::DocDB::GlobalCluster DeletionProtection Cloud Control UpdateResource

One flag covers every type in the table. Protection that is not in the table, such as a CloudFront distribution or S3 bucket retention, is left on. The table is limited to the cases where AWS offers a synchronous call that turns the protection off.

Two rows do more than the table says:

  • An Auto Scaling group also has DisableApiTermination turned off on each instance it launched. cdkd then deletes the group with ForceDelete=true, so AWS terminates its running instances.
  • A Cognito user pool needs its other settings sent back with the change. See Cognito user pools.

A DocDB instance has no protection field, so only the DocDB cluster is listed.

cdkd destroy: deletion protection covers what happens when turning the protection off fails, how cdkd puts the protection back after a failed delete, and the commands to put it back by hand.

--purge-events: also delete deployment-event history on destroy

A destroy removes state.json and lock.json but keeps the stack's deployment-event history, the log of what each deploy and destroy did. The history lives under the stack's deployments/ prefix in the state bucket, so the bucket is not empty after a teardown. --purge-events deletes the history too:

cdkd destroy MyStack --purge-events -y

The purge runs only after a clean, uninterrupted destroy of that stack. A failed or interrupted destroy keeps its events, because you need them for the retry. With several stacks, each cleanly destroyed stack is purged on its own.

cdkd state destroy does not take this flag. Use cdkd events prune '<stack>' --all instead, which also works for a stack that is already destroyed.

Edge cases

  • The purge fails. cdkd logs a warning. The destroy already succeeded and still exits 0.
  • Ctrlc after the stack was fully destroyed. The purge still runs, because nothing is left to retry and the run reports success.
  • A versioned state bucket. The purge also deletes the earlier versions under the stack's deployments/ prefix, including streams an earlier delete left behind a delete marker. That needs s3:ListBucketVersions and s3:DeleteObjectVersion. Without them the destroy still succeeds and a warning prints first. What the purge does not reach is listed in Earlier versions on the versioned state bucket.

Exit codes

Code Meaning
0 Everything was destroyed.
1 Hard error, including a confirmation prompt refused on a non-interactive stdin.
2 Partial: resources failed or were skipped, or the run was interrupted with work left. state.json is preserved; re-run to finish.
130 Force-quit by Ctrlc. A stack lock may be left behind. See Interrupting a destroy.

The full cross-command table is in the CLI Reference.

In this section

Last updated: