Это седьмая глава, так что HCL ты уже читаешь, terraform plan и terraform apply запускал. Теперь добавим в этот скелет нервную систему - параметры, которые меняют поведение без переписывания тела.
Переменные ввода: variable
Переменная объявляется блоком variable. У него есть имя и набор аргументов, из которых тебе реально пригодятся пять: type, default, description, validation и sensitive.
Код: Выделить всё
variable "vm_name" {
type = string
description = "Имя виртуальной машины в Yandex Cloud"
default = "web-1"
}
variable "cores" {
type = number
default = 2
}
variable "preemptible" {
type = bool
default = true
}
Код: Выделить всё
variable "zones" {
type = list(string)
default = ["ru-central1-a", "ru-central1-b"]
}
variable "labels" {
type = map(string)
default = {
env = "dev"
owner = "khovanskiy"
}
}
variable "vm_spec" {
type = object({
cores = number
memory = number
})
default = {
cores = 2
memory = 4
}
}
default - значение по умолчанию. Если оно есть, переменная необязательная. Если default не задан, Terraform потребует значение при каждом запуске и спросит интерактивно, что в автоматизации обычно нежелательно.
description - человеческое описание. Кажется мелочью, но через месяц именно оно объяснит будущему тебе, зачем нужна эта переменная. В команде это вообще обязательный минимум вежливости.
validation - проверка значения своими правилами. Например, не дадим выставить меньше одного ядра:
Код: Выделить всё
variable "cores" {
type = number
description = "Число vCPU"
default = 2
validation {
condition = var.cores >= 1 && var.cores <= 32
error_message = "Ядер должно быть от 1 до 32."
}
}
sensitive = true помечает переменную как чувствительную. Пароль, токен, приватный ключ. Terraform перестанет печатать её значение в выводе plan и apply, показывая (sensitive value). Важно понимать: в файле состояния значение всё равно лежит в открытом виде, sensitive прячет его только из консоли и логов, а не из terraform state. Защита state - отдельная тема, тут лишь гигиена вывода.

Как передать значения переменным
Объявить переменную мало, ей надо дать значение. Способов несколько, и у них есть приоритет - кто перебьёт кого.
- default в блоке variable - самый слабый, базовый уровень.
- Файлы *.tfvars - terraform.tfvars и любые *.auto.tfvars подхватываются автоматически, остальные подключаешь флагом -var-file. Это основной способ хранить наборы значений под разные окружения.
- Переменные окружения вида TF_VAR_имя. Terraform читает TF_VAR_cores как значение переменной cores. Удобно для секретов в CI, чтобы не светить их в файлах.
- Флаг -var в командной строке - точечное переопределение, перебивает почти всё.
Код: Выделить всё
vm_name = "web-prod"
cores = 4
preemptible = false
zones = ["ru-central1-a", "ru-central1-d"]
Код: Выделить всё
export TF_VAR_folder_id="b1g***"
terraform plan -var-file="prod.tfvars" -var="cores=8"
Интерполяция и ссылки на переменные
Внутри конфигурации к переменной обращаешься через var.имя. Раньше любую подстановку оборачивали в синтаксис интерполяции ${...}, сегодня в чистых выражениях он не нужен - пишешь var.vm_name напрямую. Конструкция ${} осталась нужна только когда вставляешь значение внутрь строки:
Код: Выделить всё
metadata = {
hostname = "${var.vm_name}.internal"
}
Выходные значения: output
Apply отработал, ресурсы созданы. Но как узнать публичный IP машины, не лазая в веб-консоль? Для этого есть output - значения, которые Terraform печатает после apply и хранит в состоянии.
Код: Выделить всё
output "external_ip" {
description = "Публичный IP веб-сервера"
value = yandex_compute_instance.web.network_interface[0].nat_ip_address
}
output "fqdn" {
value = yandex_compute_instance.web.fqdn
}
Если output отдаёт секрет, помечай его sensitive = true, иначе Terraform напечатает его прямым текстом в конце apply:
Код: Выделить всё
output "db_password" {
value = random_password.db.result
sensitive = true
}
Локальные значения: locals
Иногда нужно промежуточное вычисление, которое не должно настраиваться снаружи. Для этого есть locals - именованные выражения внутри конфига. Ссылаешься на них через local.имя (без s, в отличие от блока locals).
Код: Выделить всё
locals {
name_prefix = "${var.project}-${var.env}"
common_labels = {
project = var.project
env = var.env
managed = "terraform"
}
}
Практика: рефакторим веб-сервер на переменные
Соберём всё вместе на нашей знакомой ВМ из прошлых глав. Было захардкожено - стало настраиваемо.
Код: Выделить всё
variable "project" { type = string, default = "cyberlake" }
variable "env" { type = string, default = "dev" }
variable "zone" { type = string, default = "ru-central1-a" }
variable "image_id" { type = string }
variable "vm_spec" {
type = object({
cores = number
memory = number
})
default = { cores = 2, memory = 2 }
}
locals {
vm_name = "${var.project}-web-${var.env}"
}
resource "yandex_compute_instance" "web" {
name = local.vm_name
platform_id = "standard-v3"
zone = var.zone
resources {
cores = var.vm_spec.cores
memory = var.vm_spec.memory
}
boot_disk {
initialize_params {
image_id = var.image_id
size = 10
}
}
network_interface {
subnet_id = yandex_vpc_subnet.web.id
nat = true
}
labels = {
project = var.project
env = var.env
}
}
output "web_external_ip" {
value = yandex_compute_instance.web.network_interface[0].nat_ip_address
}
output "web_fqdn" {
value = yandex_compute_instance.web.fqdn
}
Типичные грабли
- Секрет в default. Не клади пароль или токен в default переменной и не коммить tfvars с секретами. *.tfvars сразу в .gitignore, секреты - через TF_VAR_ в CI.
- Думаешь, sensitive шифрует. Нет. Он лишь прячет значение из вывода консоли. В terraform state оно лежит как есть, защищай state отдельно (удалённый бекенд, шифрование).
- Перепутал var и local. var.x - переменная ввода, local.x - локальное значение. Блок называется locals, а ссылка - local в единственном числе. Классическая опечатка.
- Лишние ${} вокруг всего выражения. name = "${var.vm_name}" работает, но это лишний шум. Пиши name = var.vm_name, скобки только для подстановки внутрь строки.
- Список вместо одного значения. network_interface это блок, у атрибутов обращайся через индекс: network_interface[0].nat_ip_address, а не просто .nat_ip_address.
Мини-лаба
Повтори руками за 15 минут:
- Вынеси из своего конфига ВМ имя, зону и количество ядер в три переменные с type и description.
- Добавь validation на cores: разреши только значения от 1 до 8.
- Создай dev.tfvars и prod.tfvars с разными значениями, прогони terraform plan с каждым через -var-file и сравни план.
- Добавь два output: внешний IP и fqdn. Сделай apply (или хотя бы plan) и убедись, что значения видны.
- Заведи один local name_prefix и собери из него имя ВМ.
- Чем variable отличается от local и когда выбираешь каждый?
- Какой источник значения переменной сильнее: default, файл *.tfvars или флаг -var?
- Что на самом деле делает sensitive = true и от чего он НЕ защищает?
- Когда конструкция ${} обязательна, а когда её надо убрать?
Переменные ввода превращают жёсткий конфиг в настраиваемый шаблон - база гибкого iac. variable - вход снаружи с type, default, description, validation и sensitive. Значения летят из default, *.tfvars, TF_VAR_ и -var, и чем ближе источник к команде, тем он сильнее. output отдаёт результаты после apply - внешний IP, fqdn, секреты под sensitive. locals - промежуточные вычисления внутри конфига. К переменной обращаешься через var.имя, к локали через local.имя, ${} нужен только для подстановки в строку. Освоил это - считай, ты на шаг от полноценных модулей terraform.