Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 8 min read

Terraform Error: 'Instance cannot be destroyed' Due to lifecycle prevent_destroy

Quick answer

Fix Terraform's 'Instance cannot be destroyed' error from lifecycle prevent_destroy: understand the guard, safely remove or override it, and handle destroy/replace and terraform destroy.

  • #terraform
  • #iac
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Exact Error Message


│ Error: Instance cannot be destroyed

│   on database.tf line 1:
│    1: resource "aws_db_instance" "primary" {

│ Resource aws_db_instance.primary has lifecycle.prevent_destroy set, but the
│ plan calls for this resource to be destroyed. To avoid this error and continue
│ with the plan, either disable lifecycle.prevent_destroy or reduce the scope of
│ the plan using the -target option.

What It Means

The lifecycle { prevent_destroy = true } meta-argument is a safety guard. When it is set on a resource, Terraform refuses to produce any plan that would destroy that object — whether you ran terraform destroy, or a normal apply where a change forces replacement (destroy-then-create).

This error is Terraform working as intended: it caught a plan that would delete a resource you explicitly protected. It is common on stateful, hard-to-recreate infrastructure — databases, S3 buckets holding data, KMS keys — where an accidental delete would be catastrophic. Resolving it means deciding whether the destroy is genuinely intended, and if so, deliberately lifting the guard.

Common Causes

  • You ran terraform destroy (or terraform apply -destroy) against a stack containing a protected resource.
  • A configuration change forces replacement of the resource (a change to an immutable, ForceNew attribute triggers destroy-then-create).
  • A terraform state rm followed by re-import went wrong and the plan now wants to delete.
  • A module was removed or renamed without a moved block, so Terraform plans to destroy the old address.
  • The guard was left on from a previous project phase and now blocks an intended teardown.

Diagnostic Commands

See exactly what the plan wants to do and why the destroy/replacement is proposed:

terraform plan

Find every resource that carries the guard:

grep -R "prevent_destroy" --include="*.tf" .

Inspect the attribute change forcing a replacement (look for # forces replacement):

terraform plan -no-color | grep -B2 "forces replacement"

Confirm the resource’s current state entry:

terraform state show aws_db_instance.primary

Step-by-Step Resolution

First decide: do you actually want this resource destroyed? If not, the guard did its job — change your configuration so the plan no longer proposes a destroy (for example, revert the ForceNew attribute change).

If the destroy is genuinely intended:

  1. Locate the lifecycle block on the resource:
resource "aws_db_instance" "primary" {
  # ...
  lifecycle {
    prevent_destroy = true
  }
}
  1. Set the guard to false (or remove the block) as a deliberate, reviewable change:
  lifecycle {
    prevent_destroy = false
  }
  1. Re-plan to confirm the destroy is now allowed and is the only change you expect:
terraform plan
  1. Apply or destroy as intended:
terraform apply
# or, to tear the whole stack down
terraform destroy
  1. If the destroy is caused by an unintended replacement, do not remove the guard — fix the trigger instead. Revert the immutable attribute, or if the change is intentional and safe, plan it explicitly. To carve just the intended change out of a large plan, scope it:
terraform plan -target=aws_security_group.web
  1. If the resource was removed from config by accident, restore it or add a moved/removed block so Terraform stops planning a destroy. To drop it from management without deleting the real object:
removed {
  from = aws_db_instance.primary
  lifecycle {
    destroy = false
  }
}

Note that prevent_destroy must be a literal boolean — it cannot reference a variable — so toggling it is always a code change. If you manage many protected resources and want a consistent, reviewable pattern for lifting guards safely, the Terraform lifecycle prompts in the prompt library can generate the change set and a checklist.

Prevention

  • Keep prevent_destroy = true on stateful, data-bearing resources (databases, buckets, KMS keys) by default.
  • Treat any change that lifts the guard as a high-scrutiny pull request requiring review.
  • Watch plans for # forces replacement so you catch unintended destroy-then-create before it hits the guard.
  • Use moved/removed blocks when refactoring so renamed resources are not planned for destruction.
  • Take a backup/snapshot immediately before intentionally destroying a protected resource.
  • Do not templatize prevent_destroy; it must be a literal, so keep it explicit and per-resource.
  • ... has lifecycle.create_before_destroy set interactions — replacement ordering issues, related to lifecycle.
  • Error: Instance cannot be destroyed on a module resource — same guard, addressed inside the module.
  • Resource ... has been deleted outside of Terraform — the object is already gone, not a guard block.
  • -target “Resource targeting is in effect” warning — expected when you scope a plan to sidestep an unrelated replacement.

Frequently Asked Questions

Can I override prevent_destroy on the command line? No — there is no flag to bypass it; you must edit the configuration to set it false (or remove the block) and re-plan, which keeps the override auditable.

Why does a normal apply hit this and not just terraform destroy? A change to an immutable attribute forces replacement, which is a destroy-then-create; the guard blocks the destroy half, so even a routine apply can trip it.

Can I set prevent_destroy from a variable? No — it must be a literal boolean known at plan time, so it cannot reference variables or expressions; toggling it is always a code change.

How do I stop managing a protected resource without deleting it? Add a removed block with lifecycle { destroy = false } (or use terraform state rm) so Terraform forgets the resource while leaving the real object intact. For more lifecycle fixes, see the Terraform guides.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.