À 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 { … } }, distinguerdynamic(blocs imbriqués) defor_eachsur 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 Regionca-central-1avec 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,count0/1, map filtrée - Avoir suivi Boucles (
terraform-boucles-for-each) :for_each, expressionfor, 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-identityOK)
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_keydans le provider. Pour ce lab,ec2:CreateSecurityGroup,Authorize*,DeleteSecurityGroupsur 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
- Schéma provider — Seuls les blocs répétables du schéma supportent
dynamic. Un bloc singleton (souventlifecycle, ou certainstimeoutsselon la resource) n’est pas itérable : consultez la doc HashiCorp / registry.
- Meta-arguments hors
dynamic— Vous ne pouvez pas « dynamiser »lifecycle,depends_on,provider,count/for_eachresource via undynamic. Ce sont des meta-arguments du langage, pas des nested blocks de schéma.
- Valeurs inconnues au plan — Comme pour
for_eachresource, la collection dudynamicdoit être connue au plan. Une liste dérivée d’un attribut cloud encore unknown provoque une erreur.
- 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) etaws_vpc_security_group_*_rulesur 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
- Nested block répété + liste variable →
dynamic "NAME". for_each= map à clés stables ;content { }= un exemplaire du bloc.dynamic≠for_eachresource : blocs enfants vs instances cloud.- Iterator = nom du bloc (
ingress.value) oueach/iteratorcustom. - Vérifier le schéma : pas tous les nested blocks sont dynamisables.
- Collection connue au plan ; pas de secrets dans HCL.
- 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
ingressdans une resource - C. Remplacer obligatoirement
countsur 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 ;
dynamicremplit des blocs enfants d’une instance - C.
dynamicest 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ésingress, pas des resources. - Référencer mal
ingress.value/ingress.keydanscontent(le label du dynamic devient le nom de l’itérateur). - Forcer un
dynamicsur 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 enca-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 ledynamicen un seul endroit. - Vous n’êtes pas à l’aise avec
for_eachresource : 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
- Doc : dynamic blocks, for_each, aws_security_group
- Sur ce site : Conditionnels, Boucles, Providers ; suite Modules bases
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.



