Что делает validate
terraform validate отвечает на простой вопрос: скомпилируется ли
ваш HCL вообще, если бы его попытались выполнить? Без обращения к
AWS, без чтения state, без скачивания provider'ов (если они уже
скачаны).
Конкретно validate ловит:
- Битый синтаксис HCL: незакрытая скобка, потерянная кавычка.
- Неизвестные аргументы:
bucket_nameвместоbucketуaws_s3_bucket. - Неправильные типы:
count = "три"(нужно число). - Ссылки на несуществующие ресурсы/переменные/locals.
- Циклы в зависимостях (cycle detected).
- Неправильную сигнатуру функций:
format()без аргументов.
Что validate не ловит:
- Что бакет с таким именем уже занят (это plan/apply).
- Что у тебя нет прав в AWS (это plan/apply).
- Что drift в state (это refresh/plan).
- Что результат
dataбудет пустым.
Базовое использование
cd ~/myproject
terraform init -backend=false # один раз, скачать provider
terraform validate
Успех:
Success! The configuration is valid.
Ошибка:
Error: Unsupported argument
on main.tf line 5, in resource "aws_s3_bucket" "demo":
5: bucket_name = "my-bucket"
An argument named "bucket_name" is not expected here.
Did you mean "bucket"?
Заметьте. Terraform даёт подсказку через "Did you mean". Это
работает на похожих именах: bucket_name → bucket, tag → tags.
Зачем нужен init -backend=false
Чтобы валидировать, нужны provider'ы, без них Terraform не знает
схемы ресурсов. Но terraform init без флагов попытается
настроить backend (если он remote, пойдёт в S3, спросит credentials).
В CI на каждом PR это не нужно. Поэтому:
terraform init -backend=false -input=false
terraform validate
-backend=false, не настраивать backend, не подключаться к remote state.-input=false, не задавать интерактивных вопросов (CI ничего не введёт).
Это типичный паттерн для PR-ревью: «проверим что HCL валиден, без доступа к prod-state».
Машинно-читаемый вывод
Для IDE-интеграций и автоматизации есть -json:
terraform validate -json
Вывод:
{"format_version": "1.0",
"valid": false,
"error_count": 1,
"warning_count": 0,
"diagnostics": [
{"severity": "error",
"summary": "Unsupported argument",
"detail": "An argument named \"bucket_name\" is not expected here.",
"range": {"filename": "main.tf",
"start": {"line": 5, "column": 3}, "end": {"line": 5, "column": 14}}
}
]
}
Это парсится в любом IDE и подсвечивается прямо в редакторе. Все Terraform-плагины (VS Code, JetBrains) под капотом гоняют этот же JSON.
Чем validate отличается от plan
| validate | plan | |
|---|---|---|
| Ходит в облако | нет | да (refresh) |
| Читает state | нет | да |
| Скачивает provider | да (через init) | да |
| Скорость | секунда | от секунд до минут |
| Ловит «неправильно написано» | да | да |
| Ловит «такого ресурса нет в облаке» | нет | да |
| Можно ли запустить в PR без AWS-credentials | да | нет |
Правило большого пальца: validate, на каждое сохранение в IDE. Plan, на каждый PR (с credentials).
Подводные камни
-
Validate требует
initхотя бы раз. Без.terraform/providers/в директории validate не знает схемы ресурсов и упадёт с «provider not configured». В свежей CI-сборке нуженinit -backend=falseперед каждым validate. -
Validate проверяет один root-module. Если в репозитории
environments/prod/,environments/staging/. Нужно валидировать каждый отдельно. Один общий validate из корня всё не покроет. -
Validate не подставляет переменные. Если в HCL
var.foo, но переменная не объявлена, это ошибка. Если объявлена и нетdefault, validate пройдёт, потому что значение может прийти в runtime. -
Validate не ловит логические ошибки.
count = var.enabled ? 1 : 0пройдёт, даже еслиvar.enabledвсегда false: валидное выражение, просто бесполезное. -
Между версиями TF меняется строгость. В TF 1.5+ validate стал жёстче к provider-schema несовместимостям. После апгрейда TF, проверьте validate на ваших старых проектах.