5.3 Destroy Terraform-Managed Infrastructure
Key Takeaways
- `terraform destroy` is a convenience alias for `terraform apply -destroy`. It builds a destroy plan and, after approval, applies it.
- Destroy only deprovisions objects **in state**. Click-ops resources that were never imported are left untouched.
- Deleting a resource block from configuration and running a normal apply also destroys that object. That is how you remove one resource from a long-lived stack.
- `terraform destroy -target=ADDRESS` is an exceptional, last-resort way to tear down a subset. It still follows the same last-resort targeting caveats as plan/apply.
- `lifecycle { prevent_destroy = true }` rejects destroy plans while that resource block remains. It is a trap: remove the block and apply, and Terraform can still destroy the object. Details belong with lifecycle (4f).
5.3 Destroy Terraform-Managed Infrastructure
Quick Answer:
terraform destroyis a convenience alias forterraform apply -destroy. After you approve the destroy plan, Terraform deprovisions every object in the current state and leaves an empty state. It does not hunt down click-ops resources that were never imported. Removing one resource from configuration and running a normal apply also destroys that object.prevent_destroycan block a destroy plan while the resource block remains; it is not a force field if you delete the block.
Objective 3f on Terraform Associate (004) is destroy Terraform-managed infrastructure. Terraform 1.12 still ships terraform destroy as the everyday command, and official docs describe it as an alias rather than a separate engine. HCP Terraform exposes the same idea as a destroy run on a workspace.
Destroy is apply in destroy mode
Official destroy docs:
terraform destroy [options]
is just a convenience for
terraform apply -destroy
Because of that alias, destroy accepts most of the options terraform apply accepts. Two official limits matter on the exam:
- Destroy does not accept a plan file argument.
- Destroy forces destroy planning mode. You are not running a normal create/update plan that happens to include a couple of deletes.
To preview a teardown without executing it, run the speculative form:
terraform plan -destroy
That is terraform plan in destroy mode. You get the same - symbols and a Plan: 0 to add, 0 to change, N to destroy. line, and infrastructure is unchanged until someone applies a destroy plan.
The approval prompt is the same family as apply: Terraform shows the destroy plan and waits. Only yes continues, unless you pass -auto-approve. HashiCorp's own copy is blunt: you typically do not want to destroy long-lived production objects; destroy is convenient for ephemeral development infrastructure you are finished with.
On Terraform versions before v0.15.2, terraform apply -destroy did not exist and you had to use terraform destroy. The exam product is 1.12, so both spellings are valid and they mean the same thing.
Only objects in state are destroyed
Destroy deprovisions objects managed by the configuration, which in practice means objects recorded in state. Terraform walks state, plans a destroy for each managed instance, and calls each provider's delete API.
| Object | In state? | terraform destroy |
|---|---|---|
aws_instance.web created by an earlier apply | Yes | Destroyed |
| IAM role created by a module you still call | Yes | Destroyed |
| Security group a teammate clicked together in the console and never imported | No | Left untouched |
VM you created with Terraform, then removed from state with terraform state rm | No | Left running |
Data source (data.aws_ami.ubuntu) | Not a managed object | Not destroyed; it was only a read |
This is the click-ops trap. A candidate who thinks "destroy empties the AWS account" fails 3f. Terraform is not a cloud janitor. If the object is not in this workspace's state, destroy does not know the id and does not call delete.
The same rule applies on HCP Terraform: a destroy run empties that workspace's state. Another workspace, another state file, or a console-only object is out of scope.
Destroy everything versus removing one resource and applying
Two legal ways to delete infrastructure look similar in the plan (both show -) and are not the same workflow.
1. terraform destroy (or terraform apply -destroy)
Goal: empty this state. Every managed resource is planned for destroy, including objects you still have resource blocks for. When the apply finishes, state is empty. The .tf files are still on disk; a later normal apply would create everything again.
2. Delete (or comment out) a resource block, then terraform plan / terraform apply
Goal: keep the rest of the stack. Official lifecycle docs state the default apply operations: Terraform destroys resources that exist in state but not in configuration. That is the everyday way to decommission one database, one DNS record, or one module call without torching the VPC.
| Intent | Command / edit | What the plan contains |
|---|---|---|
| Tear down the whole workspace | terraform destroy | Destroy all managed objects |
| Remove one object, keep the rest | Delete that resource/module block, then apply | Destroy only the removed address (plus dependents that cannot exist without it) |
| Preview teardown | terraform plan -destroy | Speculative full destroy |
| Exceptional single-address teardown | terraform destroy -target=ADDRESS | Destroy that address and the targeting graph HashiCorp documents |
Destroy does not delete your configuration files, providers, or the backend settings. It deletes remote objects and the state records that pointed at them.
-target destroy is still last resort
Official destroy page: you can use -target to destroy a particular resource. Example:
terraform destroy -target aws_instance.example
The same targeting caveats from the plan docs apply. Targeting selects the address and objects required by the targeting rules, not a clean "just this one VM and nothing it is attached to" guarantee in every graph. HashiCorp still labels targeting as exceptional. Prefer removing the resource from configuration (or splitting the configuration) over making -target destroy a habit.
prevent_destroy is a trap, not a vault door
You will study lifecycle rules in depth under objective 4f. For 3f you only need the destroy-shaped trap.
lifecycle { prevent_destroy = true } tells Terraform to reject plans that would destroy the infrastructure object associated with that resource and return an error. Official constraints:
- The argument must be present in the configuration. It is not a sticky bit Terraform stores independently in state (except as implied by still having the resource).
- It does not prevent Terraform from destroying a resource if you remove its configuration. Delete the block and apply, and the object is eligible for destroy.
- Once such objects exist,
prevent_destroyalso preventsterraform destroyfrom operating, because a full destroy plan would destroy them. - HashiCorp's advice: use it sparingly, as protection against accidental replacement of costly stateful objects (databases, storage). It also makes some configuration changes impossible to apply.
Exam pattern: "We set prevent_destroy, so the object can never be deleted." False. "We set prevent_destroy and then deleted the resource block; apply still tried to destroy it." True, and that surprise is the point of the trap. Removing from state without destroying the real object is a different operation (removed blocks / terraform state rm) and belongs to later state objectives.
Worked scenario
A developer workspace has a VPC, two instances, and a click-ops jump host someone launched from the Azure portal during a fire drill. The jump host was never imported. The developer finishes the spike and runs terraform destroy. After typing yes, Terraform destroys the VPC and both instances. The jump host keeps running and keeps billing. State is empty. The .tf files still describe the VPC.
A second story: production must retire aws_instance.canary only. An engineer who runs terraform destroy just scheduled an outage. The 3f-correct edit is to remove the aws_instance.canary block (or the module call), review a normal plan that shows 1 to destroy, and apply. If that instance has prevent_destroy = true, the destroy plan errors until someone removes the lifecycle rule or removes the whole block (which, remember, also drops the protection).
Exam traps for objective 3f
- Destroy is not a different product. It is
apply -destroy. - Destroy is not "delete the AWS account" or "delete every object in the subscription."
- Click-ops and forgotten imports survive destroy.
- Removing a block and applying is a destroy of that address.
terraform plan -destroyis speculative. Nothing is torn down until apply.prevent_destroyis not absolute. Details live with lifecycle later; do not over-claim it here.-targetdestroy is still last resort.
Official references: terraform destroy command and the prevent_destroy notes in lifecycle.
What is terraform destroy on Terraform 1.12?
A teammate created a security group in the cloud console. It was never imported. What does terraform destroy do to that group?
A database resource sets lifecycle { prevent_destroy = true }. Which statement is accurate for objective 3f?