Выражения for и строковые директивы

Рейтинг: 55.2% · 12 голосов
Подробный курс по Terraform и OpenTofu на примерах Yandex Cloud: ресурсы и провайдеры, состояние и бэкенды, модули, циклы и условия HCL, секреты и Lockbox, тестирование, CI/CD и командная работа. С актуализацией на 2026 (Terraform 1.3-1.10, OpenTofu).
Ответить
Аватара пользователя
Vlad_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Выражения for и строковые директивы

Сообщение Vlad_DevOps »

Оглавление курса (47)
  1. Что такое инфраструктура как код и зачем она нужна
  2. Категории инструментов IaC: чем отличаются Ansible, Packer, Kubernetes и Terraform
  3. Чем Terraform отличается от Ansible, Pulumi и CloudFormation
  4. Как работает Terraform под капотом и кто такой OpenTofu
  5. Установка Terraform и OpenTofu, подготовка Yandex Cloud
  6. Первый ресурс: разворачиваем виртуальную машину в Yandex Cloud
  7. Веб-сервер на ВМ: метаданные, user-data и группы безопасности
  8. Переменные ввода и выходные значения в Terraform
  9. Кластер ВМ: группа экземпляров Yandex Compute Instance Group
  10. Балансировщик нагрузки: Application Load Balancer в Yandex Cloud
  11. Состояние Terraform: что такое tfstate и чем опасен локальный файл
  12. Удалённое хранилище состояния: бэкенд на Yandex Object Storage
  13. Изоляция состояния: рабочие области против раскладки по папкам
  14. Источники данных и terraform_remote_state: связываем компоненты
  15. Модули Terraform: переиспользуем инфраструктуру
  16. Входные, локальные и выходные переменные модуля
  17. Версионирование модулей и подводные камни
  18. Циклы в Terraform: параметр count
  19. Циклы в Terraform: for_each по множествам и картам
  20. Выражения for и строковые директивы (вы здесь)
  21. Условная логика: count-трюк, for_each и директива if
  22. Встроенные функции, типы и выражения HCL
  23. Развёртывание без простоя: create_before_destroy
  24. Подводные камни Terraform и рефакторинг с блоком moved
  25. Управление секретами: основы и типы
  26. Инструменты управления секретами: Yandex Lockbox, Vault, SOPS
  27. Аутентификация провайдера и секреты: сервисные аккаунты и OIDC
  28. Секреты в ресурсах и состоянии, ephemeral-значения
  29. Провайдеры Terraform: установка, версии, required_providers
  30. Несколько копий одного провайдера: alias и мультизона
  31. Несколько провайдеров и аккаунтов: configuration_aliases
  32. Terraform, Docker и Kubernetes: Managed Kubernetes в Yandex Cloud
  33. Код Terraform промышленного уровня: почему это долго
  34. Мелкие компонуемые модули вместо монолита
  35. Тестируемые модули и проверки: validation, precondition, postcondition
  36. Версионирование, публикация модулей и Terragrunt
  37. Ручное тестирование Terraform и очистка ресурсов
  38. Автоматические тесты на Terratest: модульные тесты
  39. Интеграционные и сквозные тесты, пирамида тестирования
  40. Нативный terraform test и статический анализ
  41. Внедрение Terraform в команде: процессы и культура
  42. Конвейер развёртывания прикладного кода
  43. Конвейер инфраструктурного кода и золотое правило Terraform
  44. Стиль, ревью и CI/CD-инструменты: Atlantis, TACOS, policy as code
  45. Что нового в Terraform 1.3-1.10: моды, импорт, провайдер-функции, Stacks
  46. OpenTofu в 2026: чем живёт форк и как мигрировать
  47. Сертификация HashiCorp Terraform Associate и карьера
Зачем тебе for и шаблоны

Рано или поздно в любом проекте на Terraform случается момент, когда ты копипастишь блок ресурса в пятый раз, меняя в нем одно слово. Три виртуалки, пять правил фаервола, восемь записей в DNS - и вот ты уже сидишь и руками правишь почти одинаковые куски HCL. Это скучно, это ломается при первой же опечатке, и это полностью противоречит идее инфраструктура как код, ради которой мы вообще взялись за terraform.

В этом уроке разберем два инструмента, которые превращают копипасту в нормальный код. Первый - выражение for: оно берет список или карту и перегоняет в другой список или карту, по дороге трансформируя и фильтруя элементы. Второй - строковые директивы внутри шаблонов: это циклы и условия прямо в тексте, которыми удобно собирать cloud-init, скрипты и конфиги. Плюс функция templatefile(), которая склеивает шаблон с данными. Все это - чистый язык Terraform, никакой магии и никакой отдельной доки провайдера тут не нужно.

Изображение

Выражение for: трансформация списков и карт

Идея проста до неприличия. У тебя есть коллекция, ты хочешь получить из нее новую коллекцию, где каждый элемент как-то изменен. В обычном языке ты бы написал цикл, в Terraform для этого есть выражение for.

Если результат обернуть в квадратные скобки - получишь список. Если в фигурные - карту (map). Вот базовый пример с приведением имен зон к верхнему регистру:

Код: Выделить всё

variable "zones" {
  type    = list(string)
  default = ["ru-central1-a", "ru-central1-b", "ru-central1-d"]
}

locals {
  zones_upper = [for z in var.zones : upper(z)]
  # => ["RU-CENTRAL1-A", "RU-CENTRAL1-B", "RU-CENTRAL1-D"]
}
Здесь z - это временная переменная итерации (название любое), а выражение после двоеточия применяется к каждому элементу. На выходе - новый список.

С картами интереснее: при переборе можно достать и ключ, и значение, а в результат отдать пару через стрелку k => v. Допустим, у нас есть карта инстансов с их параметрами, а нам нужна карта "имя -> размер диска":

Код: Выделить всё

locals {
  instances = {
    web = { cores = 2, disk = 20 }
    db  = { cores = 4, disk = 100 }
  }

  disks = { for name, cfg in local.instances : name => cfg.disk }
  # => { web = 20, db = 100 }
}
Самое вкусное - условие if в конце. Оно работает как фильтр: элемент попадает в результат, только если условие истинно. Соберем список имен только тех машин, у кого больше двух ядер:

Код: Выделить всё

locals {
  big_machines = [
    for name, cfg in local.instances : name
    if cfg.cores > 2
  ]
  # => ["db"]
}
For удобно комбинировать с другими функциями: toset(), keys(), values(), а еще он отлично кормит meta-аргумент for_each у ресурсов. По сути for - это твой швейцарский нож для перекладывания данных из одной формы в другую.

Строковые директивы и heredoc

Теперь вторая половина урока. Внутри строк Terraform умеет не только подставлять значения через ${...}, но и выполнять директивы через %{...}. Это мини-язык управления прямо в тексте: условие %{ if ... } %{ else } %{ endif } и цикл %{ for x in list } ... %{ endfor }.

Когда строка длинная и многострочная, ее удобно писать через heredoc - синтаксис с <<- и меткой. Это идеальный формат для cloud-init и bash-скриптов. Соберем кусок конфигурации, где список пакетов разворачивается циклом:

Код: Выделить всё

variable "packages" {
  type    = list(string)
  default = ["nginx", "git", "htop"]
}

locals {
  cloud_init = <<-EOT
    #cloud-config
    packages:
    %{ for pkg in var.packages ~}
      - ${pkg}
    %{ endfor ~}
    runcmd:
      - [ systemctl, enable, nginx ]
  EOT
}
Обрати внимание на тильду ~ перед закрывающей скобкой директивы - она съедает лишние пробелы и переводы строки, чтобы YAML не разъехался по отступам. Это первое, обо что спотыкаются все новички. Условие в строке выглядит так же естественно:

Код: Выделить всё

locals {
  motd = <<-EOT
    Welcome!
    %{ if var.env == "prod" ~}
    ВНИМАНИЕ: это боевой сервер, думай головой.
    %{ endif ~}
  EOT
}
templatefile() и сборка user-data

Держать большие шаблоны прямо в .tf-файле быстро становится больно. Функция templatefile(path, vars) выносит шаблон в отдельный файл и подставляет в него переменные. Внутри файла работают те же ${...} и %{...}, что и в heredoc.

Делаем файл templates/cloud-init.yaml.tftpl:

Код: Выделить всё

#cloud-config
hostname: ${hostname}
users:
  - name: ${user}
    sudo: ALL=(ALL) NOPASSWD:ALL
packages:
%{ for pkg in packages ~}
  - ${pkg}
%{ endfor ~}
runcmd:
%{ for cmd in commands ~}
  - ${cmd}
%{ endfor ~}
И подключаем его к виртуалке Yandex Cloud через метаданные user-data:

Код: Выделить всё

resource "yandex_compute_instance" "vm" {
  name        = "app-1"
  platform_id = "standard-v3"
  zone        = "ru-central1-a"

  resources {
    cores  = 2
    memory = 2
  }

  boot_disk {
    initialize_params {
      image_id = "fd80mrhj8fl2oe87o4e1"
    }
  }

  network_interface {
    subnet_id = yandex_vpc_subnet.subnet.id
    nat       = true
  }

  metadata = {
    user-data = templatefile("${path.module}/templates/cloud-init.yaml.tftpl", {
      hostname = "app-1"
      user     = "yc-user"
      packages = ["nginx", "git", "htop"]
      commands = ["systemctl enable nginx", "systemctl start nginx"]
    })
  }
}
Теперь шаблон один, а машин на нем можешь поднять хоть десять - меняя только значения переменных. Это и есть нормальный подход: данные отдельно, логика рендеринга отдельно. Кстати, templatefile никак не зависит от провайдера - точно так же это работает в opentofu, форке Terraform, синтаксис языка у них общий.

Типичные грабли
  • Забытая тильда. Без ~ директива оставляет пустые строки и отступы. Для YAML и cloud-init это смертельно - конфиг просто не распарсится на загрузке. Всегда ставь ~ там, где важна структура.
  • Доллар и проценты в bash-скрипте. Если внутри шаблона есть настоящий ${VAR} баша, Terraform примет его за свою интерполяцию и упадет. Экранируй удвоением: $${VAR} и %%{ соответственно.
  • Путь без path.module. templatefile() ищет файл относительно рабочей директории, а не модуля. Всегда пиши ${path.module}/..., иначе при вызове модуля из другого места все развалится.
  • Сложная логика в шаблоне. Директивы - это не полноценный язык программирования. Если в шаблоне начинаются вложенные циклы с условиями в три этажа, лучше подготовь данные заранее выражением for в locals, а в шаблон отдай уже готовый плоский список.
Мини-лаба
  • Заведи переменную-список из 3-4 пакетов и через for в верхнем регистре собери из нее новый список. Проверь результат через terraform console.
  • Сделай карту инстансов (имя -> число ядер) и выражением for с if отфильтруй только те, где ядер 2 и больше.
  • Напиши heredoc cloud-config, который циклом %{ for } разворачивает список пакетов. Поиграй с тильдой: посмотри в terraform plan, как меняется вывод с ~ и без.
  • Вынеси этот шаблон в .tftpl-файл и подключи через templatefile() в metadata.user-data у yandex_compute_instance. Прогони plan и убедись, что user-data собирается как ты ждешь.
Контрольные вопросы
  • Чем отличается [for x in list : ...] от {for k, v in map : k => ...} по типу результата?
  • Что делает if в конце выражения for и куда его ставить?
  • Зачем нужна тильда ~ в строковой директиве %{ for ... ~}?
  • Как заэкранировать настоящий ${VAR} баша внутри шаблона, чтобы Terraform его не трогал?
Итог: что запомнить

For - твой главный инструмент трансформации: квадратные скобки дают список, фигурные со стрелкой - карту, if в конце фильтрует. Строковые директивы %{ for } и %{ if } собирают многострочные конфиги прямо в тексте, а тильда спасает отступы. templatefile() выносит шаблон в файл и кормит его переменными - так рождается чистый user-data для виртуалок Yandex Cloud. Готовь данные через for в locals, а рендери через шаблоны - и копипаста в твоем terraform state наконец-то закончится.
👍2 ❤️4 🔥1 😄 🤔1
Аватара пользователя
terraform_coder
Сообщения: 1
Зарегистрирован: 24 май 2026, 20:39

Re: Выражения for и строковые директивы

Сообщение terraform_coder »

Спасибо, наконец понял зачем эта стрелка k => v в фигурных скобках. А подскажите, тильду надо ставить только перед endfor или у обеих директив? У меня yaml все равно разъезжается.
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
Richie87
Сообщения: 1
Зарегистрирован: 15 май 2026, 22:26

Re: Выражения for и строковые директивы

Сообщение Richie87 »

Поймал ту самую граблю с $${VAR}, баш-скрипт падал на plan и я полчаса не мог понять почему. Оказалось terraform мою переменную окружения за свою интерполяцию принимал. Очень в тему урок.
👍2 ❤️2 🔥 😄 🤔
Ответить
← Предыдущая глава
Циклы в Terraform: for_each по множествам и картам
Следующая глава →
Условная логика: count-трюк, for_each и директива if

Все главы курса «Terraform: инфраструктура как код на Yandex Cloud»

Поделиться темой: ✈ Telegram VK
  • Похожие темы
Похожие запросы: условная логика в terraform count for_each и директива if

Вернуться в «Terraform: инфраструктура как код на Yandex Cloud»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость