Функции, выражения и условная логика
HCL — не Turing-полный язык, но в нём есть всё для гибкой конфигурации: встроенные функции, тернарный оператор, выражения-циклы for и динамические блоки.
В HCL нет переменных-присваиваний и циклов в привычном смысле. Вместо них — выражения, которые вычисляются в значение. Это сознательное ограничение: конфигурация должна оставаться предсказуемой.
Terraform даёт сотни встроенных функций: строковые (upper, replace, format), коллекций (length, merge, concat, lookup), числовые, сетевые (cidrsubnet), кодирующие (jsonencode, base64encode). Своих функций писать нельзя — только использовать готовые. Это держит язык простым.
Условия и циклы-выражения
# тернарный оператор
instance_type = var.env == "prod" ? "t3.large" : "t2.micro"
# for-выражение: преобразовать список
locals {
upper_names = [for n in var.names : upper(n)]
# мапа из списка
by_id = { for s in var.servers : s.id => s.name }
}
# dynamic-блок: генерировать вложенные блоки
dynamic "ingress" {
for_each = var.ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
}
}
Тернарный оператор условие ? a : b заменяет if. for-выражение преобразует одну коллекцию в другую (список → список или список → мапа). dynamic-блок генерирует повторяющиеся вложенные блоки — например, набор правил файрвола из списка портов.
Как работает под капотом
for-выражение в HCL — это, по сути, list/dict comprehension из Python. Смоделируем три типичных преобразования, которые вы будете писать постоянно:
servers = [
{"name": "web", "env": "prod"},
{"name": "api", "env": "prod"},
{"name": "test", "env": "dev"},
]
# [for s in servers : upper(s.name)]
names_upper = [s["name"].upper() for s in servers]
print("Список имён:", names_upper)
# { for s in servers : s.name => s.env }
env_by_name = {s["name"]: s["env"] for s in servers}
print("Мапа имя->env:", env_by_name)
# фильтрация: [for s in servers : s.name if s.env == "prod"]
prod_only = [s["name"] for s in servers if s["env"] == "prod"]
print("Только prod:", prod_only)
«Попробуй сам ▶» — синтаксис HCL почти дословно повторяет эти три строки. for-выражение с if умеет и фильтровать.
Частые ошибки
- Вложенные тернарники.
a ? b : c ? d : eнечитаемы; вынесите логику вlocalsили мапу-словарь. - Путать
[]и{}в for-выражении. Квадратные скобки дают список, фигурные с=>— мапу. - Логика вместо данных. Если конфиг превращается в дерево условий — вынесите варианты в переменные-мапы (
lookup(var.sizes, var.env)).
Best practices
- Сложные выражения выносите в блок
locals— это именованные промежуточные значения, читаемые и переиспользуемые. - Используйте
lookupи мапы вместо длинных цепочек тернарников. dynamic-блоки применяйте умеренно: они мощные, но затрудняют чтение. Иногда явные блоки понятнее.
Разбор глубже
Среди функций есть категория, которую используют чаще всего, — функции для работы со значениями по умолчанию и проверками. Функция try(expr1, expr2, ...) возвращает первое выражение, которое вычислилось без ошибки, — это удобно, когда структура данных может не содержать какого-то поля. Функция coalesce(a, b, c) возвращает первое непустое значение, а lookup(map, key, default) достаёт значение из мапы с запасным вариантом. Вместе они избавляют от громоздких проверок на null и делают конфигурацию устойчивой к отсутствующим данным.
Важно понимать философию ограничений HCL. Отсутствие циклов while и рекурсии — это не недоработка, а защита: конфигурация инфраструктуры должна вычисляться за конечное предсказуемое число шагов, без риска зависнуть. Всё, что нужно для генерации повторяющихся структур, дают for-выражения и dynamic-блоки, а вся «настоящая» логика выносится либо в значения переменных, либо во внешние data-источники. Этот аскетизм — сознательная цена за то, что план всегда детерминирован и его результат можно безопасно показать в ревью.
На практике мощнее всего оказывается связка for-выражений с функциями преобразования коллекций. Типичный приём — превратить список объектов в мапу по ключу через { for x in list : x.name => x }, чтобы потом удобно скармливать её в for_each. Другой частый паттерн — функция flatten, разворачивающая вложенные списки в плоский, что незаменимо, когда из конфигурации с группами и подгруппами нужно получить единый список правил. Освоив десяток таких идиом, вы перестанете писать громоздкие конструкции и начнёте выражать намерение коротко и читаемо — именно так выглядит зрелый код на HCL.
Итог: HCL даёт функции, тернарники, for-выражения и dynamic-блоки — этого хватает для гибкости без полноценного программирования. Выносите сложное в locals. Дальше — как параметризовать конфигурацию переменными.