Aller au contenu

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

Niveau Intermédiaire

Effectif 6 à 12 participants (idéal : 8)

Certification Prépare à Terraform Associate (004) — examen non inclus

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 (local et random), 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) → plan propose ~ / 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 : plan propose recreate / erreurs selon le provider
  • terraform import (CLI) et bloc import (config moderne) : écrire le HCL avant / aligner les attributs, ID Azure correct, vérifier avec plan (idéalement no changes ou écarts compris)
  • terraform refresh / refresh au plan : ce que ça met à jour dans le state vs ce que ça ne « répare » pas
  • state 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 pendant plan/apply pour é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 + plan propre ; cas state rm contrô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 provider avec alias — multi-souscription / multi-tenant (ex. provider "azurerm" { alias = "prod" … } + provider = azurerm.prod sur les ressources)
  • Arguments avancés du provider AzureRM — environment, auxiliary_tenant_ids, auth Service Principal / MSI / OIDC, variables ARM_*
  • features en 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 azurerm via OIDC (ARM_USE_OIDC, ARM_CLIENT_ID, ARM_TENANT_ID, ARM_SUBSCRIPTION_ID, éventuellement ARM_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 plan depuis 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.tf de 800 lignes
  • Où placer terraform.tfvars.example, fichiers par environnement (dev.tfvars, prod.tfvars), backend config
  • Lien avec le state : un dossier racine Terraform = un state (rappel) ; impact du découpage sur les output consommé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 :

  1. Basics rappel — un dossier, state local, fichiers séparés (ce que les élèves connaissent déjà)
  2. Modularisation — extraction modules/network, modules/app, racine qui compose ; variables / outputs entre modules
  3. Équipe / prod — backend distant, séparation env ou repos, CI (plan sur PR, apply contrô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 : list vs set vs tuple ; map(string) vs object({…}) (déjà contrastés en Basics — ancrer en atelier)
  • optional() dans les object : champs optionnels, défauts, impact sur les tfvars et les appels module
  • 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) en object / list(object) pour subnets (name + cidr + déléguations éventuelles) ; plan qui 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’apply Azure
  • 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 :

  • condition doit s’évaluer à true pour accepter la valeur (sinon error_message)
  • Préférer des messages actionnables (quoi corriger)
  • Plusieurs blocs validation possibles 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) :

  1. Écrire la convention (ex. équipe lab) :
  2. Resource group : rg-<projet>-<env>-<alias> (minuscules, tirets)
  3. VNet : vnet-<projet>-<env>-<alias>
  4. Subnet : snet-<projet>-<env>-<alias>-<rôle> (ex. …-app)
  5. Construire les noms avec locals + format / interpolation à partir de var.project, var.environment, var.alias (et rôle subnet)
  6. Valider par regex sur les variables d’entrée et/ou sur un local « nom candidat » :
  7. can(regex("^rg-[a-z0-9]+-[a-z]+-[a-z0-9]+$", local.rg_name)) (ajuster le motif à la convention retenue)
  8. Motifs distincts pour vnet-… et snet-… (longueur, caractères autorisés Azure)
  9. Tester les motifs dans terraform console (regex, can) avant de figer les validation
  10. Cas négatifs : tfvars avec mauvais alias / majuscules / underscore → plan échoue avec error_message clair
  11. Cas positifs : noms conformes → plan OK ; 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 — null vs valeur omise ; nullable = false ; impact sur les interfaces de modules ; lien avec optional() dans les object
  • ephemeral — variables / valeurs éphémères (write-only) : ne pas traiter comme un sensitive classique ; ce qui entre (ou non) dans le state ; versions Terraform concernées ; cas d’usage (mots de passe, tokens injectés une fois)
  • sensitive en profondeur — variables et outputs :
  • Insister : sensitive masque 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 : apply avec un faux secret → terraform output masqué → 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 + nullable sur une interface de module ; démo courte ephemeral / 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 un output
  • Évalué au plan / apply : si condition est false, 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 être sensitive = true et avoir une precondition — 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

  1. Output simple sans precondition (rappel Basics)
  2. Ajouter une precondition qui échoue volontairement → lire le message
  3. Corriger la condition → plan OK ; montrer terraform output / -json
  4. 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.module vs path.root vs path.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 data sources

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 :

  1. À la racine du projet : souvent module ≈ root (chemins proches ou identiques selon le lancement)
  2. Depuis un sous-module (même output dans modules/network) : path.module pointe vers le module, path.root vers la racine qui appelle — l’écart devient visible
  3. Lancer Terraform depuis un autre répertoire (ou en CI) : path.cwd peut diverger → pourquoi préférer path.module / path.root pour file / 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

  1. output "current_workspace" { value = terraform.workspace } (+ éventuellement tag sur un RG lab)
  2. 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)
  3. workspace select default → constater que l’autre infra n’apparaît plus dans ce state (state list)
  4. Discussion : « Que se passe-t-il si j’applique en default en croyant être sur demo-2 ? »
  5. Option : lookup d’un SKU ou d’une taille selon terraform.workspace

Mini-exercices (path + workspace)

  1. Démo / lab output "paths" — avant d’utiliser file("${path.module}/…")
  2. Dans un module local, file("${path.module}/files/readme.txt") ou templatefile(...) — échec si chemin « racine » erroné
  3. Lab workspace ci-dessus (show / new / select + output terraform.workspace)
  4. 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’éviter
  • for_each — sur set / map (et toset(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.)
  • count vs for_each — tableau de décision ; impact sur les adresses state (resource[0] vs resource["app"])
  • Expressions for — construire list/map dérivées ; filtre if ; lien avec locals et fonctions
  • dynamic blocks — générer des blocs imbriqués (NSG rules, etc.) ; croiser avec ce mémo
  • Méta-arguments liés : depends_on déjà vu en Basics ; cycle de vie / replace si la clé for_each change

Lab proposé — N subnets via for_each

  1. Variable subnets en list(object({ name = string, cidr = string })) ou map(object({ cidr = string })) (clés = noms)
  2. Un azurerm_virtual_network + azurerm_subnet avec for_each (pas trois blocs copiés)
  3. Comparer (démo formateur ou variante élève) la même idée en count — montrer pourquoi for_each est plus sûr si on retire un subnet au milieu
  4. terraform state list : lire les adresses azurerm_subnet.this["app"] vs [0]
  5. Option : expression for pour 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 avec ignore_changes, jamais un RG prod
  • length : chaîne et list/map — même nom, deux usages
  • lookup vs var.map[key] : lookup accepte 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)

  1. Storage name — lower + replace des tirets + substr si length(...) > 24 ; validation avec length (3–24) et can(regex("^[a-z0-9]+$", …))
  2. Naming — join("-", […]) pour rg / vnet / snet ; split pour extraire l’alias
  3. Tags — merge(var.tags, { env = var.env }) ; lookup d’un SKU par environnement
  4. Ternaire — LRS vs GRS si var.env == "prod" ; contains pour les env autorisés
  5. Dates (lab) — formatdate pour un suffixe de lab ; expliquer pourquoi pas timestamp() dans un nom prod
  6. 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) pour remote-exec / file
  • Objet spécial self : uniquement dans provisioner / 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) ≠ objet self (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-exec simple + exemple self dans un remote-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.