Первый цикл: write, plan, apply, destroy

Весь Terraform держится на четырёх командах: init готовит проект, plan показывает что изменится, apply применяет, destroy удаляет. Это цикл, который вы повторяете каждый день.

Никогда не запускайте apply, не прочитав вывод plan. План — это контракт: Terraform обещает сделать ровно то, что показал, и ничего больше.

Рабочий процесс Terraform всегда один и тот же. Сначала вы пишете HCL-код. Потом инициализируете проект, чтобы скачались провайдеры. Затем смотрите план — Terraform сравнивает код с реальностью и показывает предполагаемые изменения. И только если план устраивает — применяете.

Команды цикла

terraform init      # скачать провайдеры, подготовить рабочую папку
terraform fmt       # отформатировать код по канону
terraform validate  # проверить синтаксис без обращения к облаку
terraform plan      # показать предполагаемые изменения (dry-run)
terraform apply     # применить изменения к реальной инфраструктуре
terraform destroy   # удалить всё, что было создано

Самое простое окружение для старта — провайдер local, который создаёт файлы на диске без всякого облака. Вот полный пример, который можно запустить на любом ноутбуке:

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

resource "local_file" "hello" {
  filename = "${path.module}/hello.txt"
  content  = "Привет от Terraform!"
}
WRITE  ->  INIT  ->  PLAN  ->  APPLY  ->  (живёт)  ->  DESTROY
 .tf      плагины   diff     меняет        state       сносит
код      качаются  читаем   реальность   обновлён    ресурсы
  ^                                                       |
  +---------------- правим код и снова --------------------+

Как работает под капотом

terraform plan — это «сухой прогон». Он ничего не меняет, только считает diff между желаемым (код) и текущим (state + опрос реальности) состоянием и печатает символы: + создать, ~ изменить, - удалить, -/+ пересоздать. apply заново строит этот план и выполняет его. Смоделируем форматирование плана:

changes = [
    ("local_file.hello", "create"),
    ("aws_instance.web", "update"),
    ("aws_s3_bucket.old", "delete"),
    ("aws_db_instance.main", "replace"),
]
symbol = {"create": "+", "update": "~", "delete": "-", "replace": "-/+"}

print("Terraform создаст план:\n")
for name, action in changes:
    print(f"  {symbol[action]:>3} {name}  ({action})")

counts = {}
for _, a in changes:
    counts[a] = counts.get(a, 0) + 1
print("\nИтого:", ", ".join(f"{v} {k}" for k, v in counts.items()))

«Попробуй сам ▶» — именно так выглядит сводка Plan: 1 to add, 1 to change, ... внизу реального вывода.

Частые ошибки

  • Забыть init. Без него провайдеры не скачаны и любая команда падает.
  • Применять не глядя. apply без чтения плана однажды снесёт прод. Особенно опасны строки -/+ (пересоздание).
  • Запускать destroy в проде «чтобы почистить». Эта команда удаляет ВСЁ из текущего state без жалости.

Best practices

  • В CI прогоняйте fmt -check и validate до plan — это ловит ошибки бесплатно.
  • Сохраняйте план в файл: terraform plan -out=tfplan, затем apply tfplan — так apply применит именно то, что вы видели.
  • В проде требуйте ручного подтверждения plan через pull request, а не локальный apply на ноутбуке.

Разбор глубже

Разберём, что физически происходит при terraform init. Terraform читает блоки required_providers, обращается к Registry, скачивает нужные плагины в скрытую папку .terraform/ и записывает их точные версии и контрольные суммы в файл .terraform.lock.hcl. Этот lock-файл — как package-lock.json в мире Node: он гарантирует, что у вас, у коллеги и в CI окажется ровно один и тот же набор провайдеров. Поэтому lock-файл коммитят в git, а папку .terraform/ — нет.

Важно прочувствовать разницу между plan и apply на уровне безопасности. plan не делает ни одного изменяющего вызова к API облака — только чтение. Его можно запускать сколько угодно раз, в том числе автоматически на каждый коммит, без всякого риска. apply же выполняет изменяющие операции (create, update, delete) и потому требует осторожности. Именно поэтому в зрелых командах plan гоняют свободно и часто, а apply — только через защищённый процесс с ревью. Команда destroy — это, по сути, apply с планом «удалить всё»; в проде её закрывают защитами вроде prevent_destroy.

Итог: цикл write → init → plan → apply (→ destroy) — основа всей работы. plan безопасен и показывает diff; apply применяет его. Дальше разберём, на каком языке этот код пишется.

Проверьте себя
1. Что делает команда terraform plan?
AСразу создаёт ресурсы
BПоказывает diff между кодом и реальностью, ничего не меняя
CУдаляет инфраструктуру
DСкачивает провайдеры
2. Что означает символ -/+ в выводе плана?
AРесурс будет обновлён на месте
BРесурс будет пересоздан (удалён и создан заново)
CОшибка синтаксиса
DРесурс не изменится