# Terraform lifecycle: prevent_destroy, create_before_destroy, ignore_changes

> How to use the Terraform and OpenTofu lifecycle block: prevent_destroy, create_before_destroy, ignore_changes, replace_triggered_by and conditions.

- Source: https://www.itwonderlab.com/terraform-lifecycle-meta-argument/
- Published: 2026-09-16
- Updated: 2026-09-16
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## Control how Terraform creates, updates and destroys a resource

The `lifecycle` block is available in every `resource` and changes the default behavior of the plan. For a different approach to a related problem, see [avoiding instance destroy in AWS](https://www.itwonderlab.com/avoiding-instance-destroy-aws-with-terraform/).

### prevent_destroy

Makes any plan that would destroy the resource fail. Use it for databases, buckets with data and anything hard to recover:

```hcl title="prevent_destroy.tf"
resource "aws_db_instance" "main" {
  identifier = "ditwl-pro-db"
  # ...

  lifecycle {
    prevent_destroy = true
  }
}
```

It does not protect against removing the resource and its `lifecycle` block from the code at the same time, so it is a safety net and not a security control. Use [AWS deletion protection](https://www.itwonderlab.com/aws-rds/) too.

### create_before_destroy

When a change forces replacement, Terraform destroys first and then creates. With `create_before_destroy` the new object is created first, so there is no gap:

```hcl title="create_before_destroy.tf"
resource "aws_launch_template" "web" {
  name_prefix   = "web-"
  image_id      = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"

  lifecycle {
    create_before_destroy = true
  }
}
```

Both objects exist for a moment, so unique names must not collide. Use `name_prefix` instead of `name`, as in the example. It is common in [auto scaling groups and load balancers](https://www.itwonderlab.com/aws-terraform-tutorial-aws-auto-scaling/).

### ignore_changes

Tells Terraform not to react to differences in some attributes. It is useful when another system changes them, for example autoscaling changing `desired_capacity`:

```hcl title="ignore_changes.tf"
resource "aws_autoscaling_group" "web" {
  # ...
  desired_capacity = 2

  lifecycle {
    ignore_changes = [desired_capacity]
  }
}
```

Use `ignore_changes = all` only as a last resort, because Terraform will stop managing the resource.

### replace_triggered_by

Forces the replacement of a resource when another resource or attribute changes:

```hcl title="replace_triggered_by.tf"
resource "aws_instance" "app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"

  lifecycle {
    replace_triggered_by = [aws_security_group.app.id]
  }
}
```

### precondition and postcondition

Custom checks that fail the plan or apply with your own message:

```hcl title="conditions.tf"
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
  }
}

resource "aws_instance" "app" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type

  lifecycle {
    precondition {
      condition     = data.aws_ami.ubuntu.architecture == "x86_64"
      error_message = "The AMI must be x86_64."
    }

    postcondition {
      condition     = self.public_ip != ""
      error_message = "The instance must have a public IP."
    }
  }
}
```

A `precondition` is evaluated before creating the resource and a `postcondition` after, with `self` pointing to the result.

### Summary

| Argument | Use it for |
|---|---|
| `prevent_destroy` | Protect critical resources from accidental destruction |
| `create_before_destroy` | Replace without downtime |
| `ignore_changes` | Attributes changed outside of Terraform |
| `replace_triggered_by` | Replace a resource when a dependency changes |
| `precondition` / `postcondition` | Validate assumptions with clear errors |

`lifecycle` arguments must be literal values: they cannot use variables or expressions. Related: [import, moved and removed](https://www.itwonderlab.com/terraform-import-moved-removed/).
