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-garden-02-state-drift

lesson ── terraform-garden ── ~20 мин ── 5 шагов

Troubleshooting Garden: state and the cloud have drifted apart

The scenario: you went on vacation, came back, and someone "fixed it by hand, urgently." The bucket in state has versioning=Enabled. The bucket in the cloud has versioning=Suspended. On top of that, a second bucket showed up that Terraform knows nothing about.

Your job is to spot both drifts, decide what to do with each one (revert it, import it, leave it), and bring the plan back to a clean state.

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

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

запустить sandbox →

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

Шаги

  1. 01

    Show the drift with refresh-only

    bash
    cd /home/student/tf-garden
    terraform plan -refresh-only 2>&1 | tee /tmp/drift.log

    You should see a line like this:

    ~ aws_s3_bucket_versioning.data
      ~ versioning_configuration { status = "Enabled" -> "Suspended" }

    This is drift number one: state believes versioning is Enabled, but reality shows Suspended. The bucket itself is still there, this is just a setting.

    ✓ Drift number one is visible. Now the orphan bucket.

  2. 02

    Find the orphan bucket in the cloud

    Terraform does not know about the second bucket, so as far as it is concerned the bucket "does not exist." But the AWS CLI sees it directly:

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls | tee /tmp/buckets.log

    You will see both buckets: garden-drift-data (this one is in state) and garden-drift-orphan (this one is the orphan).

    This is not drift in the strict sense, it is an unmanaged resource. Terraform does not know about it, so a plan will not show it.

    ✓ The orphan is visible. Now the strategies.

  3. 03

    Revert versioning to the HCL with apply

    The decision for drift number one: assume the HCL is the truth (Enabled). Someone made a mistake and turned versioning off, so you put it back.

    bash
    terraform apply -auto-approve

    Terraform sees that the cloud has Suspended where it should have Enabled, and reverts it. After that, the plan should be clean for versioning.

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

    If you get exit 2, there is more drift somewhere; if you get 0, versioning is fixed.

    ✓ Versioning is back to Enabled. Drift number one is closed.

  4. 04

    Import the orphan bucket into state

    The decision: the orphan bucket is one you want, someone created it by hand for logs but forgot to add it to the HCL. You pull it into Terraform by adding the HCL and an import block.

    Add this to main.tf:

    hcl
    resource "aws_s3_bucket" "orphan" {
      bucket = "garden-drift-orphan"
    }
    import {
      to = aws_s3_bucket.orphan
      id = "garden-drift-orphan"
    }

    Run:

    bash
    terraform plan
    terraform apply -auto-approve

    The import block (TF 1.5+) declaratively pulls the existing bucket into state. After the apply, the plan becomes clean.

    More on this in tf-state-import.

    ✓ The orphan is imported. It is now managed by Terraform.

    The same thing on OpenTofu

    OpenTofu has supported the import block since 1.6, and the syntax is identical. In a matrix CI you can run both side by side and compare the state files, they are compatible. See tf-opentofu-parity.

    • → terraform import
    • → OpenTofu parity
    • → Drift detection
  5. 05

    Finish: the plan should be clean

    bash
    terraform plan -detailed-exitcode

    Exit 0 means state and the cloud match, and the plan proposes nothing.

    If you get exit 2, something is still in drift, look at -refresh-only and walk through the steps again.

    ✓ The plan is clean. Drift and orphan are both closed.

Что ты узнал

You catch drift with terraform plan -refresh-only, with -detailed-exitcode in CI, and with a scheduled pipeline. You reconcile with apply (revert), with import (accept a new resource), or with removed (declaratively take it out of state without a destroy).

команды

  • terraform plan -refresh-onlysee the drift without building a change plan
  • terraform apply -refresh-onlyupdate state from the cloud without touching the infrastructure
  • terraform plan -detailed-exitcodeexit 2 means there is drift; use it in a scheduled pipeline

концепции

  • · Not every drift should be fixed with an apply, someone may have changed it on purpose
  • · An orphan resource is either an import or an ignore; never a destroy 'because it is not mine'
  • · S3 versioning Suspended is not the same as deleted; the data stays, new versions just stop being written

← предыдущий

Variables: removing the hardcode

следующий →

A Module from the Terraform Registry

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