lesson ── terraform-garden ── ~20 мин ── 5 шагов
Сценарий: ты ушёл в отпуск, вернулся, кто-то «срочно поправил руками». Бакет в state, versioning=Enabled. Бакет в облаке, versioning=Suspended. Плюс рядом завёлся ещё один бакет, которого Terraform не знает.
Твоя задача, увидеть оба drift'а, решить как с каждым поступить (вернуть, импортировать, оставить), и привести plan к чистому виду.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
cd /home/student/tf-garden
terraform plan -refresh-only 2>&1 | tee /tmp/drift.log
Должна вылезти строка вида:
~ aws_s3_bucket_versioning.data
~ versioning_configuration { status = "Enabled" -> "Suspended" }Это и есть drift №1: state считает versioning Enabled, реальность показывает Suspended. Сам бакет тоже на месте, это просто настройка.
✓ Drift №1 виден. Теперь, orphan bucket.
Terraform не знает про второй бакет, для него «его не существует». Но через AWS CLI напрямую он виден:
aws --endpoint-url=http://localstack:4566 s3 ls | tee /tmp/buckets.log
Ты увидишь оба бакета: garden-drift-data (этот в state) и
garden-drift-orphan (этот orphan).
Это не drift в строгом смысле, это unmanaged resource. Terraform про него не знает, и plan его не покажет.
✓ Orphan виден. Теперь, стратегии.
Решение по drift №1: предполагаем что в HCL правда (Enabled). Кто-то ошибся и убрал versioning, возвращаем.
terraform apply -auto-approve
Terraform увидит что в облаке Suspended, а должно быть Enabled, и вернёт. После, plan должен быть чистым по versioning.
terraform plan -detailed-exitcode || echo "exit-code: $?"
Если exit 2, где-то ещё drift; если 0, versioning починен.
✓ Versioning вернулся в Enabled. Drift №1 закрыт.
Decision: orphan-бакет нужный, кто-то завёл его руками для логов, но забыл добавить в HCL. Подтянем в Terraform: добавь HCL и import-блок.
Добавь в main.tf:
resource "aws_s3_bucket" "orphan" {bucket = "garden-drift-orphan"
}
import {to = aws_s3_bucket.orphan
id = "garden-drift-orphan"
}
Запусти:
terraform plan
terraform apply -auto-approve
Import-блок (TF 1.5+) декларативно затащит существующий бакет в state. После apply plan становится чистым.
Подробнее, tf-state-import.
✓ Orphan заимпортирован. Теперь под управлением Terraform.
OpenTofu поддерживает import-блок начиная с 1.6, синтаксис идентичен. В матричных CI можно гонять оба бок-о-бок и сравнивать state-файлы они совместимы. См. tf-opentofu-parity.
terraform plan -detailed-exitcode
Exit 0, state и облако совпадают, plan ничего не предлагает.
Если exit 2, что-то ещё в дрифте, посмотри -refresh-only и пройди шаги ещё раз.
✓ Plan чистый. Drift и orphan закрыты.
Drift ловится через terraform plan -refresh-only, через -detailed-exitcode в CI, через scheduled-pipeline. Reconcile через apply (вернуть), import (принять новый ресурс), removed (декларативно убрать из state без destroy).
команды
terraform plan -refresh-onlyпосмотреть drift не строя план измененийterraform apply -refresh-onlyобновить state из облака, не меняя инфруterraform plan -detailed-exitcodeexit 2 = есть drift; для scheduled-pipelineконцепции