🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Terraform Accidentally Destroyed Resources — How to Recover

You ran terraform apply and it deleted something it shouldn't have. Here's how to recover from accidental Terraform destroys before they become a disaster.

DevOpsBoysMay 29, 20264 min read
Share:Tweet

It happens to everyone. A terraform apply that was supposed to update a tag deleted the RDS instance. Or refactoring moved a resource and Terraform decided to destroy-and-recreate instead of update.

Here's how to recover — fast.


First: Stop the Bleeding

If the destroy is still in progress:

bash
# Ctrl+C immediately
# Terraform will finish the current operation but stop queuing new ones
# Don't run anything else until you understand what happened

If it already completed, check what was destroyed:

bash
# Check state for what's gone
terraform state list
 
# Check the plan that was applied
# (if you used -out flag, the plan file shows what was destroyed)

Case 1: RDS / Database Deleted

If you have automated backups enabled (you should):

bash
# AWS Console → RDS → Snapshots → Automated backups
# Or via CLI
aws rds describe-db-snapshots \
  --query 'DBSnapshots[?DBInstanceIdentifier==`your-db`]' \
  --output table
 
# Restore from snapshot
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier your-db-restored \
  --db-snapshot-identifier rds:your-db-2026-05-29-00-01

Then import into Terraform state:

bash
terraform import aws_db_instance.main your-db-restored

Case 2: EC2 / EBS Volume Deleted

EC2 instances can't be recovered once terminated (unless you have an AMI). But EBS volumes can if they weren't deleted with the instance:

bash
# Check if volume still exists (might be in "available" state)
aws ec2 describe-volumes \
  --filters "Name=status,Values=available" \
  --query 'Volumes[*].[VolumeId,Size,AvailabilityZone]'
 
# If found, create new instance and attach
aws ec2 attach-volume \
  --volume-id vol-xxxxxxxx \
  --instance-id i-xxxxxxxx \
  --device /dev/sdf

Case 3: S3 Bucket Deleted

If versioning was enabled:

bash
# Versioning protects objects even if bucket is deleted? No.
# Bucket deletion removes everything. 
 
# Check if bucket still shows in AWS (might be soft-deleted briefly)
aws s3 ls | grep your-bucket-name
 
# If using S3 replication, check destination bucket
aws s3 ls s3://your-replica-bucket/

Prevention: Enable S3 Object Lock or replicate to another region.


Case 4: Wrong Resource Destroyed (Resource Still Exists in AWS, Just Missing from State)

Most common scenario when refactoring. The resource exists in AWS but Terraform state doesn't know about it.

bash
# Re-import the existing resource into state
terraform import aws_instance.web i-1234567890abcdef0
terraform import aws_security_group.main sg-12345678
terraform import aws_s3_bucket.data my-bucket-name
 
# Verify
terraform plan
# Should show "No changes" if import was correct

Case 5: Terraform Destroy-and-Recreate During Rename

When you rename a resource in Terraform, it destroys old and creates new:

hcl
# Before
resource "aws_instance" "web_server" { ... }
 
# After (rename) — Terraform treats this as destroy old + create new!
resource "aws_instance" "application_server" { ... }

Fix with moved block (Terraform 1.1+):

hcl
moved {
  from = aws_instance.web_server
  to   = aws_instance.application_server
}

This tells Terraform it's the same resource, just renamed. No destroy.


How to Prevent This in the Future

1. Lifecycle protect on critical resources

hcl
resource "aws_db_instance" "production" {
  # ...
  
  lifecycle {
    prevent_destroy = true  # terraform destroy will error out
  }
}

2. Always run plan before apply

bash
# Save plan to file
terraform plan -out=tfplan
 
# Review it (look for any "-" destroy lines)
terraform show tfplan | grep -E "^  # |will be destroyed|must be replaced"
 
# Only apply if plan looks right
terraform apply tfplan

3. Use -target for surgical applies

bash
# Only apply changes to one resource
terraform apply -target=aws_instance.web

4. Enable state locking + versioning

hcl
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"  # state locking
  }
}

Enable versioning on the S3 bucket — you can roll back to a previous state:

bash
# List state versions
aws s3api list-object-versions --bucket my-terraform-state --prefix prod/terraform.tfstate
 
# Restore previous version
aws s3api get-object \
  --bucket my-terraform-state \
  --key prod/terraform.tfstate \
  --version-id <version-id> \
  terraform.tfstate.backup

5. Use Atlantis or Terraform Cloud for PR-based applies

Never apply from a local machine in production. All applies go through PR review with a plan posted as a comment.


Quick Recovery Checklist

  • Check AWS Console for the resource (might still exist)
  • Check automated backups / snapshots
  • Check S3 state bucket for previous state versions
  • Re-import if resource exists in AWS
  • Restore from backup if truly deleted
  • Add prevent_destroy to critical resources after recovery

Accidental destroys are painful the first time. Set up prevent_destroy on production databases, S3 buckets, and VPCs today — before it happens again.

Practice Terraform in safe sandboxes — KodeKloud Terraform labs let you break things without consequences.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments