Когда лезть в state руками
В идеале, никогда. Terraform любит declarative-подход: меняешь HCL, делаешь plan, видишь что будет, применяешь. Прямые операции над state, для случаев когда declarative не работает:
- Переименование ресурса без destroy и create (исторически, через
state mv; с TF 1.1+, черезmovedблок, см. tf-moved-block). - Удаление из управления terraform без удаления в облаке (с TF 1.7+
, через
removedблок, см. tf-removed-block). - Аварийное починить битый state,
state pull, поправить JSON руками,state push. Редко, страшно, обычно через support. - Слияние двух state'ов в один,
state mvс межgolang-state'овой нотацией.
Декларативные блоки (moved, removed, import): предпочтительны.
Они оставляют след в HCL и git, повторяются между членами команды
автоматически. CLI-операции, invisible, только тот кто запустил знает
что произошло.
terraform state list
Точка входа: что в state'е?
terraform state list
# aws_s3_bucket.demo
# module.logs.aws_s3_bucket.this
# random_id.suffix
Фильтр по адресу:
terraform state list 'module.logs.*'
# module.logs.aws_s3_bucket.this
Полезно перед любой state mv/rm, проверь что трогаешь именно то.
terraform state show
Атрибуты конкретного ресурса:
terraform state show aws_s3_bucket.demo
Печатает HCL-подобную форму со всеми attributes. Включая sensitive (в
отличие от terraform plan/apply, который маскирует). Это причина
почему state защищается так же строго как секреты.
terraform state mv
Самая частая «руками» операция. Меняет адрес ресурса в state без destroy.
Кейс 1: переименовать ресурс
Был:
resource "aws_s3_bucket" "logs" { ... }Хочешь:
resource "aws_s3_bucket" "log_storage" { ... }Без state mv. Terraform увидит «logs больше нет в HCL, log_storage
появился» и сделает destroy+create. Бакет уничтожится с данными.
С state mv:
terraform state mv aws_s3_bucket.logs aws_s3_bucket.log_storage
Теперь в state ресурс называется log_storage. HCL поправь параллельно.
terraform plan → No changes.
Кейс 2: переместить в модуль
Был ресурс в root, хочешь вынести в модуль ./modules/buckets.
# сначала добавь в HCL:
# module "buckets" { source = "./modules/buckets" }# внутри модуля: resource "aws_s3_bucket" "logs" { ... }terraform state mv aws_s3_bucket.logs module.buckets.aws_s3_bucket.logs
plan → No changes. Это типичный refactoring-сценарий, см.
tf-refactor-patterns.
Кейс 3: между разными state'ами
Один root отделяешь на два root'а. Часть ресурсов мигрирует:
# в исходном root:
terraform state mv -state-out=../other/terraform.tfstate \
aws_s3_bucket.logs aws_s3_bucket.logs
Это редкая операция, обычно, при разделении монолита на сервисы.
terraform state rm
Убирает ресурс из state. Облачный ресурс остаётся.
terraform state rm aws_s3_bucket.demo
Теперь Terraform «забывает» про бакет. Следующий apply (если ресурс
в HCL остался) увидит «нет в state, есть в HCL», попытается создать
заново, упадёт на «bucket already exists». Чтобы захватить, import,
см. tf-state-import.
Кейсы:
- Ресурс случайно попал в этот state, должен жить в другом. После
state rmделаешьimportв правильном state. - Переход на другую конструкцию: был
count = 1, делаемfor_each. Адреса разные, без помощи Terraform делает destroy+create.state rmстарого адреса +importнового, без пересоздания. С TF 1.1+ это лучше черезmoved.
-dry-run
terraform state rm -dry-run aws_s3_bucket.demo
Покажет что бы сделал, без изменений. Всегда используй -dry-run перед настоящим rm.
terraform state pull / push
Аварийный механизм. Скачать state как файл:
terraform state pull > terraform.tfstate.dump
Работает с любым backend, local, S3, remote. Сохраняет в текущую папку.
Залить обратно:
terraform state push terraform.tfstate.dump
Terraform проверит lineage и serial. Если serial меньше текущего,
спросит подтверждение (это потенциальный rollback). Если lineage
отличается, откажется, защита от подмены state'а другого проекта.
Используется когда:
- Нужно править JSON руками (например, после провайдер-апгрейда формат
state поломался). Это последняя надежда, обычно лучше
terraform refreshили поговорить с автором провайдера. - Перенос state между backends руками (вместо
init -migrate-state). - Резервный бекап перед опасной операцией:
state pull > backup-$(date).json.
Backup перед любой операцией
Любая state mv/rm/push, сделай дамп до:
terraform state pull > backup-pre-refactor-$(date +%s).json
S3-backend с версионированием это делает автоматически, но дублировать не повредит. С local backend, обязательно вручную.
Подводные камни
-
state mvне правит HCL. Поменял имя в state, поменяй и в HCL. Иначе следующийplanснова покажет destroy+create. -
state rmне возвращает. Реально нет «undo». Если облачный ресурс важен, сначалаstate pull > backup.json, потом rm. При проблеме,state push backup.json. -
С remote backend lock работает только если ты ходишь через terraform CLI.
state pullберёт lock,state pushтоже. Но если ты рукамиaws s3 cp tfstate ./, никакого lock нет, можешь конфликтовать с apply'ем другого человека. -
state mvдляcount/for_eachресурсов требует точного адреса.aws_iam_user.user[0]илиaws_iam_user.user["alice"], неaws_iam_user.user. Если ошибся индекс, переместишь не тот. -
Между state'ами с разным
lineage, push не пройдёт. Это защита. Если действительно нужно (например, ты делаешь bootstrap нового state'а с импортом из старого): добавь-forceк push. Но сначала ответь себе зачем. -
movedиremovedблоки лучше, когда применимы. Они декларативны, видны в diff, выполняются автоматически у всех.state mv/rm, один раз, у того кто запустил, остальные узнают по конфликтам.