Ресурсу требуется переменное количество идентичных вложенных блоков (например, правил входящего трафика), определяемое списком/картой.
→Используйте динамический блок, чей `for_each` перебирает коллекцию; ссылайтесь на каждый элемент через итератор блока (имя по умолчанию = метка блока) внутри `content {}`.
Почему: Динамические блоки генерируют повторяющиеся вложенные блоки без копирования и вставки; итератор привязывает каждый сгенерированный блок к его исходному элементу.
Источник↗
Создайте один ресурс на каждую запись в карте объектов, со стабильным ключом, чтобы изменение порядка никогда не приводило к замене.
→Установите `for_each = var.objects` (карту). Используйте `each.key` для стабильного ключа и `each.value.<attr>` для полей. Избегайте `count` здесь — смещения индексов вызывают пересоздание.
Почему: Ключи карты являются стабильными идентификаторами в состоянии; индексы списка позиционны и смещаются при добавлении/удалении элементов.
Выберите между `count` и `for_each` для нескольких экземпляров.
→Используйте `for_each`, когда экземпляры имеют различные идентификаторы (набор/карта); используйте `count` только для N идентичных, нечувствительных к порядку копий. Предпочтите `for_each` для всего, что может увеличиваться/уменьшаться.
Почему: `for_each` обращается по ключу (resource["key"]); `count` обращается по индексу (resource[0]), который перетасовывается при вставках/удалениях.
Входная переменная является объектом, где некоторые атрибуты необязательны и требуют значений по умолчанию.
→Укажите тип как `object({ name = string, size = optional(number, 10) })`. `optional(type, default)` предоставляет значение по умолчанию, когда вызывающий объект опускает атрибут.
Почему: `optional()` со значением по умолчанию делает вызовы лаконичными, гарантируя конкретное значение далее по цепочке — без обработки `null` повсюду.
Источник↗
Проверить предположение о ресурсе перед применением или гарантировать результат после.
→Используйте `lifecycle { precondition { ... } }` для проверки входных данных перед созданием/обновлением и `postcondition` для проверки выходных данных после. Оба принимают `condition` + `error_message`.
Почему: Пользовательские условия быстро выдают ошибку с четким сообщением, вместо того чтобы приводить к сбою применения или запутанной ошибке далее по цепочке.
Источник↗
Ресурс должен быть пересоздан всякий раз, когда изменяется другой ресурс или атрибут.
→Добавьте `lifecycle { replace_triggered_by = [aws_x.y.id] }`. Когда ссылочное значение изменяется, Terraform принудительно заменяет этот ресурс.
Почему: Декларативно выражает зависимость замены, избегая ручного `-replace` при каждом связанном изменении.
Замена ресурса вызывает простой, потому что старый уничтожается до того, как появится новый.
→Установите `lifecycle { create_before_destroy = true }`, чтобы Terraform сначала создал замену, а затем уничтожил старый. Обеспечьте уникальные имена/отсутствие жестких конфликтов.
Почему: Замена без простоя; но следите за конфликтами имен и ограничениями квот, пока оба существуют в течение короткого времени.
Отклонять недопустимые входные значения на ранней стадии (например, окружение, которое не является dev/stage/prod).
→Добавьте блок `validation { condition = contains(["dev","stage","prod"], var.env), error_message = "..." }` к переменной.
Почему: Перехватывает некорректный ввод на этапе планирования с читаемым сообщением, вместо сбоя глубоко внутри вызова провайдера.
Ссылаться на значение, которое может не существовать, без сбоя плана.
→Используйте `try(local.maybe.value, "default")` для отката при ошибках или `can(expr)` для получения логического значения, указывающего, успешно ли выражение.
Почему: Избегает "Error: Unsupported attribute" при отсутствии ключей; обеспечивает корректную обработку опциональных/переменных данных.
Преобразовать список в карту или отфильтровать/изменить форму коллекции для аргумента ресурса.
→Используйте выражение `for`: `{ for u in var.users : u.name => u.role if u.active }` (карта) или `[for x in list : upper(x)]` (список).
Почему: Выражения `for` — идиоматический способ изменения формы данных; предложение `if` фильтрует, форма `k => v` создает карты.
Переменная или вывод содержат секрет, который не должен отображаться в выводе плана/применения.
→Пометьте переменную `sensitive = true` (и выводы тоже). Terraform скрывает ее в выводе CLI, хотя она все еще хранится в состоянии.
Почему: Предотвращает случайное раскрытие в логах/выводе CI; само состояние все еще должно быть защищено (зашифрованный бэкенд, контроль доступа).
Управлять ресурсами в двух регионах/аккаунтах в рамках одной конфигурации.
→Объявите псевдонимы провайдеров (`provider "aws" { alias = "west" region = "us-west-2" }`) и установите `provider = aws.west` для ресурсов или передайте их в модули.
Почему: Псевдонимы позволяют одной конфигурации использовать несколько экземпляров провайдеров; модули получают их явно через аргумент `providers`.
Скрытая зависимость (не выраженная через ссылки) вызывает проблемы с порядком.
→Добавьте `depends_on = [aws_iam_role_policy.x]`, чтобы принудительно установить порядок. Используйте экономно — предпочитайте неявные зависимости через ссылки на атрибуты.
Почему: Явный `depends_on` обрабатывает зависимости, которые граф не может вывести, но чрезмерное использование создает консервативные, более медленные планы.
Рендеринг файла конфигурации/пользовательских данных из шаблона со структурированными переменными.
→Используйте `templatefile("${path.module}/tpl.tftpl", { items = local.items })`; шаблон использует интерполяцию `%{ for }` / `${}`.
Почему: `templatefile` обеспечивает чистый рендеринг на этапе планирования (в отличие от устаревшего провайдера `template`) и поддерживает циклы/условные выражения.
Создайте плоский список каждой комбинации (подсеть, правило) для подачи в один `for_each`.
→Используйте `setproduct(var.subnets, var.rules)` для декартова произведения или `flatten([for ...])` для схлопывания вложенных списков в один.
Почему: Эти функции преобразуют вложенные данные в плоскую, уникально-ключевую коллекцию, которую требует `for_each`.