lesson ── terraform-beginner ── ~12 мин ── 4 шагов
By default Terraform works like this: "if the HCL changed,
update the resource. If you removed it from the HCL, delete it." But
sometimes you need to tweak that behavior. The lifecycle block inside
a resource is how you do it.
In this lesson you will try three options: prevent_destroy (guard
against deletion), ignore_changes (ignore drift on certain
attributes), create_before_destroy (zero-downtime recreation). See
tf-resource-lifecycle.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
Create main.tf:
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-lifecycle-${random_id.suffix.hex}" tags = {Owner = "student"
LastTouched = "2026-01-01" # this field often changes externally
}
}
resource "random_id" "suffix" {byte_length = 4
}
cd /home/student/tf-lifecycle
terraform init -input=false
terraform apply -auto-approve -input=false
This is the base setup: the next steps only add lifecycle options.
✓ The bucket is created with two tags. Next: guarding against destroy.
Add a lifecycle block to the resource:
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-lifecycle-${random_id.suffix.hex}" tags = {Owner = "student"
LastTouched = "2026-01-01"
}
lifecycle {prevent_destroy = true
}
}
Apply it:
terraform apply -auto-approve
Now try to delete it:
terraform destroy -auto-approve
You get:
Error: Instance cannot be destroyed
Resource aws_s3_bucket.demo has lifecycle.prevent_destroy set...
The guard works: to actually tear it down, you first have to remove
prevent_destroy, run apply, and only then destroy. This is a
two-step guard against accidents.
If destroy went through without an error, apply did not manage to write lifecycle into state. Repeat apply and destroy.
✓ prevent_destroy blocks destroy. In production this saves you from typos.
Imagine an external script updates the LastTouched tag every night.
Terraform sees this as drift and shows a change on every plan.
Annoying and dangerous.
The solution is ignore_changes. Add it to lifecycle:
lifecycle {prevent_destroy = true
ignore_changes = [
tags["LastTouched"],
]
}
Apply it:
terraform apply -auto-approve
Now simulate drift by changing the tag through the AWS CLI (more precisely, its LocalStack emulation that the aws-client runs inside). But in our sandbox we can do it differently: change the value in the HCL.
Open main.tf and change:
LastTouched = "2026-06-15" # new date
And run plan:
terraform plan
Output: No changes. Terraform saw the difference in
tags["LastTouched"], but ignore_changes told it "do not count this
as a change." State and reality are treated as matching.
If plan shows a change, check the ignore_changes syntax: a list in square brackets, with names in [].
✓ ignore_changes works: drift on a specific attribute is ignored.
The third common option is create_before_destroy = true. It changes the order during recreation of a resource (when an attribute requires replacement): by default Terraform first tears down the old one, then creates the new one, with downtime in between. With create_before_destroy it is the reverse: first create the new one, switch the dependencies over, then tear down the old one. This gives you zero-downtime, but it requires unique names (you cannot have two resources with the same identifier at once).
To leave the sandbox clean (though it will die on TTL anyway), remove lifecycle and tear down the bucket:
resource "aws_s3_bucket" "demo" { bucket = "linuxlab-lifecycle-${random_id.suffix.hex}" tags = {Owner = "student"
}
# no lifecycle at all
}
terraform apply -auto-approve
terraform destroy -auto-approve
The bucket is gone. This is the correct workflow:
prevent_destroy with apply.The two-step sequence is not pointless bureaucracy, it is a guard against accidents.
If apply complains about removing lifecycle, prevent_destroy is still in state. Run apply first, then destroy.
✓ Everything is torn down. You can now use lifecycle with intent.
OpenTofu keeps its CLI and state compatible with Terraform for the
commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. But on your first switch, back
up the state and do a run on a feature branch, since the
differences concentrate in the newer features (variables in the
backend, state encryption, OCI registry-backed modules). See
tf-opentofu-parity for the full matrix.
You saw three common uses of lifecycle: blocking destroy
with prevent_destroy, ignoring tags with
ignore_changes, and understanding how
create_before_destroy changes the order of operations during
recreation.
команды
terraform planlifecycle shows an error if a guard is violatedterraform state show <addr>see which lifecycle settings are active on a resourceконцепции