linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Resources & data sources/tf-resource-lifecycle

kb/resources ── Resources & data sources ── beginner

lifecycle: controlling resource behavior

The lifecycle block configures four behaviors: create_before_destroy (zero-downtime replacement), prevent_destroy (deletion guard), ignore_changes (ignore drift on specific attributes), replace_triggered_by (force replacement on an external signal).

view as markdown

Why lifecycle exists

By default Terraform follows a simple rule: if HCL changed, update the resource; if it was removed from HCL, delete the resource. This works in 90% of cases, but sometimes you need to adjust that behavior.

lifecycle is a nested block inside resource that gives you four options for fine-grained control.

prevent_destroy = true, guarding against accidents

This is the most valuable option for production:

hcl
resource "aws_db_instance" "main" {
  identifier = "prod-db"
  # ... Parameters ...
  lifecycle {
    prevent_destroy = true
  }
}

Any attempt to delete this resource via apply or destroy now fails with an error:

Error: Instance cannot be destroyed
Resource aws_db_instance.main has lifecycle.prevent_destroy set, but the
plan calls for this resource to be destroyed.

To actually delete the resource, you must remove prevent_destroy = true, run apply, and only then attempt destroy again. Two-step protection. You have to consciously disable the flag first.

Use it on: prod databases, S3 buckets with data, KMS keys, and any resource whose loss would be catastrophic.

create_before_destroy = true, zero-downtime

By default, when a resource must be replaced (-/+), Terraform does:

  1. Delete the old one.
  2. Create the new one.

There is downtime between steps 1 and 2. For critical resources that is unacceptable.

hcl
resource "aws_launch_template" "web" {
  name_prefix   = "web-"
  instance_type = "t3.micro"
  lifecycle {
    create_before_destroy = true
  }
}

With this flag set, Terraform:

  1. Creates the new resource.
  2. Switches dependencies to the new one.
  3. Deletes the old one.

Caveats:

  • The resource must have a unique name. You cannot use name = "web" because there will be a conflict (the old one is not deleted yet, so a new one with the same name cannot be created). Use name_prefix or include a suffix in the name.
  • Some resources have constraints. AWS RDS does not allow two instances with the same identifier.

ignore_changes = [...], ignoring drift

Sometimes an attribute changes outside Terraform and you do not care. A last_modified tag set by an external script. Auto-scaling changing desired_capacity. Without ignore_changes, every plan would show a diff and propose a rollback.

hcl
resource "aws_autoscaling_group" "web" {
  name             = "web-asg"
  min_size         = 2
  max_size         = 10
  desired_capacity = 2
  lifecycle {
    # The ASG controller changes desired_capacity automatically; leave it alone.
    ignore_changes = [desired_capacity]
  }
}

You can ignore everything:

hcl
lifecycle {
  ignore_changes = all
}

That is rarely correct. It turns the resource into "created once, no longer managed."

replace_triggered_by = [...], forcing replacement on a signal

Sometimes you need to recreate a resource when a different resource changes. For example, an EC2 instance should be replaced when user_data changes, even if the AWS provider does not consider that a reason for replacement.

hcl
resource "null_resource" "config_version" {
  triggers = {
    config_hash = filemd5("config.yaml")
  }
}
resource "aws_instance" "web" {
  ami           = "ami-..."
  instance_type = "t3.micro"
  lifecycle {
    replace_triggered_by = [null_resource.config_version]
  }
}

When config.yaml changes, null_resource.config_version is replaced (new triggers), and aws_instance.web is replaced along with it.

Pitfalls

  • prevent_destroy does not protect against terraform state rm. If someone removes the resource from state by hand, Terraform no longer tracks it, and prevent_destroy has no effect. It only blocks changes made through apply/destroy.

  • create_before_destroy requires unique naming. The name, identifier, or any attribute with a unique constraint must be dynamic. Otherwise you get a conflict.

  • ignore_changes does not apply to add/remove. If you add a tag in HCL that did not exist before, ignore_changes = [tags] will not stop Terraform from creating it. The ignore only applies when the cloud and HCL diverge, not when you explicitly change HCL.

  • lifecycle is not available in a data block. It is a construct for resource blocks only.

  • One lifecycle block per resource. Multiple lifecycle blocks in a single resource is an error.

  • Do not use ignore_changes to hide a problem. If plan shows unexpected drift, find out why the attribute is changing outside Terraform. Fix the cause, not the symptom.

§ команды

bash
terraform plan

Shows whether a lifecycle block is blocking a destroy, with a clear error message.

bash
terraform state list | xargs -I {} terraform state show {} | grep prevent_destroy

Find all resources that have prevent_destroy set. Useful before migrations.

§ см. также

  • tf-resource-blockResource block: the main building block of TerraformA resource block tells Terraform "create this thing in the cloud." It has three parts: the resource type (what it is), the name (how you refer to it internally), and the arguments (how to configure it). Writing these blocks is what you spend 90% of your time doing in Terraform.
  • tf-destroyterraform destroy: tear down everything you createddestroy removes every resource described in HCL and recorded in state. It is apply with a minus sign in front of everything. It is essential for learning tasks and ephemeral environments. In production it is a last resort.
  • tf-applyterraform apply: apply a plan to a real cloudapply takes the result of plan and actually calls the cloud API: it creates, changes, and deletes resources. After apply, the state is updated. This is the command that turns money into infrastructure.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies