# state mv, state rm, state pull/push: ручные операции _State · TerraformLab Knowledge Base_ **TL;DR:** `terraform state mv` переименовывает адрес ресурса в state (без destroy/recreate). `terraform state rm` убирает ресурс из state (но не из облака). `terraform state pull/push`, скачать/залить state как файл. Все четыре, резкие операции, делать через backup и понимая зачем. Для декларативных альтернатив есть [[tf-moved-block]] и [[tf-removed-block]]. ## Когда лезть в state руками В идеале, никогда. Terraform любит declarative-подход: меняешь HCL, делаешь plan, видишь что будет, применяешь. Прямые операции над state, для случаев когда declarative не работает: - **Переименование ресурса** без destroy и create (исторически, через `state mv`; с TF 1.1+, через `moved` блок, см. [tf-moved-block](/terraform/kb/tf-moved-block.md)). - **Удаление из управления** terraform без удаления в облаке (с TF 1.7+ , через `removed` блок, см. [tf-removed-block](/terraform/kb/tf-removed-block.md)). - **Аварийное починить** битый state, `state pull`, поправить JSON руками, `state push`. Редко, страшно, обычно через support. - **Слияние двух state'ов** в один, `state mv` с межgolang-state'овой нотацией. Декларативные блоки (`moved`, `removed`, `import`): предпочтительны. Они оставляют след в HCL и git, повторяются между членами команды автоматически. CLI-операции, invisible, только тот кто запустил знает что произошло. ## terraform state list Точка входа: что в state'е? ```bash terraform state list # aws_s3_bucket.demo # module.logs.aws_s3_bucket.this # random_id.suffix ``` Фильтр по адресу: ```bash terraform state list 'module.logs.*' # module.logs.aws_s3_bucket.this ``` Полезно перед любой `state mv/rm`, проверь что трогаешь именно то. ## terraform state show Атрибуты конкретного ресурса: ```bash terraform state show aws_s3_bucket.demo ``` Печатает HCL-подобную форму со всеми attributes. Включая sensitive (в отличие от `terraform plan/apply`, который маскирует). Это **причина** почему state защищается так же строго как секреты. ## terraform state mv Самая частая «руками» операция. Меняет адрес ресурса в state без destroy. ### Кейс 1: переименовать ресурс Был: ```hcl resource "aws_s3_bucket" "logs" { ... } ``` Хочешь: ```hcl resource "aws_s3_bucket" "log_storage" { ... } ``` Без `state mv`. Terraform увидит «logs больше нет в HCL, log_storage появился» и сделает destroy+create. Бакет уничтожится с данными. С `state mv`: ```bash terraform state mv aws_s3_bucket.logs aws_s3_bucket.log_storage ``` Теперь в state ресурс называется `log_storage`. HCL поправь параллельно. `terraform plan` → `No changes`. ### Кейс 2: переместить в модуль Был ресурс в root, хочешь вынести в модуль `./modules/buckets`. ```bash # сначала добавь в 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](/terraform/kb/tf-refactor-patterns.md). ### Кейс 3: между разными state'ами Один root отделяешь на два root'а. Часть ресурсов мигрирует: ```bash # в исходном root: terraform state mv -state-out=../other/terraform.tfstate \ aws_s3_bucket.logs aws_s3_bucket.logs ``` Это редкая операция, обычно, при разделении монолита на сервисы. ## terraform state rm Убирает ресурс из state. **Облачный ресурс остаётся**. ```bash terraform state rm aws_s3_bucket.demo ``` Теперь Terraform «забывает» про бакет. Следующий `apply` (если ресурс в HCL остался) увидит «нет в state, есть в HCL», попытается **создать заново**, упадёт на «bucket already exists». Чтобы захватить, `import`, см. [tf-state-import](/terraform/kb/tf-state-import.md). Кейсы: - Ресурс случайно попал в этот state, должен жить в другом. После `state rm` делаешь `import` в правильном state. - Переход на другую конструкцию: был `count = 1`, делаем `for_each`. Адреса разные, без помощи Terraform делает destroy+create. `state rm` старого адреса + `import` нового, без пересоздания. С TF 1.1+ это лучше через `moved`. ### `-dry-run` ```bash terraform state rm -dry-run aws_s3_bucket.demo ``` Покажет что бы сделал, без изменений. **Всегда используй -dry-run перед настоящим rm.** ## terraform state pull / push Аварийный механизм. Скачать state как файл: ```bash terraform state pull > terraform.tfstate.dump ``` Работает с любым backend, local, S3, remote. Сохраняет в текущую папку. Залить обратно: ```bash 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`, сделай дамп **до**: ```bash 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`, один раз, у того кто запустил, остальные узнают по конфликтам. ## Команды ```bash terraform state list ``` Все ресурсы под управлением. Точка входа для любых state-операций. ```bash terraform state show
``` Атрибуты ресурса включая sensitive. Чтоб видно что мы трогаем. ```bash terraform state mv