DevOps Elastic Hayway
Document

SUBSCRIBE TO GET FULL ACCESS TO THE E-BOOKS FOR FREE 🎁SUBSCRIBE NOW

Professional Dropdown with Icon

SUBSCRIBE NOW TO GET FREE ACCESS TO EBOOKS

TerraformLesson 13 / 2412 min readUpdated September 13, 2026

À la fin de ce tutoriel, vous saurez détecter un nested block qui se répète (ingress/egress Security Group, settings, etc.), écrire la syntaxe dynamic "NAME" { for_each = … content { … } }, distinguer dynamic (blocs imbriqués) de for_each sur resource (instances entières), générer des règles SG avec le provider AWS ~> 5.0, connaître les limites (tous les nested blocks ne sont pas dynamisables), puis valider un lab léger en Region ca-central-1 avec destroy systématique.

Niveau : Débutant → Intermédiaire · Temps estimé : 35–45 min · Versions testées : Terraform 1.16.2 (série ≥ 1.9), AWS provider ~> 5.0 · Dernière vérification : 2026-09-10

Slug proposé : terraform-dynamic-blocks · Série : Terraform · Remplace / fusionne : #1645 (P6 bonus dynamic blocks — quasi vide)

Prérequis

  • Avoir suivi Conditionnels (terraform-conditionnels) : ternaires, count 0/1, map filtrée
  • Avoir suivi Boucles (terraform-boucles-for-each) : for_each, expression for, adresses stables
  • Avoir suivi Providers (terraform-providers-ressources) : schéma resource, nested blocks
  • Terraform ≥ 1.9, AWS CLI v2, profil de lab (aws sts get-caller-identity OK)

Coût estimé : quasi 0 € (Security Group dans le VPC par défaut, aucune instance EC2). Destroy obligatoire. Aucun secret dans le HCL / tfvars.

Auth sans secrets. Utilisez AWS_PROFILE / SSO. Jamais d’access_key / secret_key dans le provider. Pour ce lab, ec2:CreateSecurityGroup, Authorize*, DeleteSecurityGroup sur le compte de lab suffisent.

Ce que nous allons construire

Lab dynamic blocks Terraform (ca-central-1)
  ├── versions.tf / providers.tf → TF ≥ 1.9, aws ~> 5.0, region ca-central-1
  ├── variables.tf               → list(object) des règles ingress
  ├── locals.tf                  → map dérivée pour for_each du dynamic
  ├── main.tf                    → aws_security_group + dynamic "ingress"
  ├── outputs.tf                 → id du SG + nombre de règles
  └── init → plan → apply → destroy

(Alt : « Dynamic blocks Terraform : générer des blocs nested — Security Group AWS ca-central-1 ».)

Ce guide remplace le post WordPress #1645 (quasi vide) et aligne P2 sur le terraform dynamic block, actualisé 2026 (TF ≥ 1.9, aws ~> 5.0, ca-central-1).

Étape 1 — Quand un nested block se répète

Dans le schéma d’une resource Terraform, certains arguments sont des blocs imbriqués (nested blocks) : ingress / egress sur aws_security_group, setting sur certains services, rule ailleurs. Quand la liste vient d’une variable (N ports, N CIDR, N settings), recopier le bloc à la main devient fragile et non maintenable.

Sans dynamic, chaque règle est un bloc ingress { … } recopié. Dès N > 2 ou une liste paramétrable (tfvars, module), préférez un terraform dynamic block : une seule structure content, itérée sur une collection.

Étape 2 — Syntaxe dynamic "NAME" { for_each = … content { … } }

Le meta-bloc dynamic génère zéro, un ou plusieurs nested blocks du même nom :

variable "ingress_rules" {
  type = list(object({
    description = string
    from_port   = number
    to_port     = number
    protocol    = string
    cidr_blocks = list(string)
  }))
  default = [
    {
      description = "HTTPS lab"
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = ["10.0.0.0/8"]
    },
    {
      description = "HTTP lab"
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      cidr_blocks = ["10.0.0.0/8"]
    }
  ]
}

locals {
  # Clés stables : port-protocole (évite les index fragiles)
  ingress_map = {
    for r in var.ingress_rules :
    "${r.protocol}-${r.from_port}-${r.to_port}" => r
  }
}

# Dans la resource :
# dynamic "ingress" {
#   for_each = local.ingress_map
#   content {
#     description = each.value.description
#     from_port   = each.value.from_port
#     to_port     = each.value.to_port
#     protocol    = each.value.protocol
#     cidr_blocks = each.value.cidr_blocks
#   }
# }

Points clés :

Élément Rôle
"ingress" Nom exact du nested block attendu par le schéma provider
for_each Set ou map (préférez une map à clés stables)
content { } Corps d’un nested block ; répété pour chaque élément
each.key / each.value Identiques au for_each resource (dans content)

iterator (optionnel) renomme each si vous imbriquez plusieurs dynamic. Une liste vide → aucun nested block généré (équivalent d’un omit).

Étape 3 — dynamic vs for_each sur resource

Ne confondez pas les deux niveaux de répétition :

Besoin Outil Effet dans le state
N resources distinctes (N buckets, N SG) for_each / count sur le bloc resource N adresses type.name["clé"]
N nested blocks dans une resource dynamic "bloc" Une adresse resource ; N blocs enfants dans le config
Valeur scalaire conditionnelle Ternaire / coalesce (tuto Conditionnels) Pas de répétition d’instances

Exemple mental : trois Security Groups séparés → for_each sur aws_security_group. Trois règles dans un seul SG → dynamic "ingress". Les deux se combinent : for_each sur la resource et dynamic à l’intérieur.

Règle P2 : resource-level pour des objets cloud séparés ; dynamic uniquement pour coller au schéma nested du provider.

Étape 4 — Exemple Security Group (AWS provider ~> 5.x)

Lab réaliste et peu coûteux : un SG dans le VPC par défaut, règles générées par dynamic.

data "aws_vpc" "default" {
  default = true
}

resource "aws_security_group" "lab" {
  name        = "lab-tf-dynamic-sg"
  description = "Lab dynamic blocks — ca-central-1"
  vpc_id      = data.aws_vpc.default.id

  dynamic "ingress" {
    for_each = local.ingress_map
    content {
      description = ingress.value.description
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }

  egress {
    description = "Allow all egress (lab)"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name      = "lab-tf-dynamic-sg"
    ManagedBy = "terraform"
    Lab       = "dynamic-blocks"
  }
}

output "security_group_id" {
  description = "ID du Security Group de lab."
  value       = aws_security_group.lab.id
}

output "ingress_rule_count" {
  description = "Nombre de règles ingress générées."
  value       = length(local.ingress_map)
}

L’iterator par défaut porte le nom du bloc : ingress.value (équivalent de each.value). Avec AWS ~> 5.0, les nested ingress/egress restent valides ; en prod, aws_vpc_security_group_ingress_rule (une resource par règle) offre des adresses plus granulaires — hors scope de ce lab.

Étape 5 — Limites : pas tous les nested blocks

  1. Schéma provider — Seuls les blocs répétables du schéma supportent dynamic. Un bloc singleton (souvent lifecycle, ou certains timeouts selon la resource) n’est pas itérable : consultez la doc HashiCorp / registry.
  1. Meta-arguments hors dynamic — Vous ne pouvez pas « dynamiser » lifecycle, depends_on, provider, count / for_each resource via un dynamic. Ce sont des meta-arguments du langage, pas des nested blocks de schéma.
  1. Valeurs inconnues au plan — Comme pour for_each resource, la collection du dynamic doit être connue au plan. Une liste dérivée d’un attribut cloud encore unknown provoque une erreur.
  1. Lisibilité & drift — Au-delà de ~10–15 règles complexes, préférez des resources dédiées ou un module. Ne mélangez pas règles inline (dynamic) et aws_vpc_security_group_*_rule sur le même SG.

Étape 6 — Lab ca-central-1 (SG léger + destroy)

Objectif : créer un Security Group avec 2 règles ingress via terraform dynamic block, Region ca-central-1, puis tout détruire.

# versions.tf
terraform {
  required_version = ">= 1.9.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# providers.tf
provider "aws" {
  region = "ca-central-1"
  # Auth : AWS_PROFILE / SSO — jamais de clés ici
}

# variables.tf — voir étape 2 (ingress_rules)
# locals.tf   — ingress_map
# main.tf     — data aws_vpc + aws_security_group + dynamic
# outputs.tf  — security_group_id, ingress_rule_count

Commandes :

mkdir -p ~/terraform-dynamic-blocks-lab && cd ~/terraform-dynamic-blocks-lab
# collez versions.tf, providers.tf, variables.tf, locals.tf, main.tf, outputs.tf
export AWS_PROFILE=lab
export AWS_REGION=ca-central-1
terraform init
terraform fmt
terraform validate

terraform plan
terraform apply -auto-approve
terraform output

# Vérification rapide (optionnel)
aws ec2 describe-security-groups 
  --group-ids "$(terraform output -raw security_group_id)" 
  --region ca-central-1 
  --query 'SecurityGroups[0].IpPermissions'

terraform destroy -auto-approve

Observez : 1 SG à créer, plusieurs nested ingress. Ajouter une règle → update in-place (sauf changement de name).

Étape 7 — Checklist

  1. Nested block répété + liste variable → dynamic "NAME".
  2. for_each = map à clés stables ; content { } = un exemplaire du bloc.
  3. dynamicfor_each resource : blocs enfants vs instances cloud.
  4. Iterator = nom du bloc (ingress.value) ou each / iterator custom.
  5. Vérifier le schéma : pas tous les nested blocks sont dynamisables.
  6. Collection connue au plan ; pas de secrets dans HCL.
  7. Lab ca-central-1 + destroy systématique.

Nettoyage

cd ~/terraform-dynamic-blocks-lab
terraform destroy -auto-approve
rm -rf ~/terraform-dynamic-blocks-lab

Confirmez l’absence du Security Group lab-tf-dynamic-sg dans la console EC2 / CLI (ca-central-1).

Erreurs fréquentes

Symptôme Cause Correction
Extraneous JSON object property / bloc inconnu Nom dynamic "foo" hors schéma Aligner sur le nested block doc (ex. ingress)
Invalid dynamic for_each value / unknown Collection dérivée d’attr cloud inconnu Construire la map depuis variables / locals connus
Confusion each vs iterator Dans content, l’iterator par défaut = nom du bloc Utiliser ingress.value ou définir iterator = each
Plan qui recrée tout le SG Changement de name / remplacement forcé Séparer name stable ; tags seuls → update in-place
Règles qui disparaissent au apply Mélange inline SG + aws_vpc_security_group_*_rule Un seul mode de gestion des règles par SG
Liste indexée 0,1,2 qui « glisse » for_each = toset(list) ou index Map "proto-port" => rule comme en étape 2

Quiz (3 questions)

1. À quoi sert dynamic "ingress" { for_each = … content { … } } ?

  • A. Créer N Security Groups distincts
  • B. Générer N nested blocks ingress dans une resource
  • C. Remplacer obligatoirement count sur resource

2. Quelle différence majeure avec for_each sur resource "aws_security_group" ?

  • A. Aucune : syntaxe interchangeable
  • B. Resource-level crée plusieurs instances ; dynamic remplit des blocs enfants d’une instance
  • C. dynamic est déprécié depuis Terraform 1.9

3. Pourquoi préférer une map proto-port => rule plutôt qu’une liste indexée pour for_each du dynamic ?

  • A. Les clés stables clarifient le plan et évitent les glissements
  • B. Les listes sont interdites dans Terraform ≥ 1.9
  • C. AWS refuse les index numériques

Réponses : 1‑B · 2‑B · 3‑A

Pourquoi / quand utiliser des dynamic blocks

Un nested block se répète (règles de security group, origins CloudFront, settings divers) et recopier dix fois le même bloc devient illisible. dynamic génère ces blocs imbriqués à partir d’une collection. Utilisez-le quand la carte des nested blocks varie selon l’environnement ; gardez du HCL statique si vous n’avez qu’une ou deux règles stables.

Dynamic vs for_each sur resource — rappel décisionnel

Besoin Choisir
N ressources distinctes (N SG, N buckets) for_each / count sur resource
N blocs enfants dans une resource dynamic
Transformer une map en liste d’objets expression for (+ parfois dynamic)

Mélanger les deux sans intention claire rend le plan difficile à relire.

Pièges des dynamic blocks

  • Oublier que dynamic "ingress" crée des blocs nommés ingress, pas des resources.
  • Référencer mal ingress.value / ingress.key dans content (le label du dynamic devient le nom de l’itérateur).
  • Forcer un dynamic sur un nested block non supporté ou singleton : lisez la doc du resource AWS 5.x.
  • Passer une liste ordonnée alors qu’un set/map à clés stables simplifierait les diffs.
  • Complexifier un SG de lab jusqu’à ouvrir 0.0.0.0/0 « pour tester » : gardez des règles minimales en ca-central-1, TF ≥ 1.9, puis destroy.

Quand éviter dynamic

  • Une seule règle figée : un bloc ingress { … } classique est plus lisible en revue PR.
  • La logique métier appartient à un module enfant : exposez une variable list(object) et laissez le module faire le dynamic en un seul endroit.
  • Vous n’êtes pas à l’aise avec for_each resource : maîtrisez d’abord le tuto Boucles.

FAQ dynamic blocks

dynamic modifie-t-il le state comme un for_each resource ? Il change la forme de la configuration d’une resource existante ; les adresses TYPE.NAME ne se multiplient pas.

Puis-je nested un dynamic dans un autre ? Parfois, mais la lisibilité s’effondre vite. Préférez préparer une structure via locals.

Erreur « Invalid dynamic block » : que faire ? Vérifiez le nom du nested block (doc provider), le for_each non null, et le corps content { }.

Lab sans apply ? plan suffit pour voir les nested blocks générés ; si vous appliquez un SG, détruisez-le immédiatement.

Secrets dans les règles ? Jamais de CIDR ou descriptions issus de secrets en clair dans Git ; paramétrez via variables non commitées si besoin.

Pour aller plus loin

Maillage série Terraform (P2)

← Précédent Conditionnels (terraform-conditionnels)
→ Suivant Modules bases (terraform-modules-bases)
Aussi Boucles (terraform-boucles-for-each) · Providers (terraform-providers-ressources)
Carte Overview · … · Conditionals · Dynamic blocks · Modules · Workspaces · …

Cas réel — un dynamic trop malin pour une SG

Une security group avec dynamic "ingress" sur une map mal typée ouvre 0.0.0.0/0 « le temps du debug ». Le dynamic n’est pas une excuse : chaque itération doit rester lisible dans le plan. Si vous ne pouvez pas citer les règles à voix haute, revenez à deux blocs statiques.

Piège : changer la clé for_each d’un dynamic recrée les nested blocks. Les labels stables (protocole+port, pas un index 0,1,2) évitent le churn. Préférez dynamic quand la liste vient d’une variable d’équipe ; gardez le statique pour une règle unique et critique (SSH bastion).

À retenir : dynamic = répétition contrôlée, pas un générateur opaque. Le plan doit rester reviewable en cinq minutes.

Retour parcours Terraform — hub de la série et leçons sœurs.

Share your love

Leave a Reply

Your email address will not be published. Required fields are marked *