Первый цикл: 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 применяет его. Дальше разберём, на каком языке этот код пишется.