terraform destroy
terraform destroy safely. Partial destroys with -target, reading the destroy plan, and when to decommission instead. Plus fmt and validate.
What Is terraform destroy?
terraform destroy removes all infrastructure resources managed by your Terraform configuration. It is the opposite of terraform apply.
terraform destroy
Terraform reads your configuration and state file, calculates a destroy plan (everything that exists gets removed), shows you the plan, and waits for approval before deleting anything.
Warning:
terraform destroyis permanent. Deleted cloud resources cannot be recovered unless you have backups (e.g., RDS snapshots, EBS snapshots). Always review the destroy plan carefully.
The Destroy Plan
Before destroying anything, Terraform shows you exactly what will be deleted:
Terraform will destroy all your managed infrastructure, as shown below.
These resources will be destroyed in the proper reverse order.
# aws_instance.web will be destroyed
- resource "aws_instance" "web" {
- ami = "ami-0c55b159cbfafe1f0" -> null
- instance_type = "t3.micro" -> null
- id = "i-0abc12345def67890" -> null
- public_ip = "54.123.45.67" -> null
}
# aws_security_group.web will be destroyed
- resource "aws_security_group" "web" { ... }
# aws_subnet.public will be destroyed
- resource "aws_subnet" "public" { ... }
# aws_vpc.main will be destroyed
- resource "aws_vpc" "main" { ... }
Plan: 0 to add, 0 to change, 4 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: yes
Notice the reverse dependency order: Terraform destroys in the reverse of the creation order. The EC2 instance is destroyed before the security group, which is destroyed before the subnet, which is destroyed before the VPC. This prevents dependency errors (e.g., you cannot delete a VPC that still has running instances).
When to Use terraform destroy
| Use Case | Appropriate? |
|---|---|
| Tearing down a dev/test environment | ✅ Perfect use case |
| Cleaning up after a tutorial or experiment | ✅ Perfect use case |
| Removing a feature branch environment | ✅ Standard practice |
| Decommissioning part of production | ⚠️ Use with extreme caution + backup strategy |
| "I'll just recreate it" in production | ❌ Almost always wrong |
terraform destroy is most commonly used for ephemeral environments, dev environments, feature branch previews, CI/CD testing environments, and tutorial cleanup.
Partial Destroy with -target
To destroy a single resource (or module) without touching the rest:
# Destroy a single resource
terraform destroy -target=aws_instance.web
# Destroy an entire module
terraform destroy -target=module.database
Terraform will show a destroy plan for only the targeted resource:
# aws_instance.web will be destroyed
- resource "aws_instance" "web" {
- id = "i-0abc12345def67890" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Caution:
-targetcan leave your state inconsistent. If other resources depended on the destroyed resource, they still exist in state but their dependency is gone. Always run a fullplanafter a targeted destroy to check for consistency.
Auto-Approve in CI/CD
Like apply, you can skip the confirmation with -auto-approve:
terraform destroy -auto-approve
This is commonly used in CI/CD pipelines to automatically tear down ephemeral environments:
# GitLab CI, destroy on branch delete
teardown:
stage: destroy
script:
- terraform init -input=false
- terraform destroy -input=false -auto-approve
when: manual
Preview the Destroy Plan Without Destroying
To see the destroy plan without any risk, use terraform plan -destroy:
terraform plan -destroy
This is functionally equivalent to terraform destroy without the actual deletion, great for reviewing what would be destroyed before committing.
The destroy Lifecycle Hook
If you want certain resources to be retained even when the configuration is removed, use the prevent_destroy lifecycle argument:
resource "aws_s3_bucket" "critical_data" {
bucket = "my-production-data"
lifecycle {
prevent_destroy = true
}
}
If you try to destroy this resource (or remove it from your config), Terraform will error:
╷
│ Error: Instance cannot be destroyed
│ Resource aws_s3_bucket.critical_data has lifecycle.prevent_destroy set to true.
│ To allow this object to be destroyed, remove the lifecycle block or set
│ prevent_destroy = false.
╵
Use prevent_destroy for critical resources like production databases, S3 buckets with important data, or any resource where accidental deletion would be catastrophic.
terraform fmt, Format Your Code
While you have your terminal open, terraform fmt is a command you will use constantly:
terraform fmt
It automatically formats all .tf files in the current directory to match the Terraform style guide, consistent indentation, aligned = signs, and proper spacing.
# Format and show which files were changed
terraform fmt -diff
# Format all files recursively (including modules)
terraform fmt -recursive
# Check formatting without making changes (useful in CI)
terraform fmt -check
Example, before formatting:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
After terraform fmt:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-server"
}
}
Pro tip: Add
terraform fmt -checkto your CI pipeline. If anyone submits unformatted code, the pipeline fails, this enforces consistent style across the team without manual code review comments.
terraform validate, Validate Your Configuration
terraform validate checks your configuration for syntax errors and internal consistency:
terraform validate
Successful output:
Success! The configuration is valid.
Error example:
╷
│ Error: Unsupported argument
│ on main.tf line 8
│ An argument named "instanse_type" is not expected here.
│ Did you mean "instance_type"?
╵
validate catches:
- Syntax errors (missing braces, typos)
- References to undeclared variables or resources
- Invalid attribute names for a resource type
- Type mismatches (e.g., passing a string where a number is expected)
What validate does NOT check:
- Whether the cloud credentials are valid
- Whether the AMI ID exists in your region
- Whether the VPC ID actually exists
- Anything that requires calling cloud APIs
# Typical workflow before pushing code:
terraform fmt -recursive
terraform validate
terraform plan
Knowledge Check
Question 1: Destroy Order
You have a VPC, a subnet inside the VPC, and an EC2 instance inside the subnet. When you run terraform destroy, in what order does Terraform destroy these resources?
Question 2: prevent_destroy
A developer accidentally removes the aws_rds_cluster.production resource block from the Terraform config. What happens when they run terraform apply?
Question 3: terraform fmt
Your CI/CD pipeline includes terraform fmt -check. A developer submits a merge request with an unformatted .tf file. What happens?
Frequently Asked Questions
What does terraform destroy actually delete?
Every resource recorded in the state file for that workspace and configuration. It does not touch resources created outside Terraform, and it does not touch other workspaces or other state files. The destroy plan is shown for approval first, and reading it is worth the time. The count of resources is the quickest sanity check that you are pointed at the environment you think you are.
How do I preview a destroy without actually destroying?
terraform plan -destroy produces the destroy plan without executing it, and -out saves it for review. This is the safe way to answer "what would go away", particularly before tearing down anything shared. In CI, generating a destroy plan and posting it for approval is the equivalent of the normal plan-then-apply flow.
How do I protect resources from accidental destruction?
Add lifecycle { prevent_destroy = true } to anything holding data. Terraform then refuses any plan that would destroy it, including a full terraform destroy, and you must remove the flag deliberately to proceed. Combine that with provider-level protection, RDS deletion protection, S3 bucket versioning and MFA delete, so a mistake has to get past more than one gate. Note prevent_destroy cannot stop a destroy triggered by the resource being removed from configuration entirely.
Can I destroy just one resource?
Yes, with terraform destroy -target=aws_instance.example, but treat it as a recovery tool rather than routine practice. Targeting operates on a subset, so the rest of your configuration is not reconciled and dependent resources may be left referencing something gone. A cleaner approach for permanent removal is to delete the resource from the configuration and run a normal apply, which Terraform plans with full dependency awareness.
What is the difference between terraform destroy and deleting resources in the console?
terraform destroy removes the resources and clears them from state, leaving everything consistent. Deleting in the console removes the resources but leaves state believing they exist, so the next plan shows Terraform intending to recreate them, which is drift, and at best confusing, at worst a surprise recreation of something expensive. If resources were removed outside Terraform, use terraform state rm to bring state back in line.
Should terraform destroy ever run automatically in CI?
Only for genuinely ephemeral environments. A per-pull-request preview stack that is torn down on merge, or a nightly cleanup of a sandbox account. Even then, scope it tightly with a separate state file and credentials that cannot reach production, so an error in the pipeline cannot destroy the wrong thing. For anything long-lived, destruction should require a human.