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
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-beginner-09-lifecycle

lesson ── terraform-beginner ── ~12 мин ── 4 шагов

lifecycle: blocking, ignoring, and recreating

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 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Create a bucket with a tag we will ignore later

    Create main.tf:

    hcl
    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
    }
    bash
    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.

  2. 02

    Protect the bucket from deletion

    Add a lifecycle block to the resource:

    hcl
    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:

    bash
    terraform apply -auto-approve

    Now try to delete it:

    bash
    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.

  3. 03

    Ignore changes to LastTouched

    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:

    hcl
    lifecycle {
      prevent_destroy = true
      ignore_changes  = [
        tags["LastTouched"],
      ]
    }

    Apply it:

    bash
    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:

    hcl
    LastTouched = "2026-06-15"   # new date

    And run plan:

    bash
    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.

    create_before_destroy for zero-downtime

    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).

    • → the whole lifecycle block
  4. 04

    Remove prevent_destroy and delete

    To leave the sandbox clean (though it will die on TTL anyway), remove lifecycle and tear down the bucket:

    hcl
    resource "aws_s3_bucket" "demo" {
      bucket = "linuxlab-lifecycle-${random_id.suffix.hex}"
      tags = {
        Owner = "student"
      }
      # no lifecycle at all
    }
    bash
    terraform apply -auto-approve
    terraform destroy -auto-approve

    The bucket is gone. This is the correct workflow:

    1. You need to delete a protected resource.
    2. Step 1: remove prevent_destroy with apply.
    3. Step 2: run destroy.

    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.

    The same thing on OpenTofu

    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.

    • → OpenTofu parity

Что ты узнал

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 violated
  • terraform state show <addr>see which lifecycle settings are active on a resource

концепции

  • · prevent_destroy: guard against accidental deletion (two-step)
  • · ignore_changes. Terraform does not count drift on these attributes
  • · create_before_destroy: create the new one, then tear down the old one

← предыдущий

GitHub Actions, the full pipeline through act

следующий →

The moved block: refactoring without recreation

Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies