Terraform Lifecycle Rules Controlling Resource Behavior
By default, Terraform follows a straightforward update strategy: if a change requires replacing a resource, destroy the old one and create a new one. For many resources this is acceptable. For critical production infrastructure — databases, load balancers, TLS certificates — this default behaviour is dangerous. Lifecycle rules give you precise control over how resources are created, updated, and destroyed.
What Is the lifecycle Block
Every resource block in Terraform supports an optional lifecycle meta-argument. Inside it, you configure rules that override Terraform's default behaviour for that specific resource.
resource "RESOURCE_TYPE" "NAME" {
# ... arguments
lifecycle {
# lifecycle rules go here
}
}
Terraform provides four lifecycle rules: create_before_destroy, prevent_destroy, ignore_changes, and replace_triggered_by.
Rule 1: create_before_destroy
By default, when a resource must be replaced (destroy + create), Terraform destroys first and then creates. This means downtime between the two operations. Setting create_before_destroy = true reverses the order — the new resource is ready before the old one disappears.
Diagram: Default vs create_before_destroy
Default behaviour: [Server v1 running] --DESTROY--> [gap: no server] --CREATE--> [Server v2 running] ← downtime here → create_before_destroy = true: [Server v1 running] --CREATE new--> [Server v1 + v2 both running] --DESTROY v1--> [Server v2 running] ← zero downtime →
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.medium"
lifecycle {
create_before_destroy = true
}
}
This is essential for resources behind a load balancer — the new instance registers with the load balancer before the old one is removed, preventing dropped requests.
Rule 2: prevent_destroy
This rule protects a resource from accidental deletion. When set to true, Terraform refuses any plan that would destroy this resource and prints an error instead.
resource "aws_db_instance" "production_db" {
identifier = "prod-postgres"
engine = "postgres"
instance_class = "db.t3.medium"
# ...
lifecycle {
prevent_destroy = true
}
}
If someone runs terraform destroy or makes a change that forces replacement, Terraform outputs:
Error: Instance cannot be destroyed
on main.tf line 5, in resource "aws_db_instance" "production_db":
5: resource "aws_db_instance" "production_db" {
This resource has lifecycle.prevent_destroy set to true.
To allow this object to be destroyed, remove or update lifecycle.prevent_destroy.
Apply this rule to databases, certificate authorities, and any resource whose loss would cause serious data loss or service disruption.
Rule 3: ignore_changes
Sometimes an attribute changes outside of Terraform — an auto-scaling group changes the desired count, a team member updates a tag in the console, or a tool modifies a configuration property. Without ignore_changes, Terraform sees these as drift and tries to revert them on the next apply.
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.micro"
tags = {
Name = "app-server"
# The "LastDeployedBy" tag gets updated manually after deploys
}
lifecycle {
ignore_changes = [
tags["LastDeployedBy"],
user_data
]
}
}
Terraform ignores any external changes to the listed attributes. This does not remove those attributes from the real resource — it simply stops Terraform from touching them on future applies.
Ignoring All Changes to an Attribute Set
lifecycle {
ignore_changes = all
}
Use all sparingly. It tells Terraform to ignore every attribute change on the resource. This is useful for resources managed by an external process where Terraform should only handle initial creation.
Rule 4: replace_triggered_by
This rule forces a resource to be replaced whenever a referenced resource or attribute changes — even if the resource's own arguments did not change.
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = var.ami_id
instance_type = "t3.micro"
}
resource "aws_autoscaling_group" "app" {
# ...
lifecycle {
replace_triggered_by = [
aws_launch_template.app.image_id
]
}
}
When the AMI ID in the launch template changes, the auto-scaling group is automatically replaced — cycling in new instances with the new AMI.
Combining Lifecycle Rules
You can combine multiple rules in a single lifecycle block.
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "mysql"
instance_class = "db.t3.large"
lifecycle {
create_before_destroy = true
prevent_destroy = true
ignore_changes = [password]
}
}
Key Points
- Lifecycle rules override Terraform's default resource management behaviour for a specific resource.
create_before_destroy = trueeliminates downtime by creating the replacement before deleting the original.prevent_destroy = trueblocks accidental deletion — use it on databases and other critical resources.ignore_changestells Terraform to stop tracking changes to specific attributes managed externally.replace_triggered_byforces resource replacement when a referenced resource or attribute changes.
