Terraform 22: The lifecycle block
easy⏱ 5 mincourseterraform
create_before_destroy
By default Terraform destroys then creates on a replacement — a gap of downtime. create_before_destroy = true flips the order: stand up the replacement first, then remove the old one. Essential for things that must never fully disappear, like a VM scale set or a public IP behind traffic.
resource "azurerm_public_ip" "ingress" {
name = "orders-ingress-pip"
resource_group_name = azurerm_resource_group.main.name
location = "westus2"
allocation_method = "Static"
lifecycle {
create_before_destroy = true # new IP is up before the old is freed
}
}
prevent_destroy & ignore_changes
prevent_destroy = true makes any plan that would delete the resource fail — a seatbelt for state stores, prod databases, key vaults. ignore_changes = [ ... ] tells Terraform to stop managing specific attributes after creation, so out-of-band edits (autoscaler-set instance counts, a rotated secret) don't show as drift every plan.
resource "azurerm_storage_account" "tfstate" {
name = "myorgtfstatedev"
resource_group_name = azurerm_resource_group.main.name
location = "westus2"
account_tier = "Standard"
account_replication_type = "LRS"
lifecycle {
prevent_destroy = true # never let a plan delete the state store
ignore_changes = [tags] # tags are managed elsewhere
}
}
resource "azurerm_linux_virtual_machine_scale_set" "agents" {
name = "ci-agents-vmss"
resource_group_name = azurerm_resource_group.main.name
location = "westus2"
sku = "Standard_D2s_v5"
instances = 2
lifecycle {
ignore_changes = [instances] # autoscaler owns the instance count
}
}
lifecycle values must be literal
The lifecycle block is read very early, so its arguments must be static literals — you cannot compute prevent_destroy from a variable or reference other resources. ignore_changes takes a list of attribute names (bare references like tags, instances), not strings. Reach for these only when a default plan would do the wrong thing.
Model a lifecycle-aware planner
Write planChange({ preventDestroy, ignoreChanges, changedAttrs, willDestroy }). If willDestroy and preventDestroy → return "error: prevent_destroy". Otherwise drop any changedAttrs listed in ignoreChanges; if none remain → "no-op", else "update". Call it with ignoreChanges: ["instances"], changedAttrs: ["instances"], willDestroy: false and log the result (expect no-op).