lesson ── terraform-intermediate ── ~15 мин ── 6 шагов
State, это файл с записями «адрес ресурса в HCL → конкретный объект в
облаке». Иногда адрес нужно поменять (переименование, перенос в модуль): но облачный ресурс не трогать. CLI terraform state mv/rm это делают.
В этом уроке: создашь бакет, переименуешь его в state'е, проверишь что бакет не пересоздавался, потом удалишь ресурс из state без destroy.
С TF 1.1+ для большинства кейсов лучше декларативный moved блок,
он будет в следующем уроке. Сейчас, про CLI, потому что иногда
только она работает.
интерактивный sandbox
Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.
stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя
cd /home/student/tf-state-ops
cat > main.tf <<'EOF'
resource "random_id" "suffix" {byte_length = 4
}
resource "aws_s3_bucket" "logs" { bucket = "linuxlab-state-ops-${random_id.suffix.hex}"}
EOF
terraform init
terraform apply -auto-approve
terraform state list
Должно вывести:
aws_s3_bucket.logs
random_id.suffix
✓ Бакет создан под адресом aws_s3_bucket.logs.
terraform state pull > /tmp/state-backup.json
ls -la /tmp/state-backup.json
jq '.serial, .lineage' /tmp/state-backup.json
Это твой safety-net. Если что-то пойдёт не так, terraform state push /tmp/state-backup.json вернёт всё на место.
С S3-backend + версионированием это дублирует автоматическую защиту, но привычка, бесплатно.
✓ Бэкап сделан. Теперь: state mv.
Хотим переименовать aws_s3_bucket.logs → aws_s3_bucket.log_storage.
terraform state mv aws_s3_bucket.logs aws_s3_bucket.log_storage
Вывод:
Move "aws_s3_bucket.logs" to "aws_s3_bucket.log_storage"
Successfully moved 1 object(s).
Теперь обязательно поправь HCL, иначе plan покажет destroy на
log_storage и create на logs:
sed -i 's/"aws_s3_bucket" "logs"/"aws_s3_bucket" "log_storage"/' main.tf
Проверь:
grep -n "aws_s3_bucket" main.tf
terraform state list
terraform plan
State и HCL согласованы. Plan: No changes.
✓ Адрес переименован, бакет в облаке не пересоздавался.
Хотим убрать ресурс из управления, но не удалять бакет в облаке.
Сначала dry-run:
terraform state rm -dry-run aws_s3_bucket.log_storage
Покажет:
Would remove:
aws_s3_bucket.log_storage
Без изменений. Это твой sanity check. Всегда dry-run перед настоящим rm. См. tf-state-manipulation.
✓ Dry-run отработал. Теперь: настоящий rm.
terraform state rm aws_s3_bucket.log_storage
Вывод:
Removed aws_s3_bucket.log_storage
Successfully removed 1 resource instance(s).
Проверь:
terraform state list
aws_s3_bucket.log_storage исчез. В облаке, посмотри:
aws --endpoint-url=http://localstack:4566 s3 ls
Бакет всё ещё там! State его «забыл», но реально он живёт. Теперь этот бакет, ничейный с точки зрения terraform.
Если ты сейчас сделаешь terraform apply без правки HCL. Terraform
увидит «log_storage есть в HCL, нет в state» и попробует создать
его в облаке. Упадёт на «bucket already exists».
Это нормальное состояние при подготовке к import (следующий урок).
Чтобы сейчас починить, удали ресурс из HCL:
# удаляем resource блок aws_s3_bucket.log_storage
cat > main.tf <<'EOF'
resource "random_id" "suffix" {byte_length = 4
}
EOF
✓ State освободил ресурс, бакет в облаке остался. Это и есть state rm.
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 для полной матрицы.
Если хотел вернуть как было:
terraform state push /tmp/state-backup.json
Terraform проверит lineage и serial:
-force обходит проверки, но обычно не нужен.
terraform state list
Видны все ресурсы из бэкапа. Это твой safety-net.
В реальной работе с S3-backend бэкап делается автоматически
(versioning). Восстановление, через aws s3 cp s3://.../tfstate?versionId=....
✓ Восстановление возможно. Бэкап перед операциями: не паранойя, а гигиена.
Главное правило: декларативно лучше императивно.
| Кейс | Что использовать |
|---|---|
| Переименование (один человек, в текущем спринте) | state mv быстрее, но moved лучше для PR |
| Переименование (в команде, в репо) | moved блок, повторится у всех |
| Удалить из state без destroy | removed блок (TF 1.7+): видно в diff |
| Аварийный «уберите этот ресурс пока я разбираюсь» | state rm |
| Слияние state'ов разных root'ов | state mv с -state-out |
| Починка битого state из дампа | state push (только так) |
Правило: в production-репо CLI, last resort. Сначала пробуй
декларативные блоки. CLI оставляет операцию без следа в git,
полгода спустя никто не вспомнит почему bucket.logs стал
bucket.log_storage.
terraform state mv <SRC> <DST> меняет адрес в state без trogания
облака. terraform state rm <ADDR> убирает запись из state, ресурс в
облаке остаётся. Перед обоими, terraform state pull > backup.json.
команды
terraform state listчто в state: точка входаterraform state pull > backup.jsonбэкап перед опасной операциейterraform state mv SRC DSTпереименовать адресterraform state rm -dry-run ADDRчто бы сделал rm: без измененийterraform state push backup.jsonвосстановить state из дампаконцепции