terraform destroy
Learn terraform destroy, safely tearing down infrastructure, using -target for partial destroys, understanding the destroy plan, and when to use destroy vs decommission. Includes terraform 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?