Входные переменные: variable и валидация
Переменные (variable) превращают жёстко прописанный конфиг в гибкий шаблон: один и тот же код запускается для dev и prod, отличаясь лишь значениями на входе.
Хороший Terraform-код не содержит ни одного захардкоженного региона, размера или CIDR внутри логики. Всё, что меняется между окружениями, — это входная переменная.
Блок variable объявляет параметр. У него есть тип (string, number, bool, list, map, object), необязательное значение по умолчанию, описание и флаг sensitive для секретов. Внутри кода переменная доступна как var.имя.
Объявление и валидация
variable "instance_type" {
type = string
default = "t2.micro"
description = "Тип EC2-инстанса"
}
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment должен быть dev, staging или prod."
}
}
variable "db_password" {
type = string
sensitive = true # не покажется в выводе plan/apply
}
Блок validation позволяет проверить значение и выдать понятную ошибку до создания ресурсов. sensitive = true прячет значение из логов — но не из state-файла (об этом в разделе про state).
Как работает под капотом
Прежде чем строить план, Terraform собирает значения переменных и проверяет каждую через её validation-условия. Смоделируем этот сборщик-валидатор: значения берутся с приоритетом (CLI > tfvars > default), затем проверяются.
definitions = {
"environment": {"validate": lambda v: v in ("dev", "staging", "prod")},
"instance_type": {"default": "t2.micro", "validate": lambda v: True},
}
tfvars = {"environment": "prod"} # из файла
cli = {"instance_type": "t3.large"} # из -var, высший приоритет
def resolve(defs, tfvars, cli):
final, errors = {}, []
for name, spec in defs.items():
if name in cli: value = cli[name]
elif name in tfvars: value = tfvars[name]
elif "default" in spec: value = spec["default"]
else:
errors.append(f"{name}: значение не задано!"); continue
if not spec["validate"](value):
errors.append(f"{name}={value!r}: не прошло валидацию")
final[name] = value
return final, errors
vals, errs = resolve(definitions, tfvars, cli)
print("Итоговые значения:", vals)
print("Ошибки:", errs or "нет")
«Попробуй сам ▶» — CLI перебивает tfvars, tfvars перебивает default. Поменяйте environment на "qa" — увидите ошибку валидации.
Частые ошибки
- Default для секретов. Пароль с
defaultв коде — это секрет в git. Секреты не должны иметь дефолтов. - Отсутствие типов. Без
typeTerraform не поймает, что вы передали строку вместо числа. - Думать, что
sensitive= шифрование. Он лишь прячет значение из вывода CLI; в state оно лежит открытым текстом.
Best practices
- Всегда указывайте
typeиdescription— это и проверка, и документация модуля. - Используйте
validationдля бизнес-правил (допустимые регионы, диапазоны, форматы). - Секреты передавайте через переменные окружения или менеджер секретов, а не дефолты и не tfvars в git.
Разбор глубже
Система типов переменных мощнее, чем кажется на первый взгляд. Кроме простых string и number, HCL поддерживает структурные типы: object({ name = string, size = number }) описывает объект с фиксированным набором полей, а list(object(...)) — список таких объектов. Это позволяет передавать в модуль сложные конфигурации одной переменной и при этом получать строгую проверку: если потребитель забыл поле или указал неверный тип, Terraform поймает это ещё до создания ресурсов. Хорошо типизированная переменная — это и валидация, и живая документация интерфейса модуля.
Стоит ещё раз подчеркнуть разницу между sensitive и реальной секретностью. Флаг sensitive = true решает ровно одну задачу — не печатать значение в выводе plan и apply, чтобы пароль не утёк в логи CI или историю терминала. Но в state-файле это значение по-прежнему лежит открытым текстом, и любой, у кого есть доступ к state, его прочитает. Поэтому настоящая защита секретов — это контроль доступа к удалённому state, его шифрование и получение секретов из внешнего менеджера (Vault, AWS Secrets Manager) во время выполнения, а не хранение их в коде или tfvars.
Итог: переменные параметризуют конфиг; типы и validation ловят ошибки рано, sensitive прячет секреты из логов. Дальше — как именно передавать им значения.