linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/kb/State/tf-remote-backend-s3

kb/state ── State ── intermediate

Remote state в S3: бакет, DynamoDB lock, encryption

S3-backend хранит `terraform.tfstate` в бакете, DynamoDB-таблица даёт блокировку одного-apply-за-раз. Конфигурация, в блоке `backend "s3"` внутри `terraform { ... }`. State в S3. Это единственный source of truth, локального файла больше нет. Переезд с local на S3, через `terraform init -migrate-state`.

view as markdownaka: terraform-s3-backend, terraform-remote-state-s3

Зачем remote state

Локальный state живёт рядом с HCL. Это работает пока ты один. Как только команда, начинаются проблемы:

  • Кто-то делает apply со своего ноутбука. State у него на диске. У второго коллеги, другой state. Они применяют разные планы к одной инфре. Хаос.
  • Ноутбук падает / форматируется, state потерян. Восстанавливать через terraform import каждый ресурс руками.
  • CI/CD не имеет state'а вообще. Каждый run думает что инфра не создана.

Remote backend, это общий state, доступный всем (по правам), с блокировками против параллельных apply. S3+DynamoDB. Это самая частая комбинация в AWS-проектах.

Архитектура

+----------+      +-----------+      +-----------+
| dev      |      | S3 bucket |      | DynamoDB  |
| laptop A | ───→ | tfstate   | ←─── | lock      |
+----------+      +-----------+      +-----------+
                     ↑                    ↑
                     │                    │
+----------+    плюс │            проверь │ перед
| dev      |     запись                   │ записью
| laptop B | ──────────────────────────────┘
+----------+
  • S3 хранит файл terraform.tfstate. Версионирование (S3 versioning) обязательно включить, это спасает от случайного перезаписи.
  • DynamoDB хранит lock-record. Перед write Terraform пишет lock, после apply удаляет. Если кто-то параллельно пытается, видит lock и падает с понятной ошибкой.
  • Encryption. S3 server-side encryption по умолчанию. Дополнительно можно AWS KMS со своим ключом для compliance.

Минимальная конфигурация backend

Создать ресурсы (S3 + DynamoDB) можно отдельным root-модулем «bootstrap» или руками через AWS CLI. Один раз. Потом сослаться:

hcl
terraform {
  backend "s3" {
    bucket         = "myorg-terraform-state"
    key            = "billing/prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "myorg-terraform-locks"
    encrypt        = true
  }
}

Ключевые поля:

ПолеЧто
bucketИмя S3-бакета для state'ов. Один бакет, много state'ов через разные key.
keyПуть внутри бакета. Конвенция: <проект>/<env>/terraform.tfstate.
regionРегион бакета (не путать с регионом ресурсов твоего HCL).
dynamodb_tableИмя DynamoDB-таблицы для lock. Hash key = LockID, тип string.
encryptSSE для state. Всегда true.
kms_key_idОпциональный KMS ключ. По умолчанию SSE-S3.

Backend нельзя интерполировать

hcl
# НЕ РАБОТАЕТ
terraform {
  backend "s3" {
    bucket = var.state_bucket   # ОШИБКА: переменные не разрешены
  }
}

Backend конфигурируется до того как Terraform читает variables. Если нужна параметризация между env, используй partial backend config:

hcl
# terraform { backend "s3" {} } , пустой блок

И передавай при init:

bash
terraform init -backend-config="bucket=myorg-terraform-state" \
               -backend-config="key=billing/prod/terraform.tfstate" \
               -backend-config="region=us-east-1" \
               -backend-config="dynamodb_table=myorg-terraform-locks"

Или через -backend-config=prod.hcl файл. Это стандартная техника для multi-env layout.

Bootstrap: создание самих бакета и таблицы

Курица-яйцо: чтобы state был в S3, S3-бакет должен существовать. Решений два:

Вариант 1: руками через AWS CLI

bash
aws s3api create-bucket --bucket myorg-terraform-state --region us-east-1
aws s3api put-bucket-versioning --bucket myorg-terraform-state \
    --versioning-configuration Status=Enabled
aws s3api put-bucket-encryption --bucket myorg-terraform-state \
    --server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
aws dynamodb create-table --table-name myorg-terraform-locks \
    --attribute-definitions AttributeName=LockID,AttributeType=S \
    --key-schema AttributeName=LockID,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST

Один раз на организацию. State этих ресурсов не хранится в terraform, они создаются руками, документируются, никто не трогает.

Вариант 2: отдельный bootstrap-модуль с local state

Один root-модуль создаёт бакет+таблицу через aws_s3_bucket+aws_dynamodb_table, его state лежит локально (или в git как bootstrap.tfstate, для аудита). Все остальные root'ы используют S3-backend.

Это аккуратнее (всё описано в HCL), но bootstrap-state нужно где-то держать и защищать. Часто кладут в отдельный приватный git-репо.

Миграция с local на S3

Был файл terraform.tfstate. Хотим перевести в S3.

  1. Добавь в HCL блок terraform { backend "s3" { ... } }.

  2. Запусти:

    bash
    terraform init -migrate-state

    Terraform спросит: «нашёл local state и новый remote backend, перенести?» Отвечаешь yes.

  3. Локальный terraform.tfstate остаётся на диске как terraform.tfstate.backup , не удаляй сразу, дай неделю на проверку.

Обратная миграция (с S3 на local): тот же init -migrate-state, только в HCL убрать backend блок. Тоже спросит подтверждение.

Locking в действии

Когда ты делаешь apply, Terraform пишет в DynamoDB:

LockID:    myorg-terraform-state/billing/prod/terraform.tfstate-md5
Operation: OperationTypeApply
Who:       alice@laptop
Created:   2026-05-26 14:00:00 UTC
Info:

Параллельный apply увидит lock и упадёт:

Error: Error acquiring the state lock
Lock Info:
  ID:        ...
  Operation: OperationTypeApply
  Who:       alice@laptop
  Created:   2026-05-26 14:00:00 UTC

См. tf-common-errors про Error acquiring the state lock.

force-unlock

Если процесс умер (Ctrl+C, краш), lock остался, terraform force-unlock <ID>. Опасно: если процесс жив и продолжает работу, ты разрешишь второй apply и сломаешь state. Используй только когда уверен.

Подводные камни

  • Без версионирования бакета, потерял state, потерял всю инфру. Случайный terraform state rm на root + последующий apply = пересоздание всего. Версионирование S3 даёт roll-back на предыдущую версию state. Обязательно.

  • Один S3-бакет на всю организацию. Не плодить «по бакету на проект», permissions сложнее, бухгалтерия запутанная. Один бакет, разные key. IAM-policy ограничивает кто к каким key может писать.

  • encrypt = true, это minimum. Для compliance. SSE-KMS с customer-managed key, чтобы доступ к state требовал KMS-permission отдельно от S3-permission. Двойной слой.

  • DynamoDB-таблица должна быть в том же регионе что бакет. Иначе блокировка работает медленнее (cross-region call). Best practice, оба в одном регионе, обычно «головном» региона организации.

  • LocalStack эмулирует S3-backend, но не идеально. В наших уроках мы используем LocalStack, это работает для обучения и интеграционных тестов, но в проде ты столкнёшься с реальным S3+DynamoDB. Поведение в edge cases (большой state, медленные сети, eventual consistency) отличается.

  • key нельзя интерполировать на стадии apply. Это часть backend config, разрешается на init. Если хочешь переключаться между prod и dev state'ами, две разные конфигурации init, или Terraform workspaces (см. tf-workspace).

  • Бекапы поверх S3-versioning не помешают. Versioning защищает от случайной перезаписи. Не защищает от удаления бакета. Cross-account backup через S3 replication или AWS Backup, для серьёзных проектов обязателен.

§ команды

bash
terraform init -migrate-state

Переехать со старого backend на новый (или наоборот). Спросит подтверждение.

bash
terraform init -backend-config=prod.hcl

Partial backend config. Используется когда parameters различаются между env.

bash
terraform force-unlock <LOCK_ID>

Снять lock вручную. Только когда уверен что процесс умер.

bash
aws s3 cp s3://myorg-terraform-state/billing/prod/terraform.tfstate - | jq .serial

Прочитать serial из state напрямую. Полезно для дебага 'кто последний писал'.

bash
aws dynamodb scan --table-name myorg-terraform-locks

Видно все активные lock'и. Если что-то висит давно: кандидат на force-unlock.

§ см. также

  • tf-stateState: память Terraform о созданномState. JSON-файл terraform.tfstate, где Terraform записывает, что он создал в облаке. Без него Terraform не знал бы, какой бакет «его», а какой чужой. Содержит ID ресурсов, все атрибуты, и часто секреты. Самая чувствительная часть проекта.
  • tf-init-backendsBackend в Terraform: где живёт stateBackend, это место хранения state-файла. По умолчанию local, рядом с HCL. Remote backends (S3, GCS, Terraform Cloud, http) дают совместный доступ и блокировку. В этом курсе используется только local; remote, обзорно.
  • tf-state-manipulationstate mv, state rm, state pull/push: ручные операции`terraform state mv` переименовывает адрес ресурса в state (без destroy/recreate). `terraform state rm` убирает ресурс из state (но не из облака). `terraform state pull/push`, скачать/залить state как файл. Все четыре, резкие операции, делать через backup и понимая зачем. Для декларативных альтернатив есть [[tf-moved-block]] и [[tf-removed-block]].
  • localstack-providerLocalStack: учебный AWS, который живёт в DockerLocalStack эмулирует AWS API локально, в Docker-контейнере. Terraform думает что работает с настоящим AWS, но никаких реальных ресурсов не создаётся и денег не тратится. Идеально для учёбы и тестов.
  • tf-lockfile.terraform.lock.hcl: фиксация версий провайдеровLockfile фиксирует точные версии провайдеров и их хеши, чтобы у тебя и у CI всегда стояла та же сборка. Создаётся при terraform init, обновляется через init -upgrade. Коммитится в git.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки