linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/lessons/tf-intermediate-04-dynamic-blocks

lesson ── terraform-intermediate ── ~15 мин ── 4 шагов

dynamic блоки: повторяющиеся подблоки

Многие AWS-ресурсы имеют подблоки, которые повторяются: lifecycle-правила в S3, ingress/egress в security group, statement в IAM policy. Если их два-три, пишут руками. Если число зависит от переменной, без dynamic блока пришлось бы дублировать ресурсы.

В этом уроке сделаешь S3-бакет с переменным количеством lifecycle-правил. Один input, list of objects, превращается в N подблоков ресурса.

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Опиши lifecycle-правила в variable

    Создай main.tf в /home/student/tf-dynamic/:

    hcl
    resource "random_id" "suffix" {
      byte_length = 4
    }
    variable "lifecycle_rules" {
      type = list(object({
        id              = string
        enabled         = bool
        prefix          = string
        expiration_days = number
      }))
      default = [
        {
          id              = "archive-old-logs"
          enabled         = true
          prefix          = "logs/"
          expiration_days = 30
        },
        {
          id              = "remove-tmp"
          enabled         = true
          prefix          = "tmp/"
          expiration_days = 7
        },
      ]
    }

    Тип, list(object(...)) с явной схемой каждого элемента. Это даёт Terraform'у возможность проверить на этапе plan что все элементы имеют нужные поля.

    ✓ Структура входа описана. Теперь: ресурс с dynamic блоком.

  2. 02

    Используй dynamic для подблоков

    Добавь в main.tf:

    hcl
    resource "aws_s3_bucket" "demo" {
      bucket = "linuxlab-dynamic-${random_id.suffix.hex}"
    }
    resource "aws_s3_bucket_lifecycle_configuration" "demo" {
      bucket = aws_s3_bucket.demo.id
      dynamic "rule" {
        for_each = var.lifecycle_rules
        content {
          id     = rule.value.id
          status = rule.value.enabled ? "Enabled" : "Disabled"
          filter {
            prefix = rule.value.prefix
          }
          expiration {
            days = rule.value.expiration_days
          }
        }
      }
    }

    Разбор:

    • dynamic "rule", название подблока в родительском ресурсе. Здесь aws_s3_bucket_lifecycle_configuration принимает несколько rule { }.
    • for_each = var.lifecycle_rules, итерируем по списку.
    • Внутри content { }, содержимое одного подблока.
    • rule.value, текущий элемент. rule.value.id, rule.value.prefix.
    • rule.key тоже есть, но для list это индекс (0, 1, 2); для map, ключ.

    ✓ Dynamic блок написан. Init + apply.

  3. 03

    Apply: два правила в одном бакете

    bash
    cd /home/student/tf-dynamic
    terraform init
    terraform plan

    В plan'е увидишь развёрнутые два правила внутри aws_s3_bucket_lifecycle_configuration:

    + rule {
        + id     = "archive-old-logs"
        + status = "Enabled"
        + filter { prefix = "logs/" }
        + expiration { days = 30 }
      }
    + rule {
        + id     = "remove-tmp"
        + status = "Enabled"
        + filter { prefix = "tmp/" }
        + expiration { days = 7 }
      }

    dynamic раскрылся в два реальных подблока. На уровне ресурса результат идентичен ручному написанию двух rule { } блоков.

    bash
    terraform apply -auto-approve

    ✓ Lifecycle configuration создан с двумя правилами.

    То же самое на OpenTofu

    OpenTofu держит CLI и state совместимыми с Terraform по командам этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе сделай backup state и прогон на feature-branch - расхождения концентрируются в новых фичах (variables в backend, state-encryption, OCI registry-backed модули). См. tf-opentofu-parity для полной матрицы.

    • → OpenTofu parity
  4. 04

    Пустой список даёт ноль подблоков

    Поменяй default переменной, сделай пустым:

    hcl
    variable "lifecycle_rules" {
      type = list(object({
        id              = string
        enabled         = bool
        prefix          = string
        expiration_days = number
      }))
      default = []
    }

    Запусти plan:

    bash
    terraform plan

    Это типичный паттерн: dynamic с пустым list = «правил нет». Особенно полезно в модулях: если пользователь модуля не передал правила, ресурс создаётся без них, не падает.

    Можешь применить (apply) или вернуть прежнее значение через редактор, это сейчас не критично.

    подсказка

    Если хочешь вернуть как было: поправь default обратно на список с двумя элементами.

    ✓ Пустой список: валидное состояние. dynamic переживает edge case.

    dynamic + for vs map в HCL

    Иногда dynamic блок можно заменить сразу for-выражением:

    hcl
    # вариант с dynamic
    resource "aws_security_group" "web" {
      dynamic "ingress" {
        for_each = var.ports
        content {
          from_port = ingress.value
          to_port   = ingress.value
          protocol  = "tcp"
        }
      }
    }

    vs

    hcl
    # вариант с for внутри атрибута (если ресурс принимает list атрибута)
    #, некоторые ресурсы умеют так, например aws_vpc_security_group_ingress_rule
    # принимает список напрямую.

    Не каждый AWS-ресурс это позволяет. Атрибуты-списки доступны где атрибуты, не подблоки. Если в HCL пишется xxx { ... } без =, это подблок, нужно dynamic.

    Правило большого пальца:

    • Подблок с произвольным числом повторений → dynamic.
    • Атрибут типа list/map → обычное выражение.

    Если ресурс ругается «expected block, got expression», попробуй dynamic. Если «expected expression», for / list literal.

    • → Типы данных HCL
    • → count vs for_each (там же для dynamic)

Что ты узнал

dynamic "X" { for_each = ..., content { ... } } разворачивает в N блоков X внутри родительского ресурса. Используется когда количество подблоков зависит от input'а. Не путать с for_each на самом ресурсе, dynamic это внутри одного ресурса, for_each создаёт N ресурсов.

команды

  • terraform planвидишь развёрнутые подблоки в выводе
  • terraform consoleпроверить структуру var до того как воткнул в dynamic

концепции

  • · dynamic блок раскрывается на этапе plan: это статика после разрешения переменных
  • · Пустой for_each (`[]`, `{}`) = ноль подблоков: это валидное состояние
  • · Внутри content доступен each.key/each.value

← предыдущий

Troubleshooting Garden: Checkov fail в pipeline

следующий →

Mock-провайдеры, тесты без облака

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки