# State: память Terraform о созданном _State · TerraformLab Knowledge Base_ **TL;DR:** State. JSON-файл terraform.tfstate, где Terraform записывает, что он создал в облаке. Без него Terraform не знал бы, какой бакет «его», а какой чужой. Содержит ID ресурсов, все атрибуты, и часто секреты. Самая чувствительная часть проекта. ## Зачем нужен state Представьте: вы написали HCL с одним S3-бакетом и сделали `apply`. Бакет создался, имя, `my-bucket-12345`. Запускаете `apply` ещё раз. Откуда Terraform узнает, что бакет: **тот же самый** бакет? - Из HCL? Нет, в HCL только желаемое описание. - Из API AWS? Нет, в AWS миллион бакетов, и какой из них «ваш». Terraform не знает. Ответ: **из state-файла**. После первого `apply` Terraform запомнил в `terraform.tfstate`: «Ресурс aws_s3_bucket.demo → реальный бакет с именем my-bucket-12345, ARN такой-то, создан тогда-то». Без этой записи Terraform каждый раз пытался бы создавать всё заново, и падал бы на «bucket already exists». ## Где живёт state По умолчанию, файл `terraform.tfstate` в той же директории, что HCL. Это называется **local backend**. ``` my-project/ ├── main.tf ├── terraform.tfstate # ← вот он └── terraform.tfstate.backup # копия с предыдущего apply ``` В реальных проектах state выносят в **remote backend**. S3, Terraform Cloud, и т.п. Чтобы команда могла работать с одним state'ом и не конфликтовать. См. [tf-init-backends](/terraform/kb/tf-init-backends.md). ## Что внутри Это JSON. Можно посмотреть руками, но не редактируйте: ```json { "version": 4, "terraform_version": "1.9.8", "serial": 5, "lineage": "abc-123-...", "resources": [ { "mode": "managed", "type": "aws_s3_bucket", "name": "demo", "instances": [ { "attributes": { "id": "my-bucket-12345", "bucket": "my-bucket-12345", "arn": "arn:aws:s3:::my-bucket-12345", "region": "us-east-1", "tags": { "Owner": "student" } } } ] } ] } ``` Ключевые поля: - `serial`, счётчик. Каждый apply увеличивает на 1. Для проверки актуальности. - `lineage`, уникальный UUID этого state'а. Защита от случайной подмены файла. - `resources`, массив всех ресурсов под управлением. Тип, имя, атрибуты. ## Команды для работы со state Сам файл руками **не** трогаем. Есть CLI: ```bash # Список всех ресурсов в state terraform state list # Показать атрибуты одного ресурса terraform state show aws_s3_bucket.demo # Целиком state в JSON (для скриптов) terraform show -json ``` Опасные (нужны в исключительных случаях): ```bash # Удалить ресурс из state (НЕ из облака) terraform state rm aws_s3_bucket.demo # Переименовать ресурс в state terraform state mv aws_s3_bucket.demo aws_s3_bucket.renamed # Импортировать существующий ресурс в state (resource уже в HCL описан) terraform import aws_s3_bucket.demo my-existing-bucket ``` Эти команды напрямую правят `terraform.tfstate`. Перед ними делайте бэкап файла. ## Drift, расхождение state и реальности State, это **снапшот того, что Terraform думает**. Реальность в облаке может уйти. Примеры drift'а: - Кто-то руками удалил бакет через AWS Console. - Auto-scaling сам изменил `desired_capacity`. - Включился ALB Auto-Scaling target. При следующем `plan` Terraform читает state и сравнивает с HCL. Если реальность отличается от state, `plan -refresh=true` (default) сначала обновит state из API, потом покажет diff. Это нормально, но требует внимания. См. [tf-resource-lifecycle](/terraform/kb/tf-resource-lifecycle.md) про `ignore_changes` для атрибутов, которые меняются вне Terraform. ## Секреты в state Это критично понять: **state содержит все атрибуты, включая чувствительные**. - Пароль БД (`aws_db_instance.password`): в state. - Токены, ключи, secret values, в state. - `sensitive = true` маскирует только в CLI-выводе. В JSON-файле, открытым текстом. Поэтому: - **Local state в git, нельзя.** Никогда. Даже если репозиторий приватный, рано или поздно утечёт. - **Local state на диске общего сервера, тоже плохо.** Любой sudo-user прочитает. - **Remote backend с шифрованием, обязательно для продакшена.** S3 + SSE-KMS, Terraform Cloud с шифрованием на стороне, и т.п. ## Lock, защита от одновременного apply Если два человека одновременно запустят `apply` на одном проекте, state может побиться. Решается **locking**: - **Local backend** не имеет lock'а. Это риск даже на одном компьютере (открыли два терминала). - **S3 backend + DynamoDB** для lock'а, стандарт. Один apply удерживает запись в DynamoDB, второй ждёт. - **Terraform Cloud** делает lock автоматически. При попытке параллельного apply вы увидите: ``` Error: Error acquiring the state lock Lock Info: ID: abc-123 Path: s3://my-bucket/terraform.tfstate Operation: OperationTypeApply Who: user@host Created: 2026-05-20 14:00:00 +0000 UTC ``` Если знаете, что другой процесс мёртв, `terraform force-unlock `. ## Подводные камни - **Не редактируйте state руками.** JSON выглядит простым, но Terraform проверяет внутренние инварианты (lineage, serial, hashes). Сломаете, будете долго чинить. - **Не коммитьте state в git.** Никогда. Даже local. Добавьте в `.gitignore`: ``` terraform.tfstate terraform.tfstate.backup .terraform/ .terraform.lock.hcl # ← а это, наоборот, коммитьте ``` - **State синхронизирован только с одной точкой времени.** Между apply'ями он не «следит» за облаком. Если хотите свежие данные, `terraform refresh` или `terraform plan` (тоже делает refresh). - **`state rm` не удаляет ресурс в облаке.** Только из state. Ресурс остаётся существовать «вне Terraform». Полезно при миграции, опасно при ошибке. - **`state mv` нужен при переименовании.** Если в HCL поменяли `"demo"` на `"main"`, без `state mv` Terraform пересоздаст ресурс. После `state mv`, только обновит запись, без обращения в облако. - **Lineage защищает от подмены, но можно сломать.** Если случайно перетёрли state копией из другого проекта, lineage не совпадёт. Terraform откажется работать. Это **защита, а не баг**. ## Команды ```bash terraform state list ``` Список всех ресурсов под управлением. Первая команда при разборе незнакомого проекта. ```bash terraform state show aws_s3_bucket.demo ``` Все атрибуты одного ресурса: то, что Terraform думает о нём. ```bash terraform show -json | jq '.values.root_module.resources' ``` Все ресурсы в state через jq: для скриптов и анализа. ```bash terraform refresh ``` Обновить state из реального облака. Полезно перед опасным plan'ом. ```bash terraform state pull > backup.tfstate ``` Скачать текущий state в файл (работает с любым backend). Бэкап перед опасными операциями. ## См. также - [terraform init: первая команда в любом проекте](/terraform/kb/tf-init.md) - [Backend в Terraform: где живёт state](/terraform/kb/tf-init-backends.md) - [terraform plan: посмотреть, что Terraform собирается сделать](/terraform/kb/tf-plan.md) - [terraform apply: применить план в реальном облаке](/terraform/kb/tf-apply.md) - [Блок resource: главный кирпич Terraform](/terraform/kb/tf-resource-block.md)