Module 10 — Configuration avancée : itération, fonctions et provisioners
Durée indicative du cours : 1h45
Durée indicative de l’atelier : — (atelier for_each / fonctions à venir)
Durée totale (cours + atelier) : ~2h30–3h00
Objectif du module
Basics s’arrête à l’accès ponctuel list[0] / map["clé"] (types).
Associate ouvre l’itération (count, for_each, for), les fonctions HCL au programme TA-004, les dépendances / lifecycle (objectif 4f), les blocs dynamic, et les provisioners (dernier recours) avec l’objet self.
count vs for_each
count — N copies quasi identiques
resource "azurerm_resource_group" "lab" {
count = 3
name = "rg-demo-${count.index}"
location = var.location
}
- Adresse state :
azurerm_resource_group.lab[0],[1], … - Retirer l’élément du milieu décale les index → risque de remplacements.
for_each — identité stable
variable "subnets" {
type = map(object({
cidr = string
}))
default = {
app = { cidr = "10.10.1.0/24" }
db = { cidr = "10.10.2.0/24" }
}
}
resource "azurerm_subnet" "this" {
for_each = var.subnets
name = each.key
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
address_prefixes = [each.value.cidr]
}
- Adresse state :
azurerm_subnet.this["app"]. - Retirer
dbne renumérote pasapp.
| Critère | count |
for_each |
|---|---|---|
| Identité | Index numérique | Clé (set / map) |
| Retrait au milieu | Dangereux | Sûr pour les autres clés |
| Accès | res[0] |
res["app"] / each.key |
| Préférence | Lab très simple | Dès qu’il y a une identité (noms, env) |
list → for_each
for_each = toset(var.names) ou, mieux, une map construite en locals avec une expression for.
Expressions for
locals {
subnet_cidrs = { for s in var.subnet_list : s.name => s.cidr }
app_only = { for k, v in var.subnets : k => v if startswith(k, "app") }
}
Utile pour dériver tags, maps d’outputs, ou nourrir un for_each.
Lab proposé — N subnets
VNet + azurerm_subnet en for_each ; comparer une variante count ; lire state list ; option expression for pour outputs.
Blocs dynamic
Certains arguments Azure sont des blocs répétés (règles NSG, etc.).
dynamic génère ces blocs à partir d’une collection :
resource "azurerm_network_security_group" "app" {
name = "nsg-${var.alias}-app"
location = var.location
resource_group_name = azurerm_resource_group.main.name
dynamic "security_rule" {
for_each = var.nsg_rules
content {
name = security_rule.value.name
priority = security_rule.value.priority
direction = security_rule.value.direction
access = security_rule.value.access
protocol = security_rule.value.protocol
source_port_range = "*"
destination_port_range = security_rule.value.destination_port_range
source_address_prefix = security_rule.value.source_address_prefix
destination_address_prefix = "*"
}
}
}
Quand l’utiliser
Dès que copier-coller dix blocs identiques devient fragile.
Gardez la collection source typée (module 8).
Fonctions HCL et ternaires
Catalogue officiel : Functions.
Pédagogie : tester dans terraform console, puis figer dans locals.
Cœur à maîtriser (TA-004 + pratique Azure)
| Fonction / opérateur | Rôle | Exemple type |
|---|---|---|
join / split |
Coller / découper | join("-", ["rg", var.project, var.env]) |
upper / lower / replace |
Casse / substitution | lower(replace(var.prefix, "-", "")) |
substr / trimspace |
Couper / nettoyer | Tronquer un nom storage |
format |
Gabarit | format("snet-%s-%02d", var.env, 1) |
length |
Longueur chaîne ou collection | Contrôle 3–24 car. storage |
contains |
Appartenance | Env autorisés |
lookup |
Map + défaut | lookup(var.sku, var.env, "Standard_LRS") |
merge |
Fusionner des maps | Tags communs + env |
concat / flatten / distinct |
Listes | CIDR, dédup |
keys / values |
Collections | Clés d’un for_each |
max / min / ceil / floor |
Nombres | Plafonds |
timestamp / timeadd / formatdate |
Dates | Lab / TTL — pas dans un nom prod |
Ternaire cond ? a : b |
Choix | var.env == "prod" ? "GRS" : "LRS" |
can / try / coalesce |
Robustesse | Validations, repli |
alltrue / anytrue |
Agrégats booléens | Listes de checks |
regex / regexall |
Motifs | Naming |
tolist / tomap / tostring / tonumber |
Conversions | set → list |
jsonencode |
Sérialiser | Policy, app settings |
cidrsubnet / cidrhost |
Réseau | Dériver des subnets |
file / templatefile |
Fichiers | Avec path.module |
locals {
st_name = substr(
lower(replace("${var.project}${var.env}${var.alias}", "-", "")),
0,
24
)
tags = merge(var.tags, {
environment = var.environment
managed_by = "terraform"
})
replication = var.environment == "prod" ? "GRS" : "LRS"
}
Pièges
timestamp()/uuid()dans un nom de ressource → recreate à chaque apply.length: chaîne et list/map.lookupvsmap[key]: seullookupaccepte un défaut.- Ternaire : les deux branches doivent être du même type.
Mini-exercices
- Nom storage :
lower+replace+substr+validation. - Naming
join/split. - Tags
merge+lookupSKU. - Ternaire LRS/GRS.
formatdatepour un suffixe lab seulement.- Option :
cidrsubnetpour le subnet app.
Dépendances et cycle de vie des ressources
Objectif examen TA-004 (4f) : savoir quand Terraform crée des dépendances tout seul, quand forcer avec depends_on, et comment le bloc lifecycle change le comportement create / destroy.
Dépendances implicites (références)
Dès qu’une ressource référence une autre dans HCL, Terraform construit le graphe :
resource "azurerm_resource_group" "lab" {
name = "rg-lab"
location = var.location
}
resource "azurerm_virtual_network" "lab" {
name = "vnet-lab"
resource_group_name = azurerm_resource_group.lab.name
location = azurerm_resource_group.lab.location
address_space = ["10.10.0.0/16"]
}
Ici, le VNet dépend du Resource Group : Terraform crée le RG d’abord, et le détruit après le VNet.
Dans la plupart des cas, vous n’avez pas besoin de depends_on. Preferez les références d’attributs.
depends_on — dépendance explicite
Utilisez depends_on quand Terraform ne peut pas voir le lien dans le code (effet de bord, ordre d’API, ressource sans attribut référencé).
resource "azurerm_role_assignment" "reader" {
# … principal_id, scope, role_definition_name …
# Exemple pédagogique : forcer l’ordre si aucune référence HCL ne lie les deux
depends_on = [
azurerm_resource_group.lab,
]
}
Points à retenir :
depends_onaccepte une liste de ressources (ou modules).- Trop de
depends_onrend le graphe opaque et ralentit les plans inutiles. - Preferez toujours une référence (
resource_group_name = azurerm_resource_group.lab.name) quand c’est possible.
Documentation : Resource dependencies.
Bloc lifecycle — règles utiles à l’examen
resource "azurerm_linux_web_app" "app" {
# … attributs …
lifecycle {
create_before_destroy = true
prevent_destroy = false
ignore_changes = [
tags["last_manual_touch"],
]
}
}
| Argument | Effet | Quand y penser |
|---|---|---|
create_before_destroy |
Crée la nouvelle instance avant de détruire l’ancienne (remplacement) | Éviter un trou de service quand un attribut force un replace |
prevent_destroy |
Refuse un plan qui détruirait cette ressource | Garde-fou lab / prod critique (attention : bloque aussi les remplacements) |
ignore_changes |
Ignore certains attributs dans le plan | Tags ou champs gérés hors Terraform (à manier avec prudence) |
Les garde-fous precondition / postcondition dans lifecycle sont traités au module 9 (objectif 4g).
Message examen TA-004
depends_on = ordre quand la référence manque.
create_before_destroy = stratégie de remplacement (create puis destroy).
Ce n’est pas la même chose qu’un prevent_destroy.
Provisioners et objet self
Les provisioners exécutent des actions locales ou distantes au create / destroy :
| Type | Où ça tourne |
|---|---|
local-exec |
Sur la machine qui lance Terraform |
remote-exec |
Sur la ressource (SSH / WinRM) |
file |
Copie de fichiers via connection |
resource "azurerm_linux_virtual_machine" "lab" {
# … attributs VM …
connection {
type = "ssh"
host = self.public_ip_address
user = var.admin_username
private_key = var.ssh_private_key
}
provisioner "remote-exec" {
inline = [
"echo hello from ${self.name}",
]
}
}
Objet self
- Disponible uniquement dans
provisioner/connection. - Référence la ressource parente (
self.public_ip_address, etc.). - Évite un cycle de dépendances qu’une référence
azurerm_…normale créerait.
Nom local self ≠ objet self
En Basics, la convention resource "…" "self" est un label HCL.
L’objet self des provisioners est un mécanisme différent.
Ne pas les confondre à l’oral ni à l’examen.
Bonnes pratiques HashiCorp
- Provisioners = dernier recours.
- Preferez cloud-init, images Packer, config management, extensions Azure.
- L’idempotence est fragile.
terraform_data(ounull_resourcehistorique) pour des hooks hors ressource métier.
Documentation : Provisioners.
À enrichir
Démo local-exec minimal ; exercice dynamic NSG complet ; fiche « 20 fonctions — 20 exemples console » chronométrée type examen.
Suite
Fin du jour 3.
Poursuivre au jour 4 avec Terraform Cloud, puis la révision examen.