
Votre for_each crée 5 VMs, mais vous voulez un output qui liste
leurs noms en majuscules, ou filtrer uniquement celles de l’environnement
prod. Avec for_each seul, c’est impossible — il faut une couche
de transformation.
Les expressions for de HCL sont cette couche. Elles ne créent pas
de ressources — c’est le rôle de for_each — mais elles transforment
des collections : construire une liste de noms, pivoter une map,
filtrer par condition. On les retrouve surtout dans les locals et
les outputs, mais elles sont utilisables partout où Terraform
attend une valeur dérivée d’une collection. Sans cette
compétence, chaque dérivation d’une valeur existante impose soit de la
duplication, soit un output intermédiaire inutile. Les expressions for
sont également le mécanisme pour alimenter un for_each avec des données
transformées depuis une source brute.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »for→ liste :[for k, v in map : expression]for→ map :{ for k, v in map : clé => valeur }- Filtre conditionnel :
[for ... if condition] - Boucle sur une liste vs sur une map
- Usages typiques : construire des noms, filtrer, transformer pour
for_each
Prérequis
Section intitulée « Prérequis »for_eachcompris (for_each Terraform)- Fonctions de collections connues (fonctions Terraform)
L’idée derrière les boucles for
Section intitulée « L’idée derrière les boucles for »Pensez à la compréhension de liste en Python :
vms = { "web": 512, "db": 1024, "cache": 512 }
# Transfo 1 : liste de nomsnoms = [f"lab-{k}" for k, v in vms.items()]# ["lab-web", "lab-db", "lab-cache"]
# Transfo 2 : filtrer par valeurbackend = {k: v for k, v in vms.items() if v > 512}# { "db": 1024 }
# Transfo 3 : map mémoire → liste de nomsnoms_upper = [n.upper() for n in noms]# ["LAB-WEB", "LAB-DB", "LAB-CACHE"]Les expressions for de Terraform font exactement cela : transformer des collections sans créer des ressources.
Syntaxe minimale
Section intitulée « Syntaxe minimale »for → liste
Section intitulée « for → liste »La logique : « pour chaque élément de la collection, appliquer une transformation et en faire une liste ».
[for k, v in var.vms : "lab-${k}"] # ← Liste de noms construits# ["lab-cache", "lab-db", "lab-web"]
[for name in var.hostnames : upper(name)] # ← Transformer une liste# ["WEB", "DB", "CACHE"]for → map
Section intitulée « for → map »La logique : « construire une nouvelle map en utilisant une expression pour la clé et une pour la valeur »
{ for k, v in var.vms : k => v.memory } # ← Nouvelle map : clé => mémoire# { "cache" = 512, "db" = 1024, "web" = 512 }for avec filtre if
Section intitulée « for avec filtre if »Ajouter une condition pour exclure certains éléments :
{ for k, v in var.vms : k => v if v.role == "backend" } # ← Seulement les VMs backend# { "cache" = {...}, "db" = {...} }Exemple : transformer une map de VMs
Section intitulée « Exemple : transformer une map de VMs »variable "vms" { type = map(object({ memory = number, role = string })) default = { "web" = { memory = 512, role = "frontend" } "db" = { memory = 1024, role = "backend" } "cache" = { memory = 512, role = "backend" } }}
locals { # Liste de noms vm_names_list = [for k, v in var.vms : "lab10-${k}"]
# Map nom => mémoire vm_memory_map = { for k, v in var.vms : k => v.memory }
# Filtre : seulement les VMs backend backend_vms = { for k, v in var.vms : k => v if v.role == "backend" }
# Transformer une liste names_upper = [for name in local.vm_names_list : upper(name)]
# Construire des noms de disques disk_names = { for k, v in var.vms : k => "lab10-${k}.qcow2" }}Sorties réelles :
backend_vms = { "cache" = { memory = 512, role = "backend" } "db" = { memory = 1024, role = "backend" }}disk_names = { "cache" = "lab10-cache.qcow2" "db" = "lab10-db.qcow2" "web" = "lab10-web.qcow2"}names_upper = [ "LAB10-CACHE", "LAB10-DB", "LAB10-WEB",]vm_memory_map = { "cache" = 512 "db" = 1024 "web" = 512}vm_names_list = [ "lab10-cache", "lab10-db", "lab10-web",]Boucle sur une liste
Section intitulée « Boucle sur une liste »Quand la source est une liste (et non une map), la variable d’itération est l’élément (pas de clé/valeur) :
variable "envs" { type = list(string) default = ["dev", "staging", "prod"]}
locals { env_upper = [for env in var.envs : upper(env)] # ["DEV", "STAGING", "PROD"]
env_names = [for env in var.envs : "vm-${env}"] # ["vm-dev", "vm-staging", "vm-prod"]}Avec index dans la liste :
locals { indexed = [for i, env in var.envs : "${i}: ${env}"] # ["0: dev", "1: staging", "2: prod"]}Préparer un set pour for_each
Section intitulée « Préparer un set pour for_each »Un usage très courant : transformer une liste en set utilisable par for_each.
for_each n’accepte pas directement une liste (ou un tuple) — il
exige une map ou un set. La fonction toset() fait cette conversion :
variable "hostnames" { type = list(string) default = ["web", "db", "cache"]}
resource "libvirt_volume" "disk" { for_each = toset(var.hostnames) # list → set pour for_each
name = "${each.key}.qcow2"}Ou avec une boucle for pour filtrer avant :
resource "libvirt_volume" "disk" { for_each = toset([for h in var.hostnames : h if h != "cache"])
name = "${each.key}.qcow2"}Transformer les outputs d’un for_each
Section intitulée « Transformer les outputs d’un for_each »Après un for_each, les ressources sont des maps. Une boucle for
extrait les attributs souhaités :
output "vm_names" { value = { for k, v in libvirt_domain.vm : k => v.name } # { "web" = "lab10-web", "db" = "lab10-db", ... }}
output "vm_ips" { # Filtrer les VMs avec une IP assignée value = { for k, v in libvirt_domain.vm : k => v.network_interface[0].addresses[0] if length(v.network_interface[0].addresses) > 0 }}Inverser une map
Section intitulée « Inverser une map »locals { roles = { "web" = "frontend", "db" = "backend" }
# Inverser : valeur => clé by_role = { for name, role in local.roles : role => name } # { "frontend" = "web", "backend" = "db" }}Grouper des valeurs (opérateur …)
Section intitulée « Grouper des valeurs (opérateur …) »Pour regrouper les valeurs avec la même clé en liste :
locals { vms_by_role = { for k, v in var.vms : v.role => k... } # { "frontend" = ["web"], "backend" = ["db", "cache"] }}L’opérateur ... (ellipsis) indique à Terraform d’accumuler les valeurs
en liste quand des clés se répètent.
Référence rapide
Section intitulée « Référence rapide »| Expression | Résultat | Usage |
|---|---|---|
[for x in list : f(x)] | Liste transformée | Dériver une liste |
[for i, x in list : ...] | Liste avec index | Accéder à la position |
{ for k, v in map : k => f(v) } | Map transformée | Dériver une map |
[for k, v in map : expr if cond] | Liste filtrée | Sélectionner des entrées |
{ for k, v in map : k => v if cond } | Map filtrée | Filtrer une map |
{ for k, v in map : v => k... } | Map groupée | Inverser/grouper |
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
Error: duplicate map key | Deux éléments produisent la même clé | Utiliser ... pour grouper, ou revoir l’expression de clé |
for ne filtre pas | Condition if mal placée | La condition se place après l’expression, pas avant |
| Résultat en liste au lieu de map | Crochets [] au lieu d’accolades {} | { for ... } pour une map, [ for ... ] pour une liste |
À retenir
Section intitulée « À retenir »[for x in col : expr]→ liste ;{ for k, v in col : k => v }→ mapif conditionfiltre les éléments — se place après l’expression- Les boucles
fortransforment des données partout où Terraform attend une valeur —locals,outputs, argumentfor_each, etc. toset([for ...])prépare une collection pourfor_eachv.role => k...groupe les valeurs avec la même clé en liste