linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-production-10-drift-detection

lesson ── terraform-production ── ~14 мин ── 6 шагов

Drift detection, scheduled plan и алертинг

Финал production-трека. Drift, расхождение между HCL и облаком (кто-то правит руками, default-теги, чужой apply). Ловим scheduled-job'ой в CI: terraform plan -detailed-exitcode, exit 2 = drift. На этом уроке создаёшь baseline, ломаешь его через aws-cli, видишь как plan обнаруживает drift.

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

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

запустить sandbox →

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

Шаги

  1. 01

    Создай baseline-инфру

    bash
    cd /home/student/tf-drift
    cat > main.tf <<'EOF'
    resource "aws_s3_bucket" "drift_demo" {
      bucket = "linuxlab-drift-demo"
      tags = {
        ManagedBy = "terraform"
        Owner     = "student"
      }
    }
    EOF
    terraform init -no-color > /dev/null
    terraform apply -auto-approve -no-color

    Бакет создан, state совпадает с реальностью. Baseline.

    ✓ Baseline есть. State == облако.

  2. 02

    detailed-exitcode без drift'а

    bash
    set +e
    terraform plan -detailed-exitcode -no-color > /dev/null 2>&1
    code=$?
    set -e
    echo "exit: $code"

    Должно быть 0, clean, нет drift'а. Это то что мы ожидаем в production когда ничего не сломано.

    Если ты добавишь сейчас --refresh=false, exit код тот же, но проверка слабее (см. подводные камни).

    ✓ Plan чистый. Без drift'а, clean cron-pass.

  3. 03

    Сломай реальность через aws-cli

    «Кто-то полез в Console и поправил теги»:

    bash
    aws --endpoint-url=http://localstack:4566 \
      s3api put-bucket-tagging \
      --bucket linuxlab-drift-demo \
      --tagging 'TagSet=[
        {Key=ManagedBy,Value=manual},
        {Key=Hacker,Value=was-here}
      ]'
    aws --endpoint-url=http://localstack:4566 \
      s3api get-bucket-tagging --bucket linuxlab-drift-demo

    Реальный бакет теперь:

    • ManagedBy: manual (был terraform)
    • Hacker: was-here (новый)
    • Owner пропал

    State Terraform этого не знает.

    ✓ Drift введён. Сейчас Terraform его обнаружит.

  4. 04

    plan -detailed-exitcode == 2

    bash
    set +e
    terraform plan -detailed-exitcode -no-color 2>&1 | tail -20
    code=$?
    set -e
    echo "exit: $code"

    Должно показать diff (теги отличаются) и exit 2. Это и есть drift-сигнал.

    В CI:

    • exit 0 → clean, no action.
    • exit 1 → ошибка (state corrupt, провайдер падает, etc.).
    • exit 2 → drift; alert Slack/PD/issue.

    Можно прочитать diff детально:

    bash
    terraform plan -no-color -out=drift.tfplan 2>&1 | grep -A20 "drift_demo" | head -40

    Видно конкретно что разъехалось, Terraform хочет вернуть теги к HCL-описанию.

    ✓ Drift пойман. exit 2, сигнал для cron-алерта.

  5. 05

    Скрипт scheduled-drift'а

    Производственный shell-скрипт для scheduled-job'а:

    bash
    cat > drift-check.sh <<'EOF'
    #!/usr/bin/env bash
    set -uo pipefail
    cd /home/student/tf-drift
    terraform init -input=false -no-color > /dev/null
    set +e
    terraform plan \
      -detailed-exitcode \
      -input=false \
      -no-color \
      -lock-timeout=2m \
      -out=drift.tfplan
    code=$?
    set -e
    case $code in
      0)
        echo "drift-check: clean, no changes"
        exit 0
        ;;
      2)
        echo "drift-check: DRIFT DETECTED"
        terraform show -no-color drift.tfplan > drift.txt
        # Здесь был бы webhook в Slack:
        # curl -X POST -H 'Content-Type: application/json' \
        #   --data "{\"text\": \"Drift detected:\n$(cat drift.txt | head -50)\"}" \
        #   "$SLACK_WEBHOOK_URL"
        echo "--- begin drift ---"
        head -30 drift.txt
        echo "--- end drift ---"
        exit 1  # CI считает drift как failure
        ;;
      *)
        echo "drift-check: ERROR (exit $code)"
        exit $code
        ;;
    esac
    EOF
    chmod +x drift-check.sh
    ./drift-check.sh 2>&1 | tail -30
    echo "script exit: $?"

    Должно показать DRIFT DETECTED и exit 1 (потому что drift есть).

    ✓ Cron-script готов. В GitHub Actions это запускается через schedule cron.

    То же самое на OpenTofu

    OpenTofu держит CLI и state совместимыми с Terraform по командам этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе сделай backup state и прогон на feature-branch - расхождения концентрируются в новых фичах (variables в backend, state-encryption, OCI registry-backed модули). См. tf-opentofu-parity для полной матрицы.

    • → OpenTofu parity
  6. 06

    Reconcile vs ignore, что делать с drift'ом

    Есть две стратегии:

    1. Reconcile, apply возвращает облако к HCL:

    bash
    terraform apply -auto-approve drift.tfplan

    Это уничтожит teг Hacker:was-here, восстановит ManagedBy:terraform и Owner:student. Подходит когда drift, нежелательный.

    2. Update HCL, облако правое:

    bash
    # ничего не делать с облаком, а в HCL добавить:
    # tags = { ManagedBy = "manual", Owner = "student" }

    Это когда «кто-то правил Console», но изменение нужное; ты легализуешь его в HCL. Затем apply -refresh-only для синка state'а.

    3. Ignore, drift есть, но не важен:

    hcl
    lifecycle {
      ignore_changes = [tags["Hacker"]]
    }

    Terraform перестанет рапортовать о теге Hacker. Используй когда этот тег ставится другой системой (k8s-operator, AWS Config) и не относится к Terraform.

    Сделаем reconcile:

    bash
    terraform apply -auto-approve -no-color > /dev/null
    aws --endpoint-url=http://localstack:4566 \
      s3api get-bucket-tagging --bucket linuxlab-drift-demo

    Теги вернулись к HCL-версии.

    ✓ Drift отreconcile'ен. Production-трек закрыт.

    Когда HCL не покрывает всё, что есть в облаке

    terraform plan видит drift только для ресурсов, которые в state. Если кто-то создал бакет руками, он в облаке есть, в state нет, plan его не увидит.

    Для этого, driftctl:

    bash
    # установка
    curl -L https://github.com/snyk/driftctl/releases/latest/download/driftctl_linux_amd64 \
      -o /usr/local/bin/driftctl
    chmod +x /usr/local/bin/driftctl
    # сканирование
    driftctl scan \
      --from tfstate+file://terraform.tfstate \
      --output console

    Покажет:

    • Managed resources, у которых drift.
    • Unmanaged, что в облаке есть, в state нет.
    • Deleted, что в state есть, в облаке нет.

    Это шире, чем terraform plan. В production стек: cron с terraform plan -detailed-exitcode (часто) + driftctl scan (реже, например, раз в неделю).

    Альтернатива от AWS, AWS Config: cloud-native сервис, который логит каждое изменение конфигурации. Использовать когда хочешь cross-account / cross-team visibility, и audit-trail важнее terraform-specific сигнала.

    См. tf-drift-detection.

    • → Drift detection теория
    • → Plan-as-artifact

Что ты узнал

terraform plan -detailed-exitcode, exit 0 (clean), 1 (error), 2 (drift). Cron-job в CI гоняет это раз в день/час, на 2, алерт в Slack/PD/issue. Plan-job читает state, не пишет, IAM-роль read-only.

команды

  • terraform plan -detailed-exitcode -no-colorканонический drift-check. Exit код = вердикт.
  • terraform plan -refresh-onlyтолько refresh без сравнения с HCL, что в state изменилось из облака.
  • aws s3api put-bucket-tagging --bucket X --tagging '...'пример того что делает «чужой apply», создаёт drift.

концепции

  • · detailed-exitcode 2, drift; 1, ошибка не drift
  • · Read-only role важен, drift-job не должен случайно apply'ить
  • · False positives устают команду; чисти их через ignore_changes

← предыдущий

preconditions, postconditions и check блок

следующий →

Гигиена HCL: fmt, validate, console

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки