What destroy does
terraform destroy is apply in reverse. It reads the state, sees every resource it once created, and deletes those resources through the cloud API. After destroy, the state is empty.
It is the equivalent of:
Plan: 0 to add, 0 to change, N to destroy.
where N is every resource Terraform manages.
When it helps
- Learning tasks. You experiment, tear it down, repeat. Nothing left behind.
- Ephemeral environments. You bring up a preview environment for a PR, run the tests, tear it down.
- End of a project. The client closed the project, so you run
destroyand leave no dangling resources. - Rolling back a failed experiment. You created the wrong thing, run
destroy, and start over.
What it looks like
terraform destroy
Terraform shows a plan (like plan, but with minuses):
Terraform will perform the following actions:
# aws_s3_bucket.demo will be destroyed
- resource "aws_s3_bucket" "demo" {- bucket = "my-bucket-12345" -> null
- tags = { ... } -> null- id = "my-bucket-12345" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure...
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value:
Notice the phrase There is no undo. It is literally true: there is no rollback. Delete an S3 bucket and the data is gone. Delete an RDS instance and the snapshots are gone too (unless you set skip_final_snapshot = false).
Type yes. Terraform starts deleting in reverse dependency order. If a bucket references a KMS key, the bucket goes first, then the key.
Selective removal
Sometimes you need to tear down a single resource rather than everything:
terraform destroy -target=aws_s3_bucket.demo
This is the equivalent of "remove only this block from HCL and run apply." You will rarely need it, but it can help, for example when a resource is stuck and recreating it is easier.
As an alternative, if you need to recreate a resource, prefer terraform apply -replace=aws_s3_bucket.demo. That means "tear it down and create it again in one apply."
Protection against accidental destroy: prevent_destroy
You can set a flag in the lifecycle block:
resource "aws_rds_instance" "prod_db" {# ... Parameters ...
lifecycle {prevent_destroy = true
}
}
Now terraform destroy (or any apply that tries to delete this resource) fails with an error. To actually tear it down, you first remove prevent_destroy = true, run apply, and only then run destroy.
This is production's best friend. See tf-resource-lifecycle.
Pitfalls
-
Destroy removes only what is in state. If the cloud holds resources created by hand (not Terraform), it leaves them alone.
-
Destroy does not delete the state file. After destroy,
terraform.tfstatestays, but it is empty ("resources": []). If you want a clean slate, delete the file by hand. -
Dependencies can block destroy. Say you have an S3 bucket with objects inside it. AWS will not let you delete the bucket without
force_destroy = truein HCL. This is an AWS protection, not a Terraform one, and Terraform fails with a provider error. -
destroy -targetis a crutch. Reaching for-targetregularly means your dependency graph is wrong. -
Run destroy in CI with care. A separate pipeline for destroy is better, available to only a few people, with a second confirmation.
-
Auto-approve is dangerous.
terraform destroy -auto-approvetears everything down without a second thought. In production, never.
Recovery after destroy
There is none. This is not git revert. If there was data, it is gone. So:
- Back up before destroy (an RDS snapshot, S3 versioning, an EBS snapshot).
- Put
prevent_destroy = trueon critical resources. - In production repositories, do not hand out destroy rights to just anyone.