The lifecycle Meta-Argument
prevent_destroy guards the irreplaceable, ignore_changes ends fights with drift, create_before_destroy avoids downtime.
The lifecycle block is how you tell Terraform "I know better than the default here." Used well, it prevents outages and disasters. Used carelessly, it hides real problems.
The three you will actually use
prevent_destroy— a hard stop on deletion. Put it on production databases and state buckets so a straydestroyerrors out instead of wiping them.ignore_changes— stop fighting a value that something else legitimately manages (autoscaling changingdesired_capacity, an external tool tagging resources).create_before_destroy— spin up the replacement before tearing down the original, so a replace does not cause a gap in service.
ignore_changes is a scalpel, not a bandage. Ignoring a whole resource to silence a noisy plan usually hides a real config problem.
Example
resource "aws_db_instance" "main" {
engine = "postgres"
instance_class = "db.t3.medium"
lifecycle {
prevent_destroy = true # refuse to delete this DB
ignore_changes = [password] # rotated outside Terraform
}
}
resource "aws_launch_template" "web" {
name_prefix = "web-"
lifecycle {
create_before_destroy = true # no gap during replacement
}
}When to use it
- A team adds prevent_destroy = true to the production RDS cluster so a misconfigured terraform destroy cannot remove the database even if someone runs it.
- An engineer adds ignore_changes = [ami] to an EC2 instance so AMI ID drift caused by an auto-patching system does not trigger a Terraform-initiated replacement.
- A zero-downtime deployment uses create_before_destroy = true on an ALB target group so the new group is ready and attached before the old one is deleted.
More examples
prevent_destroy on a database
Adds prevent_destroy so Terraform refuses any plan that includes destroying this database, even if the resource block is removed.
resource "aws_db_instance" "prod" {
identifier = "prod-db"
engine = "postgres"
instance_class = "db.m5.large"
allocated_storage = 100
username = "admin"
password = var.db_password
lifecycle {
prevent_destroy = true
}
}ignore_changes for drift tolerance
Tells Terraform to ignore changes to the AMI and a specific tag so drift from external systems does not generate noisy plan output.
resource "aws_instance" "app" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
lifecycle {
ignore_changes = [
ami, # auto-patching changes this
tags["LastScan"] # security scanner adds this tag
]
}
}create_before_destroy for zero downtime
Creates the new target group before deleting the old one so the load balancer always has a healthy target group attached during updates.
resource "aws_lb_target_group" "app" {
name = "app-tg-${var.version}"
port = 8080
protocol = "HTTP"
vpc_id = aws_vpc.main.id
lifecycle {
create_before_destroy = true
}
}
Discussion