Сетевые и облачные инфраструктуры — ключевые элементы современной ИТ-среды. Перенос рабочей нагрузки в облако упростил масштабирование, но также потребовал автоматизации управления ресурсами. Terraform — один из наиболее популярных инструментов для описания и управления облачной инфраструктурой через код (IaC). Эта статья подробно рассматривает доступные в Terraform способы повторения (loops) ресурсов и блоков конфигурации, объясняет отличия между ними и даёт практические рекомендации.
Важно: под «повторениями» мы понимаем приём создания нескольких сходных ресурсов или блоков конфигурации на основе коллекций данных.
Что такое Terraform (кратко)
Terraform — это Open Source-инструмент от HashiCorp для декларативного описания инфраструктуры. Конфигурации пишут на HCL (HashiCorp Configuration Language). Terraform сравнивает текущее состояние (state) и желаемое состояние конфигурации и вносит только необходимые изменения.
Ключевые термины: state — файл состояния (JSON), провайдер — плагин для взаимодействия с API облачного или корпоративного сервиса.
Почему важно понимать циклы в Terraform
Использование циклов помогает:
уменьшить дублирование кода;
управлять наборами ресурсов как единой сущностью;
переложить логику генерации имён и параметров на конфигурацию, а не на ручные сценарии.
Но неправильное применение циклов приводит к неожиданной замене ресурсов, конфликтам ключей и проблемам с источником правды (state).
Обзор доступных механизмов
Terraform предоставляет три основных способа повторений:
count — создаёт N копий ресурса или модуля;
выражение for — трансформирует коллекции (list/map/object) в другие коллекции;
for_each — создаёт один экземпляр ресурса на каждый элемент множества или мапы.
Ниже — подробные объяснения и примеры.
1. Параметр count
Описание: count — целочисленный аргумент ресурса/модуля. Если count = N, Terraform создаст N экземпляров. Для изменения конфигурации на уровне экземпляра используют объект count и его атрибут index (начинается с 0).
Когда использовать: когда нужно создать несколько полностью идентичных ресурсов или когда уникальная идентичность может выражаться индексом.
Пример — создание 5 одинаковых EC2-инстансов (упрощённый HCL):
Plan: 5 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
Если нужно пронумеровать ресурсы и задать уникальные теги/имена, используйте count.index:
Результат: экземпляры с именами my-server-0 .. my-server-4.
Важно: изменение значения count может привести к удалению или пересозданию элементов с целым смещением индексов. Планируйте миграцию и используйте lifecycle или create_before_destroy при необходимости.
2. Выражение for
Описание: выражение for (не путать с for_each) применяется в выражениях и шаблонах для трансформации коллекций в новые коллекции (list/map). Синтаксис:
[for in : ]
Когда использовать: когда нужно получить новую коллекцию, например, преобразовать список имён в верхний регистр или собрать только определённые поля.
Пример — вернуть элементы в верхнем регистре:
variable "demo" {
description = "A list of items"
type = list(string)
default = ["one", "two", "three"]
}
output "upper_case" {
value = [for name in var.demo : upper(name)]
}
Выражения for можно комбинировать с условными фильтрами внутри (например, for … if …).
3. for_each
Описание: for_each позволяет итеративно создавать ресурсы на основе множества (set) или мапы (map). Для каждой пары ключ/значение создаётся отдельный экземпляр, доступный через специальный объект each (each.key, each.value).
Когда использовать: когда требуется однозначная привязка элементов коллекции к создаваемым ресурсам и когда важно, чтобы добавление/удаление элемента не смещало другие ресурсы (в отличие от count/index).
Пояснение: toset() конвертирует список в набор; each.key — ключ набора/мапы для конкретного экземпляра, each.value — соответствующее значение.
Примечание: одновременно использовать count и for_each внутри одного и того же ресурса нельзя.
Сравнение: когда count, а когда for_each
count удобен, когда экземпляры полностью идентичны и их порядок/индексы не критичны.
for_each нужен, когда наборы имеют уникальные ключи или вы хотите избежать смещения при удалении/добавлении элементов.
for (expression) — для трансформации коллекций, но не для создания ресурсов напрямую.
Таблица-Шпаргалка (коротко):
count = N => инстансы с count.index
for_each = { key = value } / set => each.key / each.value
[for x in list : expr] => трансформирует коллекцию
Подводные камни и случаи, когда подходы не работают
Смещение индексов при count: если вы удаляете элемент из середины списка, индексы всех последующих изменятся — Terraform может удалить и пересоздать ресурсы.
for_each требует уникальных ключей: если ваша коллекция содержит дубликаты без явных ключей, преобразуйте её в map с уникальными ключами.
Динамически вычисляемые коллекции, зависящие от ресурсов, могут приводить к циклическим зависимостям.
Ресурсы, созданные вручную вне Terraform, могут конфликтовать со state — избегайте смешивания источников управления.
Альтернативные подходы и когда их применять
Используйте модули для повторно используемых шаблонов с параметрами, вместо копирования большого числа ресурсов.
dynamic blocks внутри ресурса подходят для создания повторяющихся вложенных блоков (например, security_group_rule).
Для конфигураций уровня провайдера можно использовать нативные шаблоны (CloudFormation, ARM) там, где Terraform не даёт нужной гибкости.
Ansible/Cloud-Init/packer — для пост-конфигурации виртуальных машин, а не для создания инфраструктуры.
Проверить, что конфигурация не ломает SD (service discovery).
Команда безопасности:
Убедиться в применении корректных IAM/ACL при генерации множества аккаунтов/прав.
Чек-лист «Шпаргалка» (quick cheat sheet)
Нужно N одинаковых ресурсов и порядок не важен? Рассмотрите count.
Нужно стабильно связывать элемент коллекции с ресурсом? Используйте for_each (map или set).
Нужно получить новую коллекцию — используйте выражение for.
Изменение count может вызвать пересоздание — проверьте plan.
Для вложенных повторений используйте dynamic blocks.
Модель мышления (heuristics)
«Желаемое состояние» как цель: создавайте конфигурацию так, чтобы она выражала конечный результат, а не шаги.
«Идентичность важнее порядка»: если важна идентичность ресурса во времени, используйте ключи (for_each), а не индексы.
«Минимизируй деструктивность»: перед крупными изменениями делайте бэкап и прогоняйте plan с коллегой.
Decision flow (простое дерево решений)
flowchart TD
A{Нужно создать несколько похожих ресурсов?} -->|Нет| B[Используйте одиночный ресурс]
A -->|Да| C{Есть ли у элементов уникальные идентификаторы?}
C -->|Да| D[Используйте for_each с map/set]
C -->|Нет| E{Порядок и индекс важен?}
E -->|Да| F[Используйте count с индексом]
E -->|Нет| D
Критерии приёмки
terraform plan показывает ожидаемое количество создаваемых/изменяемых ресурсов.
Изменение коллекции не приводит к неожиданной пересоздаче критичных ресурсов.
Каждому создаваемому ресурсу назначен понятный уникальный идентификатор (ключ/тег).
State хранится надёжно и доступ к нему ограничен.
Короткий словарь (1 строка термина)
count — целочисленный счётчик для создания N экземпляров.
count.index — индекс текущего экземпляра (0..N-1).
for_each — повторение на основе множества или мапы, даёт each.key/each.value.
for (expression) — трансформация коллекций в другие коллекции.
state — файл состояния Terraform, источник правды о созданных ресурсах.
Заключение
Повторения в Terraform — мощный инструмент для уменьшения дублирования и управления множеством ресурсов. Выбор между count и for_each зависит от требований к идентичности и устойчивости ресурсов при изменениях. Выражения for полезны для преобразования данных. Всегда прогоняйте terraform plan, делайте резервные копии state и документируйте принятые решения.
Краткое резюме:
count — прост и полезен для одинаковых экземпляров;
for_each — безопаснее для коллекций с уникальными ключами;