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/lessons/tf-beginner-05-update

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

Update: change an attribute, read the diff

In real work HCL changes all the time: you add a tag, fix a name, switch the class on a database. Terraform has to figure out what changed and send exactly the API calls the change needs, without deleting and recreating everything along the way.

In this lesson you create a bucket, change its tags, and watch how plan shows the diff. You learn the difference between an update (update-in-place) and a recreate (-/+ replacement).

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

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

Шаги

  1. 01

    Create the initial bucket with one tag

    Create the file main.tf:

    hcl
    resource "aws_s3_bucket" "demo" {
      bucket = "linuxlab-update-${random_id.suffix.hex}"
      tags = {
        Owner = "student"
      }
    }
    resource "random_id" "suffix" {
      byte_length = 4
    }

    Run:

    bash
    cd /home/student/tf-update
    terraform init -input=false
    terraform apply -auto-approve -input=false

    After apply, state knows the bucket with one tag, Owner.

    подсказка

    If something fails: check init. And that LocalStack is alive.

    ✓ Bucket created with the tag Owner=student. Now add a second tag.

  2. 02

    Add a second tag in HCL

    Open main.tf and change the tags block:

    hcl
    tags = {
      Owner       = "student"
      Environment = "learning"
    }

    Now run plan without apply, and you will see the diff:

    bash
    terraform plan

    The output will contain a block like this:

      ~ resource "aws_s3_bucket" "demo" {
          ~ tags = {
                "Owner" = "student"
              + "Environment" = "learning"
            }
        }
      Plan: 0 to add, 1 to change, 0 to destroy.

    The ~ next to resource means update in place. No recreation. Inside: + "Environment" means the new tag will be added. Owner has no sign, so it does not change.

    See tf-plan on what the signs mean.

    подсказка

    If plan shows `-/+ resource ...`: you changed something that cannot be updated without replacement. Revert and change only tags.

    ✓ Plan showed a ~ change. Apply it: it will update without recreation.

  3. 03

    Apply the change

    bash
    terraform apply -auto-approve

    Apply will show:

    aws_s3_bucket.demo: Modifying...
    aws_s3_bucket.demo: Modifications complete after Xs
    Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

    The key part: 0 destroyed, 1 changed. The bucket was not recreated. The AWS provider simply sent a PutBucketTagging API request. The objects inside (if there had been any) stayed where they were.

    This is update-in-place. Your job is to structure HCL so that most changes follow this path.

    подсказка

    If you get a destroy: something besides tags changed. Check the main fields (bucket, etc): they should not be changing.

    ✓ Tag updated in place, same bucket.

    The same thing on OpenTofu

    OpenTofu keeps the CLI and state compatible with Terraform for the commands in this step: migration usually goes through mv .terraform .terraform.bak; tofu init -upgrade. On a first switch, though, make a backup of the state and do a run on a feature branch. The differences cluster in the newer features (variables in backend, state encryption, OCI registry-backed modules). See tf-opentofu-parity for the full matrix.

    • → OpenTofu parity
  4. 04

    Run plan again: clean

    The main Terraform invariant: after apply, a repeated plan = No changes. If it shows changes, something is off (drift, or ignore_changes skipped something).

    Check it:

    bash
    terraform plan -detailed-exitcode
    echo "exit: $?"

    It should be exit: 0. If it were 2, that means there is an inconsistency.

    -detailed-exitcode matters for CI: you can alert automatically on drift.

    подсказка

    If plan prints `No changes` in the human-readable text: everything is OK, the exit code will be 0.

    ✓ Plan is clean. Invariant holds: state == HCL.

    When apply does destroy + create (-/+)

    Some resource attributes cannot be changed without recreation. For example, availability_zone on an EC2 instance, instance_class on some RDS instances, bucket on an S3 bucket. If you change the bucket name, AWS cannot rename it, only create a new one and tear down the old one. Terraform shows this in the plan as -/+. Be careful: the data of a recreated resource is lost.

    • → lifecycle block
    • → terraform plan

Что ты узнал

You saw four kinds of change in a plan: + create, ~ update in place, -/+ recreate, and the final No changes after apply. This is the basic grammar of reading plans.

команды

  • terraform planshow the diff between HCL and state
  • terraform apply -auto-approveapply the plan
  • terraform plan -detailed-exitcodeexit 0 = clean, 2 = there are changes

концепции

  • · + = create, ~ = update in place, -/+ = recreate, - = delete
  • · After apply a repeated plan must be clean: that is the invariant
  • · Not every attribute can be updated: some require recreation

← предыдущий

Blue-green migration of legacy infrastructure in Terraform

следующий →

Troubleshooting Garden: the module went stale, the provider got upgraded

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