Skip to content
cdkd

Stop on Destructive Changes

Most changes to a CDK app are safe to deploy without a second look. The ones that are not are the changes that lose an existing resource: a property change that forces a replacement, a refactor that renames a construct's logical ID, a resource dropped from the template. cdkd can stop on exactly those changes and let everything else through:

# CI check: exit 1 if a change would lose a resource
cdkd diff --all --fail-on=destructive

# deploy, but ask first if a change would lose one
cdkd deploy MyStack --require-approval=destructive

Both use the same rule for what counts as destructive, so a change the diff flags is the change the deploy asks about.

What counts as destructive

A change is destructive when it replaces, deletes or orphans a resource that already exists. Additions and in-place updates are not.

Impact When
will be replaced A create-only property changes, or the resource's Type changes.
may be replaced A create-only property reads a value only the deploy can resolve.
will be destroyed The resource leaves the template.
will be orphaned The resource leaves the template with DeletionPolicy: Retain or RetainExceptOnCreate.

Both commands list each destructive change by stack, resource type, construct path and logical ID:

MyStack: AWS::S3::Bucket MyBucket MyBucketF68F3FF0 will be replaced
MyStack: AWS::DynamoDB::Table MyTable MyTable794EDED1 will be orphaned

Nested stacks are always checked. AWS::CDK::Metadata is never reported.

Fail a CI check: cdkd diff --fail-on=destructive

Run the diff as a pull-request check. It prints the full diff, lists the destructive changes, and exits 1 if there is at least one:

$ cdkd diff --all --fail-on=destructive
...
❌  Found 2 destructive change(s) (--fail-on=destructive):
  MyStack: AWS::S3::Bucket MyBucket MyBucketF68F3FF0 will be replaced
  MyStack: AWS::DynamoDB::Table MyTable MyTable794EDED1 will be orphaned

When cdkd deploy would refuse to start, the diff exits 3 instead and lists the refusal, not the destructive changes, so treat any non-zero exit as a failed check. Exit 1 also means the command itself failed, for example on an authentication error; a failed command prints an error instead of the report. cdkd diff lists every exit code.

Use --fail-on=any-change instead to fail on every difference.

Ask before deploying: cdkd deploy --require-approval=destructive

With this level, cdkd deploy stops before a stack with a destructive change, lists the changes, and asks:

Destructive changes:
  MyStack: AWS::DynamoDB::Table MyTable MyTable794EDED1 will be orphaned

Stack MyStack: 0 to create, 1 to update, 1 to delete.
Stack includes destructive updates and "--require-approval" is set to 'destructive'.
Do you wish to deploy these changes? (y/n)

Answering no fails that stack's deploy with nothing changed. A nested stack asks when its parent reaches it; declining fails the nested stack, and the parent rolls back. A stack with no destructive change deploys without a question.

To make it the default for an app, set it in cdk.json, where the AWS CDK CLI reads it too. The flag wins over the file:

{
  "app": "npx ts-node bin/app.ts",
  "requireApproval": "destructive"
}

Important

Without a terminal, as in CI, the deploy fails instead of asking. Pass --yes to approve every question, or gate the pipeline on cdkd diff --fail-on=destructive before the deploy step.

A --recreate-via-* target counts as a replacement, so it asks too. --dry-run never asks. The AWS CDK CLI's broadening level is not available, because cdkd does not compute a security diff; in cdk.json it is ignored with a warning.

Last updated: