Terraform Associate 004
Durée : 4 jours · Niveau : intermédiaire · Cible : HashiCorp Certified: Terraform Associate (004)
Formation de quatre jours pour l’examen HashiCorp Certified: Terraform Associate (004). Les savoirs du cours Basics y sont repris en entier, présentés dans le cadre de l’examen et d’un fil conducteur, pas comme une deuxième journée d’initiation.
Cible examen — 004
L’examen porte sur Terraform 1.12 et HCP Terraform. Le déroulé ci-dessous est la carte du cours, avant la réécriture des modules. Les notes plus bas dans cette page sont un backlog formateur.
En un coup d’œil
Pourquoi suivre ce cours
- Couvrir tout le programme de l’examen Associate 004, documentation Terraform comprise
- Retrouver les savoirs de Basics dans un autre cadre, sans refaire le même cours
- Pratiquer chaque jour, sur un fil conducteur commun
- Choisir la série d’ateliers au provider neutre ou la série Azure
Prérequis
Connaissances
- Bases IT / cloud et aisance en terminal
- Un éditeur (VS Code recommandé)
- Accès à un environnement cloud de lab
Matériel et accès
- Ordinateur portable (Windows, macOS ou Linux)
- Accès Internet (téléchargements d’outils, providers Terraform, APIs cloud)
- Droits administrateur locaux sur le poste : pouvoir installer / mettre à jour Terraform et les outils associés
- Compte cloud de lab + compte HashiCorp (utile pour Terraform Cloud)
Cours préalable(s)
- Terraform Basics est utile, pas obligatoire
- Sans Basics, les explications de ce cours suffisent
- Avec Basics, les mêmes idées reviennent plus loin, reliées à l’examen, aux modules et à HCP Terraform
Travaux pratiques
Oui. Chaque jour alterne théorie et atelier sur le même fil conducteur (vue d’ensemble).
- Deux séries d’ateliers, les mêmes étapes : provider neutre (
localetrandom), ou Azure - Jour 1 — providers, workflow, premier environnement
- Jour 2 — state, dérive, import, backend
- Jour 3 — langage, modules, garde-fous
- Jour 4 — données sensibles, HCP Terraform, révision de l’examen
Statut rédaction
Le déroulé des quatre jours ci-dessous est la carte à suivre. Les modules 1 à 12 et les ateliers détaillés sont encore l’amorce précédente. Ils seront réécrits dans cet ordre, en commençant par le module 1.
{: .atlas-formateur-only }
Fil conducteur
Une équipe doit décrire un petit environnement, le faire évoluer, et permettre à un collègue de le reprendre sans repartir de zéro.
Dans les modules, cet environnement est construit avec les providers local et random. Il n’y a pas de compte cloud.
Les ateliers reprennent les mêmes étapes deux fois.
- Série neutre : les mêmes fichiers et identifiants que dans les modules.
- Série Azure : les mêmes étapes sur un lab Azure.
HCP Terraform, au jour 4, est commun aux deux séries. Ce n’est pas un cloud d’infrastructure.
Ce que Basics apporte, présenté autrement
Les idées du cours Basics sont toutes dans ces quatre jours. Elles ne reviennent pas comme un rappel du premier apply, ni comme une demi-journée d’alignement.
| Savoir déjà vu en Basics | Où il vit ici | Comment il est présenté |
|---|---|---|
| Infrastructure as code, déclaratif, idempotence | Jour 1 | Comme le modèle de l’examen : un langage, plusieurs providers, plusieurs clouds |
| Blocs HCL, provider, lockfile | Jour 1 | Comme le mécanisme des providers, avec deux providers dans la même configuration |
fmt, init, validate, plan, apply, destroy |
Jour 1 | Comme le workflow officiel, puis pratiqué sur l’environnement du fil conducteur |
| Variables, types, tfvars, locals | Jour 3 | Comme l’interface d’un module et les types composés |
| Outputs et valeurs sensibles | Jour 3 et jour 4 | Comme un contrat, puis comme une donnée qu’il ne faut pas laisser dans le state |
| State local, lecture d’un plan, dérive observée | Jour 2 | Comme le backend, le verrou, l’import et la réconciliation |
| Découpage des fichiers, aperçu des modules | Jour 3 | Comme un module versionné, appelé par une racine |
| Nommage | Jour 3 | Comme des conditions de validation sur les entrées |
Déroulé des quatre jours
Chaque jour partage le temps entre la théorie et l’atelier du jour. L’atelier pratique ce qui vient d’être expliqué. Les deux séries d’ateliers font les mêmes gestes.
Jour 1 — Providers et workflow
Objectifs d’examen : 1a, 1b, 1c, 2a, 2b, 2c, 3a à 3g.
Théorie. Terraform décrit une cible et la fait converger. Les providers sont des plugins. On installe et on verrouille leurs versions. Une configuration peut en utiliser plusieurs. Le workflow lit la configuration, le state et la réalité, puis propose un plan avant d’appliquer ou de détruire. fmt et validate font partie de ce workflow.
Atelier. Créer l’environnement du fil conducteur. En série neutre, des fichiers locaux et un identifiant aléatoire. En série Azure, les mêmes étapes sur le lab Azure. Enchaîner formatage, validation, initialisation, plan, apply, une modification, un plan sans changement, puis destroy.
Jour 2 — State, dérive et reprise
Objectifs d’examen : 2d, 6a, 6b, 6c, 6d, 7a, 7b, 7c.
Théorie. Le state est le carnet de ce que Terraform gère. Le backend local est le cas par défaut. Le verrou empêche deux écritures en même temps. Le bloc backend place ce carnet ailleurs. Une modification faite hors de Terraform crée une dérive. Une ressource qui existe déjà s’attache avec import. Les commandes d’inspection du state et les journaux détaillés servent à comprendre un écart.
Atelier. Provoquer une dérive, la lire dans un plan, puis choisir de revenir au code ou d’adopter le réel. Importer un objet déjà créé. Inspecter le state. En série Azure, configurer en plus un backend distant et observer le verrou. En série neutre, le backend distant reste compris dans la théorie. Sa pratique partagée a lieu au jour 4 avec HCP Terraform.
Jour 3 — Langage et modules
Objectifs d’examen : 4a à 4g, 5a à 5d.
Théorie. resource crée, data lit. Les attributs relient les objets. Les variables, les types composés et les outputs forment l’interface. Les expressions et les fonctions évitent de recopier. depends_on et create_before_destroy règlent l’ordre et le remplacement. Les conditions custom refusent une entrée ou un résultat incohérent. Un module a une source, une version, et ses variables ne fuient pas vers l’appelant.
Atelier. Extraire une partie de l’environnement dans un module local, l’appeler depuis la racine, poser une validation et un output. Faire évoluer la version du module. Les deux séries suivent ces étapes sur leur cible.
Jour 4 — Secrets, HCP Terraform et examen
Objectifs d’examen : 4h, 8a, 8b, 8c, 8d, et retour sur l’ensemble du programme.
Théorie. sensitive masque l’affichage. Une valeur éphémère et un argument write-only évitent de stocker un secret dans le state. Vault est un coffre, distinct de ce masquage. HCP Terraform exécute le workflow à distance, organise des workspaces dans des projects, et ajoute la collaboration et la gouvernance.
Atelier. Produire un secret éphémère avec random et vérifier qu’il ne reste pas dans le state comme une variable sensible classique. Ouvrir HCP Terraform, créer un project et un workspace, lancer un run sur le fil conducteur. La série Azure branche ce workspace sur le lab Azure. La fin de journée reprend chaque objectif d’examen avec une question et la commande ou le bloc HCL qui va avec.
Modules visés par ce déroulé
| Jour | Modules à réécrire |
|---|---|
| 1 | 1 — Concepts, 2 — Prise en main, 3 — Workflow, 4 — Providers |
| 2 | 5 — Gestion de l’état, 6 — Commandes avancées |
| 3 | 7 — Modules, 8 — Variables et types, 9 — Outputs, 10 — Config avancée |
| 4 | 11 — HCP Terraform, 12 — Révision examen |
Ateliers : index. Les consignes détaillées des ateliers seront réécrites avec les modules, en deux séries.
Backlog formateur — notes de l’amorce précédente
Ces notes servent de matière pour la réécriture. Elles ne sont pas le déroulé du cours.
Jour 2 — Providers, état, commandes avancées
| Module | Contenu |
|---|---|
| 4 — Providers | required_providers, lockfile, config avancée provider, auth CI (WIF / App Registration / ADO) |
| 5 — Gestion de l’état | state local/distant (ex. Azure Storage), workspaces CLI, state list/mv, import ; drift / state rm |
| 6 — Commandes avancées | -target, refresh, import, fmt, validate |
Modules 5–6 — à détailler : drift, import et réconciliation state
Partie prioritaire (suite naturelle de Basics atelier 5 — observation seulement) :
- Clarifier les cas (ne pas les confondre) :
- Drift : ressource déjà dans le state, modifiée hors Terraform (portail, autre outil) →
planpropose~/ recreate - Ressource orpheline hors state : créée à la main, absente du state → Terraform ne la gère pas tant qu’on ne l’importe pas
- Ressource dans le state mais détruite dehors :
planpropose recreate / erreurs selon le provider terraform import(CLI) et blocimport(config moderne) : écrire le HCL avant / aligner les attributs, ID Azure correct, vérifier avecplan(idéalement no changes ou écarts compris)terraform refresh/ refresh auplan: ce que ça met à jour dans le state vs ce que ça ne « répare » passtate rm: retirer du state sans détruire le cloud — quand c’est légitime (reprise, découpage) et les risques- Réconciliation : choisir entre revenir au code (
apply), adopter le réel (adapter le HCL puis import / refresh), ou sortir du management (state rm) - State lock (backend distant) — distinct du
.terraform.lock.hcl(providers) vu en Basics module 3 : verrou pendantplan/applypour éviter deux écritures concurrentes sur le même state (Azure Storage lease, Terraform Cloud, etc.) ; message Error acquiring the state lock ; forcer / débloquer avec prudence - Workspaces CLI — un workspace = un state nommé pour le même code ; clé / préfixe dans le backend distant ; croiser avec
terraform.workspace(développé au mémo références internes / module 8) - Atelier dédié : drift portail → décider ; ressource créée à la main →
import+planpropre ; casstate rmcontrôlé - Lien exam Associate : commandes state / import / refresh / workspaces au programme TA-004
Prérequis : atelier 5 Basics (lire un plan, drift, state list / show) — Associate = agir sur le state, pas seulement observer.
Module 4 — à détailler (reporté depuis Terraform Basics)
Contenu prévu, au-delà du rappel required_providers / lockfile :
- Plusieurs blocs
provideravecalias— multi-souscription / multi-tenant (ex.provider "azurerm" { alias = "prod" … }+provider = azurerm.prodsur les ressources) - Arguments avancés du provider AzureRM —
environment,auxiliary_tenant_ids, auth Service Principal / MSI / OIDC, variablesARM_* featuresen profondeur — Key Vault soft-delete, VM, etc. (rappel depuis Basics)- Bonnes pratiques : ne pas mettre de secrets dans le code ; config via env / CI
Auth CI/CD sans secret long terme — WIF + App Registration + Azure DevOps
- Workload Identity Federation (WIF / OIDC) : le pipeline ADO obtient un token fédéré ; plus de
client_secretà rotation manuelle dans un variable group - App Registration (Microsoft Entra ID) : application + federated credential liée à l’org / projet / pipeline ADO (issuer, subject, audience)
- Côté Azure DevOps : service connection Azure Resource Manager en mode Workload Identity federation (automatique ou manuel) ; permissions sur la souscription (RBAC)
- Côté Terraform : provider
azurermvia OIDC (ARM_USE_OIDC,ARM_CLIENT_ID,ARM_TENANT_ID,ARM_SUBSCRIPTION_ID, éventuellementARM_OIDC_TOKEN/ tâches ADO qui injectent le token) - Comparer brièvement : secret SP vs WIF (sécurité, rotation, audit)
- Atelier / démo formateur : créer App Reg + federated credential, service connection ADO,
terraform plandepuis un pipeline
Réf. doc : AzureRM provider, Multiple providers / aliases, Authenticating to Azure using OpenID Connect, ADO — Azure Resource Manager service connections (WIF).
Jour 3 — Modules et configuration avancée
| Module | Contenu |
|---|---|
| 7 — Modules | Module local, design dépôt, tfvars multi-env, bonnes pratiques |
| 8 — Variables et types | Contraintes de types ; naming + validations ; nullable / ephemeral / sensitive ; path.* / workspace |
| 9 — Outputs | Exposition, outputs sensibles ; precondition ; bloc check |
| 10 — Config avancée | count / for_each / for ; dynamic ; fonctions HCL ; provisioners + self |
Jour 3 — à détailler : design du dépôt, modules et découpage multi-repos
Partie prioritaire pour passer du « petit lab en un dossier » à une architecture d’équipe (suite naturelle du module 5 Basics — découpage main / variables / outputs seulement).
Objectif pédagogique : donner des critères de décision, pas une recette unique — puis ancrer avec un cas d’étude bout en bout.
Organisation des fichiers .tf (racine vs modules)
- Combien de fichiers à la racine ? (
providers.tf,versions.tf,main.tf,network.tf,storage.tf,variables.tf,outputs.tf,locals.tf…) - Quand fusionner vs séparer par domaine (réseau, identité, data, compute) ou par couche
- Taille / lisibilité : règles du type « un fichier = un périmètre métier clair » ; éviter le
main.tfde 800 lignes - Où placer
terraform.tfvars.example, fichiers par environnement (dev.tfvars,prod.tfvars),backendconfig - Lien avec le state : un dossier racine Terraform = un state (rappel) ; impact du découpage sur les
outputconsommés ailleurs
Organisation des tfvars par environnement (mémo formateur)
Question fréquente pendant Basics (ne pas dérouler le cours Basics) : « On met un dossier environnements/dev|uat|prd ? »
Réponse courte à donner :
- Oui pour les tfvars : même code
.tfà la racine + un fichier (ou un petit dossier) de valeurs par env, chargé avec-var-file - Non / prudence : recopier tout le HCL dans
environments/dev/*.tf,uat/*.tf,prd/*.tf— risque de drift du code entre envs
Motifs recommandés (à comparer sans dogme) :
# A — simple (souvent suffisant)
env/
dev.tfvars
uat.tfvars
prd.tfvars
# B — plusieurs fichiers de valeurs par env (toujours sans dupliquer les .tf)
environments/
dev/
terraform.tfvars # ou values.tfvars + -var-file
uat/
…
prd/
…
Commande type : terraform plan -var-file=env/prd.tfvars (CI / scripts : même idée).
À distinguer en cours Associate :
- tfvars par env (valeurs) vs workspaces CLI / TFC (states séparés) — ce n’est pas interchangeable aveuglément
- secrets : pas dans des tfvars versionnés ; injection CI / Key Vault / variables TFC
- quand un repo ou stack séparé par env devient pertinent (envs très divergents, droits, cadence de release)
- lien avec le cas d’étude jour 3 (étape « équipe / prod »)
Prérequis élèves : Basics module 4 — fournir les valeurs (-var-file, pas d’arborescence multi-env).
Modules : quoi extraire, comment structurer
- Signaux qu’il est temps de modulariser (réutilisation, équipes, env multiples, même bloc copié 3 fois)
- Structure type d’un module local :
main.tf,variables.tf,outputs.tf,README.md, exemples - Interface du module : variables d’entrée minimales, outputs stables, versioning si module Registry / Git tag
- Racine orchestratrice vs modules métier (
network,compute,identity…) — qui appelle qui - Anti-patterns : modules fourre-tout, trop de paramètres, couplage fort via 20 outputs
Un repo ou plusieurs ?
- Monorepo IaC : tout le Terraform d’un produit / d’une plateforme dans un dépôt — avantages (revue, cohérence), limites (droits, CI, taille)
- Repo par domaine : réseau, landing zone, applicatif — avantages (ownership), limites (dépendances, coordination des releases)
- Repo par environnement vs même code + tfvars / workspace / branche — comparer sans dogme
- Ce qui vit dans le repo Terraform :
.tf,.terraform.lock.hcl, docs, exemples — vs ce qui reste hors repo (state distant, secrets, pipelines dans repo séparé ?) - Consommation cross-repo :
terraform_remote_state, outputs publiés, contrat entre équipes
Cas d’étude bout en bout (à construire)
Proposer un scénario réaliste Azure (ex. petite plateforme lab : landing zone light + app) et faire évoluer le design en 3 étapes :
- Basics rappel — un dossier, state local, fichiers séparés (ce que les élèves connaissent déjà)
- Modularisation — extraction
modules/network,modules/app, racine qui compose ; variables / outputs entre modules - Équipe / prod — backend distant, séparation env ou repos, CI (
plansur PR,applycontrôlé), qui possède quel state
Livrables attendus pour les participants : arborescence cible, tableau « quoi va où », schéma des states et des dépendances, liste des trade-offs assumés.
Prérequis pédagogiques : atelier 4 Basics (découpage fichiers + outputs) ; aperçu module 6 Basics (idée de module).
Module 8 — à détailler : contraintes de types (approfondissement)
Suite du catalogue déjà vu en Basics — types (string / number / bool / list / set / map / object / tuple / any) : ici on passe de « connaître la carte » à concevoir des interfaces solides (racine + modules).
- Rappel express puis écarts fréquents :
listvssetvstuple;map(string)vsobject({…})(déjà contrastés en Basics — ancrer en atelier) optional()dans lesobject: champs optionnels, défauts, impact sur les tfvars et les appelsmodule- Compositions :
list(object({…})),map(object({…})), objets imbriqués — lire et écrire une signature de module Registry - Quand choisir quoi (critères) :
- plusieurs éléments homogènes ordonnés →
list - ensemble unique sans ordre →
set - tags / dictionnaire homogène →
map - config structurée hétérogène →
object - positions fixes typées →
tuple(rare ; quand le documenter) any: presque jamais en interface publique de module- Erreurs de type : messages Terraform, conversion (
tolist,tomap, …), pièges tfvars / JSON - Lien modules : variables d’entrée minimales mais typées strictement ; outputs typés pour le consommateur
- Atelier : refactorer une config « plates » (beaucoup de
string) enobject/list(object)pour subnets (name + cidr + déléguations éventuelles) ;planqui valide la forme avant Azure - Croiser avec le lab naming / regex et les mémos
nullable/validation
Prérequis : types Basics + list vs object + exercice optionnel atelier 3.
Module 8 — à détailler : naming convention + validations
Partie dédiée (au-delà de l’aperçu Basics — un seul exemple contains au module 4) :
- Construire une naming convention d’équipe (préfixes type, projet, env, région, alias…) — règles écrites + exemples Azure (RG, VNet, storage…)
- Les encoder en Terraform :
locals, modèles de chaînes,format/ interpolation - Validations sur les variables d’entrée :
validation { condition / error_message }, jeux de valeurs autorisées (contains), longueurs, regex (can(regex(...))), contraintes storage (minuscules, 3–24 car.) - Faire échouer tôt (
plan) plutôt que découvrir l’erreur à l’applyAzure - Lien gouvernance : tags obligatoires + Policy (aperçu) vs validation Terraform
Opérateurs dans condition (mémo Associate — pas Basics)
La condition d’un bloc validation est une expression booléenne. À enseigner explicitement (souvent croisé avec ternaires / fonctions) :
| Opérateurs | Rôle | Exemple dans une validation |
|---|---|---|
== != |
Égalité / différence | var.env == "lab" |
> >= < <= |
Comparaison numérique (ou longueur) | length(var.prefix) >= 3 |
&& \|\| |
Et / Ou logiques | var.env == "lab" && length(var.alias) > 0 |
! |
Négation | !contains(local.forbidden, var.sku) |
Exemples à faire vivre en lab :
variable "node_count" {
type = number
validation {
condition = var.node_count >= 1 && var.node_count <= 5
error_message = "node_count doit être entre 1 et 5."
}
}
variable "alias" {
type = string
validation {
condition = length(var.alias) >= 2 && can(regex("^[a-z0-9]+$", var.alias))
error_message = "alias : minuscules/chiffres, au moins 2 caractères."
}
}
Fonctions fréquentes dans condition (mémo Associate)
| Expression | Rôle | Exemple |
|---|---|---|
contains(list, value) |
Valeur dans une liste autorisée | contains(["lab", "dev", "prod"], var.env) — déjà en aperçu Basics |
can(expr) |
true si expr réussit (pas d’erreur) |
Tester une conversion ou un regex sans faire planter le plan |
can(tonumber(…)) |
La chaîne est-elle un nombre ? | can(tonumber(var.port_str)) avant usage numérique |
can(regex(pattern, str)) |
Motif OK ? | Naming / alias (lab regex RG–VNet–subnet) |
alltrue(list(bool)) |
Toutes les conditions sont true |
Plusieurs règles sur une list / object |
anytrue(list(bool)) |
Au moins une est true |
Variante « ou » sur une liste de tests |
Exemples :
# Jeu de valeurs (rappel / approfondir)
validation {
condition = contains(["switzerlandnorth", "switzerlandwest"], var.location)
error_message = "location non autorisée."
}
# can + tonumber : refuser une fausse « chaîne nombre »
variable "port" {
type = string
validation {
condition = can(tonumber(var.port))
error_message = "port doit être numérique (ex. \"443\")."
}
}
# can + regex (naming)
validation {
condition = can(regex("^[a-z0-9]+$", var.alias))
error_message = "alias : uniquement a-z et 0-9."
}
# alltrue : plusieurs checks d’un coup (ex. liste de CIDR ou d’alias)
variable "extra_cidrs" {
type = list(string)
validation {
condition = alltrue([
for c in var.extra_cidrs : can(cidrhost(c, 0))
])
error_message = "chaque entrée de extra_cidrs doit être un CIDR valide."
}
}
À retenir : can(...) absorbe l’erreur (renvoie false) — idéal en validation ; sans can, un tonumber("abc") ou un mauvais regex peut faire échouer autrement.
Règles pédagogiques :
conditiondoit s’évaluer àtruepour accepter la valeur (sinonerror_message)- Préférer des messages actionnables (quoi corriger)
- Plusieurs blocs
validationpossibles sur la même variable (une règle = un message) - Croiser avec le catalogue fonctions (module 10) :
contains,can,tonumber,alltrue/anytrue,length, ternaires? :
Lab proposé — regex + naming (RG / VNet / subnet)
Idée forte pour ancrer validations + regex (suite naturelle du nommage Basics) :
- Écrire la convention (ex. équipe lab) :
- Resource group :
rg-<projet>-<env>-<alias>(minuscules, tirets) - VNet :
vnet-<projet>-<env>-<alias> - Subnet :
snet-<projet>-<env>-<alias>-<rôle>(ex.…-app) - Construire les noms avec
locals+format/ interpolation à partir devar.project,var.environment,var.alias(et rôle subnet) - Valider par regex sur les variables d’entrée et/ou sur un local « nom candidat » :
can(regex("^rg-[a-z0-9]+-[a-z]+-[a-z0-9]+$", local.rg_name))(ajuster le motif à la convention retenue)- Motifs distincts pour
vnet-…etsnet-…(longueur, caractères autorisés Azure) - Tester les motifs dans
terraform console(regex,can) avant de figer lesvalidation - Cas négatifs : tfvars avec mauvais alias / majuscules / underscore →
planéchoue avecerror_messageclair - Cas positifs : noms conformes →
planOK ; déployer RG + VNet + subnet (lab léger) pour voir le résultat dans le portail
Livrable élève : petite fiche « convention » + variables.tf / locals.tf avec validations ; 2–3 exemples tfvars (bon / mauvais).
Prérequis pédagogiques : Nommage et tags (Basics) ; validation en aperçu au module 4 Basics ; atelier 3 Basics (variables / locals).
Module 8 — à détailler : arguments avancés des variable (nullable, ephemeral, sensitive)
Suite de la répartition avec Basics module 4 (type / default / description / sensitive court / validation aperçu) :
nullable—nullvs valeur omise ;nullable = false; impact sur les interfaces de modules ; lien avecoptional()dans lesobjectephemeral— variables / valeurs éphémères (write-only) : ne pas traiter comme unsensitiveclassique ; ce qui entre (ou non) dans le state ; versions Terraform concernées ; cas d’usage (mots de passe, tokens injectés une fois)sensitiveen profondeur — variables et outputs :- Insister :
sensitivemasque la CLI/UI, le secret est quand même dans le tfstate (souvent en clair) dès qu’il est référencé par une ressource / un output - Démo formateur :
applyavec un faux secret →terraform outputmasqué → ouvrir / grepper le state → valeur visible - State local vs distant (chiffrement, ACL, lock) ; TFC / variables d’espace de travail
- Ne jamais committer state ni tfvars secrets ; alternatives (Key Vault, injection CI,
ephemeral/ write-only selon version) - Tableau récap « attribut → Basics ou Associate » pour les élèves qui reviennent du cours d’1 jour
- Atelier : validation +
nullablesur une interface de module ; démo courteephemeral/ write-only si la version lab le permet
Prérequis : anatomie variable + section sensitive Basics ; validations croisées avec le mémo naming ci-dessus.
Module 9 — à détailler : precondition sur les output (et voisinage)
Hors scope Basics — si la question arrive en session 1 jour : « on peut bloquer un output ? » → pointer ici, ne pas dérouler.
Prérequis élèves : Basics module 5 — outputs (value, sensitive, terraform output / -raw / -json).
precondition dans un bloc output
- Bloc imbriqué
precondition { condition = … ; error_message = "…" }dans unoutput - Évalué au plan / apply : si
conditionestfalse, Terraform échoue avec le message (l’output n’est pas « publié » tranquillement) - Cas d’usage : refuser d’exposer une valeur incohérente (ex. URL vide, ID absent, env interdit), garde-fou après création / calcul, côté contrat du module
- Lien avec
sensitive: un output peut êtresensitive = trueet avoir uneprecondition— rôles différents (masquage vs refus)
Exemple pédagogique (à adapter en lab) :
output "app_url" {
value = "https://${azurerm_linux_web_app.app.default_hostname}"
precondition {
condition = length(azurerm_linux_web_app.app.default_hostname) > 0
error_message = "Hostname applicatif vide — ne pas exposer d’URL."
}
}
Ne pas confondre (tableau formateur)
| Mécanisme | Où | Quand ça joue | Idée |
|---|---|---|---|
variable → validation |
entrée | avant / à la fourniture des valeurs | « mauvaise entrée » |
output → precondition |
sortie | plan/apply sur l’output | « mauvaise valeur à exposer » |
resource → lifecycle → precondition / postcondition |
ressource | plan/apply (avant/après) | garde-fous sur la ressource |
Bloc check |
racine (Terraform 1.5+) | plan / apply | assertions multi-ressources — détail au module 9 — check |
Atelier / démo
- Output simple sans
precondition(rappel Basics) - Ajouter une
preconditionqui échoue volontairement → lire le message - Corriger la condition →
planOK ; montrerterraform output/-json - Option : même idée sur une
resource(lifecycle.precondition) pour contraster
Doc officielle : Custom Conditions (preconditions / postconditions) ; outputs dans Output Values.
Module 8 — à détailler : références / « variables internes » Terraform
Distinguer clairement ce que Basics appelle déjà locals (valeurs dérivées écrites dans le code) des références built-in fournies par Terraform — parfois dites « variables internes » en formation.
| Concept | Exemple | Rôle |
|---|---|---|
locals (rappel Basics) |
local.rg_name |
Valeur calculée par l’auteur du code |
var. |
var.prefix |
Entrée fournie de l’extérieur |
path.module |
chemin du module courant | Fichiers relatifs au module (file("${path.module}/policy.json")) |
path.root |
chemin du module racine | Ancrage depuis un sous-module |
path.cwd |
répertoire de travail au moment du plan | Moins courant ; prudence CI |
terraform.workspace |
nom du workspace actif | Suffixes env / logique par workspace (avec limites) |
terraform.앱version |
version du binaire | Diagnostics / conditions rares |
À enseigner :
path.modulevspath.rootvspath.cwd— où placer un template / script ; différence racine / sous-module / cwd CI- Croiser avec
templatefile/file(mémo fonctions) et design multi-modules (mémo dépôt) - Ce que ce n’est pas : secrets, outputs métier Azure, ou
datasources
Démonstration formateur — les 3 path dans un output
Démo sans cloud (idéal en 2 min) : un output qui expose les trois chemins, puis terraform apply (ou console) et lecture des valeurs.
output "paths" {
description = "Comparer path.module, path.root et path.cwd"
value = {
module = path.module
root = path.root
cwd = path.cwd
}
}
Scénario oral :
- À la racine du projet : souvent
module≈root(chemins proches ou identiques selon le lancement) - Depuis un sous-module (même
outputdansmodules/network) :path.modulepointe vers le module,path.rootvers la racine qui appelle — l’écart devient visible - Lancer Terraform depuis un autre répertoire (ou en CI) :
path.cwdpeut diverger → pourquoi préférerpath.module/path.rootpourfile/templatefile
Variante élève : dupliquer l’output dans un module enfant et comparer terraform output -json paths (racine) vs output du module.
terraform.workspace et workspaces CLI — à développer
Un workspace CLI est un espace d’état nommé pour le même code (même dossier / même backend). Ce n’est pas un dossier Git différent : c’est une clé de state différente.
| Idée | Détail |
|---|---|
| Workspace par défaut | Toujours default au départ |
| Ce que ça isole | Le state (et donc les ressources gérées sous ce nom) |
| Ce que ça ne change pas | Les fichiers .tf (sauf si vous branchez la logique sur terraform.workspace) |
| Référence HCL | terraform.workspace → chaîne du workspace actif |
Commandes à maîtriser :
terraform workspace list
terraform workspace show
terraform workspace new lab-alice
terraform workspace select default
# terraform workspace delete lab-alice # seulement si state vide / OK à perdre
Backend local : states souvent sous terraform.tfstate.d/<nom>/terraform.tfstate.
Backend distant (Azure Storage, etc.) : le nom du workspace entre en général dans le chemin / la clé de l’objet state — à montrer après le module state distant (jour 2).
Usages légitimes de terraform.workspace dans le HCL :
locals {
name_suffix = terraform.workspace == "default" ? "lab" : terraform.workspace
# ou : lookup({ default = "lab", dev = "dev", prod = "prod" }, terraform.workspace, "lab")
}
output "current_workspace" {
value = terraform.workspace
}
# tags / noms
# tags = merge(var.tags, { workspace = terraform.workspace })
# name = "rg-demo-${terraform.workspace}"
Bonnes pratiques / limites (à dire clairement en cours) :
- Utile pour lab, démos, ou légères variantes de nommage / sizing via
lookup+ workspace - Ne pas s’en servir comme seule stratégie « dev / test / prod » sans discipline : un mauvais
workspace select→ apply sur le mauvais state - Secrets et valeurs d’env : plutôt tfvars / CI / TFC variables qu’une usine à
if terraform.workspace == "prod" - Distinguer workspaces CLI (
terraform workspace) et workspaces Terraform Cloud / HCP (UI, VCS, permissions) — même mot, produit différent ; détail jour 4 (module 11) - Lien exam TA-004 : rôle des workspaces, state séparé,
terraform.workspace
Lab / démo — workspace + terraform.workspace
output "current_workspace" { value = terraform.workspace }(+ éventuellement tag sur un RG lab)terraform workspace new demo-2→apply→ voir un 2ᵉ state / 2ᵉ jeu de ressources (préfixe différent si le nom dépend du workspace)workspace select default→ constater que l’autre infra n’apparaît plus dans ce state (state list)- Discussion : « Que se passe-t-il si j’applique en
defaulten croyant être surdemo-2? » - Option :
lookupd’un SKU ou d’une taille selonterraform.workspace
Mini-exercices (path + workspace)
- Démo / lab
output "paths"— avant d’utiliserfile("${path.module}/…") - Dans un module local,
file("${path.module}/files/readme.txt")outemplatefile(...)— échec si chemin « racine » erroné - Lab workspace ci-dessus (
show/new/select+ outputterraform.workspace) - Tableau élève : classer
var/local/path.*/terraform.workspace(entrée / dérivé / built-in / state nommé)
Prérequis : locals Basics ; aperçu modules ; state distant (jour 2) pour voir la clé workspace en backend ; TFC workspaces (jour 4) pour le contraste produit.
Module 10 — à détailler : itération (count, for_each, expressions for)
Hors journée Basics (là-bas : accès ponctuel list[0] / map["clé"] seulement — module 4).
count— N copies quasi identiques ;count.index; limites (réordonnancement → remplacements) ; quand l’éviterfor_each— surset/map(ettoset(list)si besoin) ; clé stable ;each.key/each.value; préféré dès que les éléments ont une identité (noms de subnets, etc.)countvsfor_each— tableau de décision ; impact sur les adresses state (resource[0]vsresource["app"])- Expressions
for— construire list/map dérivées ; filtreif; lien aveclocalset fonctions dynamicblocks — générer des blocs imbriqués (NSG rules, etc.) ; croiser avec ce mémo- Méta-arguments liés :
depends_ondéjà vu en Basics ; cycle de vie / replace si la cléfor_eachchange
Lab proposé — N subnets via for_each
- Variable
subnetsenlist(object({ name = string, cidr = string }))oumap(object({ cidr = string }))(clés = noms) - Un
azurerm_virtual_network+azurerm_subnetavecfor_each(pas trois blocs copiés) - Comparer (démo formateur ou variante élève) la même idée en
count— montrer pourquoifor_eachest plus sûr si on retire un subnet au milieu terraform state list: lire les adressesazurerm_subnet.this["app"]vs[0]- Option : expression
forpour dériver une map de tags / outputs par subnet
Prérequis : types + list/object Basics ; VNet/subnet atelier 2 ; contraintes de types Associate (mémo module 8).
Module 10 — à détailler : fonctions HCL + ternaires (exemples et exercices)
Partie prioritaire (reportée depuis Basics — merge / console en aperçu seulement). Catalogue officiel : Functions.
Pédagogie : tester d’abord dans terraform console, puis figer dans un locals { } ; un exemple Azure par fonction (naming, tags, storage, réseau).
Cœur à enseigner (liste demandée + autres utiles)
| Fonction / opérateur | Rôle | Exemple type (lab) |
|---|---|---|
join(sep, list) |
Coller une liste en chaîne | join("-", ["rg", var.project, var.env]) |
split(sep, str) |
Inverse de join |
split("-", "rg-tfbasics-lab") |
upper / lower |
Casse | lower(var.alias) avant un nom storage |
replace |
Substitution | replace(var.prefix, "-", "") (storage : pas de tirets) |
substr / trimspace |
Couper / nettoyer | Tronquer un nom trop long |
format |
Gabarit (%s, %d) |
format("snet-%s-%02d", var.env, 1) |
length |
Longueur chaîne ou collection | length(local.st_name) — storage Azure 3–24 car. |
contains |
Appartenance | contains(["lab", "dev", "prod"], var.env) |
lookup(map, key, default) |
Lire une map avec repli | lookup(var.sku, var.env, "Standard_LRS") |
merge |
Fusionner des maps | tags communs + tags env (aperçu Basics) |
concat / flatten / distinct |
Listes | fusionner CIDR, dédupliquer |
keys / values / element |
Collections | clés d’un for_each, 1er élément |
max / min / abs |
Nombres | plafonner un node_count, écart |
ceil / floor |
Arrondis | capacité, quotas |
timestamp() |
UTC ISO maintenant | tag created_at — non idempotent si mal placé |
timeadd(ts, duration) |
Décaler une date | timeadd(timestamp(), "24h") (TTL lab) |
formatdate(fmt, ts) |
Formater une date | formatdate("YYYYMMDD", timestamp()) |
Ternaire cond ? a : b |
Choix | var.env == "prod" ? "GRS" : "LRS" |
Comparaisons == != > … |
Conditions | avec validation et ternaires |
can / try / coalesce |
Robustesse | regex, can(tonumber(…)), valeur nulle |
alltrue / anytrue |
Agréger des booléens | validations sur listes (for + can) |
regex / regexall |
Motifs | croiser lab naming RG / VNet / subnet |
tolist / tomap / tostring / tonumber |
Conversions | set → list pour un index |
jsonencode |
Sérialiser | policy, app settings |
cidrsubnet / cidrhost |
Réseau | dériver des subnets depuis un VNet CIDR |
file / templatefile |
Fichiers | templates (secrets : prudence) |
Pièges :
timestamp()/uuid()dans un nom de ressource → risque de recreate à chaque apply ; plutôt lab / tag avecignore_changes, jamais un RG prodlength: chaîne et list/map — même nom, deux usageslookupvsvar.map[key]:lookupaccepte un défaut ; l’indexation échoue si la clé manque- Ternaire : les deux branches doivent être du même type
Mini-exercices (console puis locals)
- Storage name —
lower+replacedes tirets +substrsilength(...) > 24;validationaveclength(3–24) etcan(regex("^[a-z0-9]+$", …)) - Naming —
join("-", […])pourrg/vnet/snet;splitpour extraire l’alias - Tags —
merge(var.tags, { env = var.env });lookupd’un SKU par environnement - Ternaire — LRS vs GRS si
var.env == "prod";containspour les env autorisés - Dates (lab) —
formatdatepour un suffixe de lab ; expliquer pourquoi pastimestamp()dans un nom prod - Réseau (option) —
cidrsubnet(var.vnet_cidr, 4, 0)pour le subnet app
Atelier filé : locals.tf « boîte à outils » + terraform console en binôme ; un apply storage name suffit comme ancrage cloud.
Lien exam TA-004 : join, lookup, merge, ternaires, length / contains.
Prérequis : types + list vs object ; merge aperçu Basics ; validations / regex (mémo module 8).
Module 10 — à détailler : provisioners et objet self
Partie dédiée (hors scope Basics ; confusion fréquente avec le nom local self) :
- Blocs
provisioner:local-exec,remote-exec,file— quand ils s’exécutent (create / destroy) - Bloc
connection(SSH / WinRM) pourremote-exec/file - Objet spécial
self: uniquement dansprovisioner/connection— référence la ressource parente (self.public_ip, etc.) sans créer de cycle de dépendances - Distinguer clairement : nom local
"self"(convention Basics) ≠ objetself(provisioners) - Limites et bonnes pratiques Hashicorp : provisioners = dernier recours ; préférer cloud-init, images Packer, config management ; idempotence fragile
terraform_data(ou null_resource historique) pour des provisioners « hors » ressource métier- Atelier / démo :
local-execsimple + exempleselfdans unremote-exec(lab)
Réf. : Provisioners, References to the parent resource (self).
Jour 4 — Terraform Cloud et examen
| Module | Contenu |
|---|---|
| 11 — Terraform Cloud | Workspaces TFC/HCP (≠ CLI), VCS, variables ; ouverture collaboration / gouvernance |
| 12 — Révision examen | Méthodologie TA-004, carte des sujets, pièges, checklists |
Prérequis (rappel)
Bases IT et cloud, aisance en terminal, éditeur. Terraform CLI 1.16.5, dernière version stable. L’examen Associate 004 reste écrit pour Terraform 1.12. Compte HashiCorp pour HCP Terraform au jour 4. Compte Azure seulement pour la série d’ateliers Azure.
Certification
Examen en ligne ~60 min, ~50 questions, seuil ~70 %, validité 2 ans — HashiCorp Certified: Terraform Associate (004). L’examen n’est pas inclus dans le tarif du cours.