Upcoming IaCConf: Building at the Intersection of AI and IaC 🤖

Register Now ➡️

Terraform prevent_destroy: How to Safeguard Resources

terraform

Terraform prevent_destroy protects essential resources from accidental deletion and is widely used as a safeguard in production environments.

In this guide, you will learn what prevent_destroy does, how it behaves inside the lifecycle block, and when to use it in real projects.

What is Terraform prevent_destroy?

Terraform prevent_destroy is a lifecycle meta-argument that you add to a resource to stop Terraform from deleting that resource while the resource and its lifecycle block remain in your configuration. When prevent_destroy = true is set, Terraform will fail the plan or apply if a change requires destroying that resource or if someone runs terraform destroy as long as that lifecycle rule is still present.

It acts as a safety lock for critical infrastructure such as production databases, logging buckets or core networking components.

Here is the basic form:

resource "aws_s3_bucket" "example" {
  bucket = "important-bucket"

  lifecycle {
    prevent_destroy = true
  }
}

If Terraform attempts to destroy this bucket, it stops with an error instead of proceeding. To allow deletion, you must intentionally edit the configuration and remove or change the lifecycle rule or delete the resource block itself, which creates a conscious review step.

Now let’s see some examples.

Example 1: Protect a production database with prevent_destroy

A common use of prevent_destroy is to protect a production database. In this scenario, you have an aws_db_instance that holds critical data, and you never want it destroyed by accident. 

Setting prevent_destroy = true forces Terraform to fail any plan that would destroy or replace the instance, even when a change would normally require recreation.

resource "aws_db_instance" "prod_db" {
  identifier        = "prod-db"
  engine            = "postgres"
  instance_class    = "db.m6g.large"
  allocated_storage = 200
  username          = "app_user"
  password          = "super-secret-password"
  skip_final_snapshot = false

  lifecycle {
    prevent_destroy = true
  }
}

In a real setup, you would keep the password in a secret manager or as a sensitive variable, not in plain text in the code.

With this configuration, Terraform prevent_destroy acts as a safety net around the database. If a change would cause a destroy step the plan will fail and show an error about the lifecycle rule. 

To proceed, you must consciously edit the config to remove or change the lifecycle block. That extra step helps protect real production data and keeps your infrastructure as code workflow safer.

Example 2: Lock an S3 log bucket so it cannot be destroyed

Another useful pattern is protecting an S3 bucket that stores logs or audit data. You may recreate application stacks frequently, while the central logging bucket should never disappear. 

Adding prevent_destroy ensures Terraform will not clean it up, even during large refactors.

resource "aws_s3_bucket" "logs" {
  bucket = "my-company-central-logs"

  tags = {
    Environment = "prod"
    Purpose     = "central-logs"
  }

  lifecycle {
    prevent_destroy = true
  }
}

Example 3: Use a short-lived branch to relax prevent_destroy during migrations

Sometimes you want prevent_destroy enabled most of the time, but still need a clear way to perform a rare destructive change during a maintenance window.

Lifecycle meta-arguments must be statically defined in configuration. They can’t use variables, expressions, or functions, so you can’t toggle prevent_destroy with a boolean flag at plan time and Terraform will reject that during validation/planning.

Instead, keep prevent_destroy in your mainline code and temporarily relax it through a short-lived Git branch when you need to perform a controlled replacement.

resource "aws_instance" "critical_app" {
  ami                    = "ami-1234567890abcdef0"
  instance_type          = "t3.large"
  subnet_id              = aws_subnet.app_subnet.id
  vpc_security_group_ids = [aws_security_group.app_sg.id]

  lifecycle {
    prevent_destroy = true
  }
}

In normal operation, this instance is protected by prevent_destroy. When you need to replace it in a controlled way, you can:

  1. Create a temporary branch in version control.
  2. In that branch, remove the prevent_destroy lifecycle block (or change it to prevent_destroy = false) for this resource.
  3. Run terraform plan and terraform apply during your planned maintenance window, ideally with peer review and approvals.
  4. After the change is complete, restore prevent_destroy = true on your main branch and merge it back.

This workflow keeps your critical resource safe in day-to-day use, and still gives you a simple, auditable path for rare destructive updates without trying to drive lifecycle settings with variables or other dynamic expressions.

Terraform prevent_destroy limitations

Terraform prevent_destroy is a useful safety net, but it has a few important limitations that you should keep in mind.

  • It only affects Terraform operations – prevent_destroy stops Terraform from planning or applying a destroy step for a protected resource. It does not lock the resource in your cloud provider. Someone with access to the AWS, GCP or Azure console can still delete it there. It is not a backup or data recovery feature.
  • It only works on resource blocks – Lifecycle blocks apply to resources. You cannot use prevent_destroy on modules or data sources. If you want to protect resources that come from a module, you need to define lifecycle settings inside that module or in the resources you manage directly.
  • It only protects resources that are still in the configuration – Terraform evaluates prevent_destroy based on what is in your code. If you remove the entire resource block from your configuration, Terraform no longer sees the lifecycle rule and can plan to destroy the resource on the next apply.
  • It can be bypassed by direct state changes – prevent_destroy relies on normal plan and apply workflows. If someone runs commands that change the state directly, such as terraform state rm, or edits the state file outside of Terraform, they can remove the resource from Terraform control and then delete or change it without hitting the lifecycle rule. This is why access to state should be limited and audited.

Key points

Terraform uses a meta argument called prevent_destroy in the lifecycle block to protect important resources from being destroyed by terraform destroy or by changes in configuration that would normally trigger recreation while the resource block with that lifecycle rule is present in your configuration. 

When prevent_destroy = true is set for a resource, any operation that would remove that resource will fail with an error and Terraform will stop the plan or apply as long as the lifecycle rule remains in the configuration.

Use it for production databases, shared logging buckets, or core networking components where accidental deletion would cause downtime or data loss. If you must proceed with a destructive change, remove or toggle the rule in version control so the decision is explicit and auditable.

Terraform is really powerful, but to achieve an end-to-end secure GitOps approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:

  • Policies (based on Open Policy Agent)
  • Multi-IaC workflows
  • Self-service infrastructure
  • Integrations with any third-party tools

If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.

Manage Terraform better and faster

If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.

Learn more

Terraform Commands Cheat Sheet

Grab our ultimate cheat sheet PDF
for all the Terraform commands
and concepts you need.

Share your data and download the cheat sheet