TerraformPilot

DevOps

Fix Terraform Error: lifecycle prevent_destroy Blocks Destroy

Fix terraform prevent_destroy errors blocking resource deletion. Temporarily disable, use state rm to decouple

LLuca Berton1 min read

Quick Answer

#
# Temporarily set to false
lifecycle {
  prevent_destroy = false  # Was true
}
terraform apply   # Applies the lifecycle change
terraform destroy -target=aws_db_instance.main

Then re-enable prevent_destroy = true.

The Error

#
Error: Instance cannot be destroyed
 
  on rds.tf line 5:
   5: resource "aws_db_instance" "main" {
 
Resource aws_db_instance.main has lifecycle.prevent_destroy set, but the
plan calls for this resource to be destroyed. To avoid this error and
continue with the plan, either disable lifecycle.prevent_destroy or
reduce the scope of the change.

What Causes This

#

prevent_destroy = true in the lifecycle block intentionally blocks any operation that would destroy the resource. This triggers when:

  1. Running terraform destroy — directly deleting protected resources
  2. Changing a force-replacement attribute — e.g., changing RDS engine forces replacement (destroy + create)
  3. Removing the resource from config — deleting the resource block from .tf files
  4. Renaming the resourceaws_instance.webaws_instance.app without moved block

Solution 1: Temporarily Disable

#
resource "aws_db_instance" "main" {
  # ...
 
  lifecycle {
    prevent_destroy = false  # Temporarily disabled
  }
}
terraform apply            # Apply the lifecycle change
terraform destroy -target=aws_db_instance.main  # Now works

Remember to re-enable prevent_destroy = true for other environments!

Solution 2: Remove from State (Keep the Resource)

#

If you want to stop managing the resource without destroying it:

# Removes from Terraform state — the actual resource stays in AWS
terraform state rm aws_db_instance.main

Then remove the resource block from your .tf files. The database continues running, just no longer managed by Terraform.

Solution 3: Use moved Block for Renames

#
# This causes destroy + create (blocked by prevent_destroy)
# resource "aws_instance" "app" { ... }  # renamed from "web"
 
# Instead, use moved block (no destroy):
moved {
  from = aws_instance.web
  to   = aws_instance.app
}
 
resource "aws_instance" "app" {
  # ...
  lifecycle {
    prevent_destroy = true
  }
}

Solution 4: Reduce Scope of Change

#

If a force-replacement attribute triggers the error:

# ❌ This forces replacement (destroy + create) → blocked
resource "aws_db_instance" "main" {
  engine = "mysql"      # Was "postgres" — can't change in-place
  lifecycle { prevent_destroy = true }
}
 
# ✅ Create new instance with different name, migrate data, then remove old
resource "aws_db_instance" "main_v2" {
  engine = "mysql"
}

When to Use prevent_destroy

#

Use it on resources where accidental deletion is catastrophic:

# Databases — data loss
resource "aws_db_instance" "production" {
  lifecycle { prevent_destroy = true }
}
 
resource "aws_rds_cluster" "production" {
  lifecycle { prevent_destroy = true }
}
 
# S3 buckets with important data
resource "aws_s3_bucket" "backups" {
  lifecycle { prevent_destroy = true }
}
 
# DNS zones
resource "aws_route53_zone" "primary" {
  lifecycle { prevent_destroy = true }
}
 
# KMS keys (can't be recreated with same ID)
resource "aws_kms_key" "main" {
  lifecycle { prevent_destroy = true }
}

Don't use it on:

  • EC2 instances (replaceable)
  • Security groups (no data loss)
  • IAM roles (recreatable)

Combining with AWS Protection

#
resource "aws_db_instance" "main" {
  deletion_protection = true   # AWS-level protection
 
  lifecycle {
    prevent_destroy = true     # Terraform-level protection
  }
}

Double protection: Terraform won't plan the destroy, AND AWS won't execute it even if Terraform tried.

Hands-On Courses

#

Conclusion

#

prevent_destroy = true is working as intended — it's protecting your resource. Temporarily set it to false to destroy, use terraform state rm to decouple without destroying, or use moved blocks for renames. Apply it to databases, S3 buckets, and DNS zones — anything where data loss would be catastrophic.

#Terraform#Troubleshooting#DevOps#Error Fix#Infrastructure as Code

Share this article