WIKI Retour au Portfolio

Dernière mise à jour : 23 juin 2026

HCTA0-004 — Fiche concise (révision express)

IAC / BASICS Terraform = IaC déclaratif HashiCorp, lang HCL, open-source / décrit état désiré → compare état réel → applique le diff Workflow standard cloud-agnostique : initplanapply (mm pr tt provider) Cloud-agnostique = workflow standardisé, PAS ressources portables (aws_instance ≠ déployable sur Azure) Multi-cloud = plusieurs clouds dans 1 config (AWS+Azure+GCP) / Hybrid = public + on-prem (vSphere, k8s self-hosted) Pourquoi : éviter vendor lock-in, workloads par affinité, résilience, cloud-agnosticité Identifiant : aws_instance=type, mon_instance=nom logique, aws_instance.mon_instance=id complet (state, console)

PROVIDERS Provider = plugin qui traduit .tf (HCL) en appels API d'un service (AWS, Azure, GitHub, Docker…) Déclaré dans terraform { required_providers {} } (source+version) + bloc provider "x" {} pr config (region, creds) Téléchargés au terraform init → dans .terraform/ / changement de version = relancer init (-upgrade) BP : TJR figer version, JAMAIS secrets en clair (env vars / auth système) ALIAS : DEFAULT 1 seule config par provider / alias = "2" pr multi-region/multi-compte → réf via aws.2 Random provider : random_password, random_uuid (à déclarer dans required_providers OBLIGATOIRE)

COMMANDES CLÉS init : init dir, dl providers/modules, crée .terraform/, prépare backend validate : syntaxe+cohérence interne, statique, NE contacte PAS provider, NE lit PAS state (rapide, CI) fmt : reformate .tf / -recursive sous-dossiers / -check (exit≠0 si à reformater, CI) plan : simule diff config vs state / -out=tfplan sauve plan → apply tfplan / -refresh-only sync state seul apply : applique le diff / apply tfplan applique plan sauvé destroy : détruit TOUT ce que gère le state (prudence) refresh : sync state avec réalité cloud (OBSOLÈTE depuis 0.15 → préférer plan -refresh-only) import : intègre ressource existante au state (resource doit déjà être déclarée dans .tf !) → puis plan taint/untaint : marque/démarque ressource "à recréer" au prochain apply force-unlock <ID> : libère un lock bloqué graph : terraform graph | dot -Tpng > g.png (besoin graphviz) / get -update : dl/maj modules providers schema : structure JSON des providers (ressources, args obligatoires/facultatifs) console : REPL pr tester expressions/fonctions/types (ex type(...)) / -state= pr interroger le state show (tt le state lisible, -json) / output (-json pr CI)

STATE terraform.tfstate = carte ressources déployées (id, attrs, métadonnées) → lien config ↔ réalité Rôle : suivi ressources, base de plan/apply/destroy, dépendances implicites, stocke outputs SERIAL = n° version state (+1 à chaque modif) / LINEAGE = id unique du state (change si init ailleurs) .tfstate.backup créé à chaque apply (state d'avant dernier apply) State peut contenir des SECRETS en clair → chiffrer, accès restreint, JAMAIS éditer à la main (sauf expert) BP : versionner .tf mais PAS le state local, backend distant en équipe state list/show <r>/rm <r> (délie sans détruire)/mv <r> (rename ou déplace entre modules) Bloc moved {from to} = équivalent déclaratif de state mv / removed {from, lifecycle{destroy=false}} = state rm déclaratif

BACKEND Définit OÙ + COMMENT le state est stocké / DEFAULT = local Distant (S3, Azure Blob…) pr partage/sécu/CI/équipe / gère LOCKING + historique Indépendant des providers / changement de backend = re-init pr migrer le state LOCKING : 2e apply concurrent BLOQUÉ (ex S3+DynamoDB) → erreur acquiring lock .terraform.lock.hcl (≠ state lock) : verrouille versions+hashes providers (reproductibilité) / maj via init -upgrade

VARIABLES variable "x" { type, default, description } (type/desc facultatifs) Déclaration valeur : CLI -var="x=y" / fichier .tfvars (-var-file=) / env TF_VAR_x / default terraform.tfvars ou *.auto.tfvars chargé AUTO (sans -var-file) PRÉCÉDENCE (gagnant en 1er) : 1.CLI -var > 2..tfvars/.tfvars.json > 3.TF_VAR_ env > 4.default code validation { condition, error_message } (||=OU, &&=ET) → bloque si faux sensitive = true : masque dans outputs/logs MAIS visible en clair dans tfstate ! ephemeral = true (TF 1.10+) : JAMAIS dans state ni plan file (var/output/resource jetables) write_only (_wo) : envoyé au provider, jamais relu → pas dans state (+_wo_version à incrémenter) Règle secret : préférer ephemeral+write_only à sensitive seul

CUSTOM CONDITIONS validation (sur variable) : avant tout, BLOQUE precondition (lifecycle) : avant création/lecture ressource, BLOQUE postcondition (lifecycle) : après création/lecture, BLOQUE (self.x accessible) check {} (top-level) : fin de plan/apply, WARNING seulement (NE bloque PAS), monitoring continu

OUTPUTS / LOCALS / DATA output : expose valeurs post-apply (IP, id) / output, -json / réf inter-module module.NAME.OUTPUT locals : valeurs intermédiaires/calculs, NON modifiables de l'extérieur, centralise (DRY) datasource data "x" "y" {} : LIT ressource existante (non créée par TF) pr réutiliser ses attrs / réf data.x.y.attr Interpolation ${...} (ex web-${var.env})

TYPES Primitifs : string, number, bool Collections : list (ordonnée), set (non ordonnée sans doublon), map (clé-valeur) Complexe : object({...}), tuple([...])

RÉPÉTITION count = N copies identiques → réf r[0], r.*.id (splat) / index numérique for_each (map/set) → each.key/each.value / clés stables (mieux pr ajout/suppr sans tt recréer) Piège count vs for_each : count réindexe (suppr milieu = décale tt) / for_each clé = stable for expr : [for x in liste : f(x)] (liste) / {for i in l : k => v} (map) dynamic "bloc" { for_each, content {} } : génère blocs imbriqués dynamiquement splat r[*].id : tous les id des instances count/for_each

BLOCS SPÉCIAUX / LIFECYCLE nested blocks = blocs dans blocs (ex default_action {} dans listener) depends_on = [r] : dépendance EXPLICITE (ordre forcé) / implicite = réf attr d'une autre ressource (auto) lifecycle : create_before_destroy = true : crée AVANT de détruire (zéro downtime, besoin name_prefix si nom unique) prevent_destroy = true : bloque suppression accidentelle (VPC, S3 critiques) ignore_changes = [attr] ou [all] : ignore modifs hors TF (ex tags) replace_triggered_by = [r.attr] : force recréation si autre ressource/var change

PROVISIONERS (last resort) local-exec (cmd locale), remote-exec (sur la ressource), file (copie fichier) RIEN stocké dans le state (destroy n'annule pas la cmd) / À éviter sauf nécessité null_resource + provisioner pr exécuter du shell

FONCTIONS (utiles exam) try(a,b,default) : 1re sans erreur / coalesce() : 1er non-null/non-vide / lookup(map,k,default) : fallback sûr templatefile(path,{vars}) : injecte vars dans template (remplace data template_file ARCHIVÉ 2021) length, contains, element (cyclique), keys, values, zipmap, merge (clés dupl = remplacées) toset (dédup non ordonné) vs distinct (dédup garde ordre) / flatten, compact (vire ""), setproduct join(liste→str) ↔ split(str→liste) / upper/lower / cidrsubnet("10.0.0.0/16",8,1)10.0.1.0/24 / regex Heredoc <<EOF ... EOF (multi-lignes, user_data) / escapes \n \" \\ / \$ = var shell non interprétée par TF

DEBUG / SÉCU TF_LOG : TRACE>DEBUG>INFO>WARN>ERROR / TF_LOG_PATH= fichier / TF_LOG_CORE / TF_LOG_PROVIDER TRACE = gros volumes (clés API, payloads) → JAMAIS commit ces logs BP : Git pr versionner, code review, backend distant + locking, OpenTofu = state crypté

MODULES Module = unité réutilisable (input=variables, logique=main.tf, output=résultats) comme une fonction Pourquoi : réutilisabilité, organisation, collab, standardisation, maintenance (1 maj → partout) Fichiers : main.tf (ressources), variables.tf (inputs), outputs.tf (exposés), README.md Root/Parent module = entry point (appelle les childs) / Child module = appelé SOURCES : local ./modules/vpc / Git git::https://...git?ref=v1.2.0 / Registry terraform-aws-modules/vpc/aws VERSIONING : Git via ?ref=tag / Registry via version = "~> 4.0" (PAS de version pr modules locaux) ~> 4.0 = >=4.0 <5.0 (pessimistic) / figer pr éviter breaking changes, rollback facile Flux : child variable → root passe valeur à l'appel / child output → root module.NAME.OUTPUT Child module hérite providers du root, NE déclare PAS provider {} / cas alias → providers = { aws = aws.us } explicite Appel multiple : count/for_each sur le bloc module {} terraform get -update : dl/maj modules

MULTI-ENV

  1. Dossiers séparés : envs/dev, envs/prod (chacun sa config+state+tfvars) → flexible, explicite
  2. Workspaces CLI : workspace new/select dev, mm code, states isolés + tfvars par env BP : 1 tfvars par env + backend distant Chemins : path.cwd (où TF lancé) / path.root (root module) / path.module (module courant)

HCP / TERRAFORM CLOUD HCP Terraform (ex-Terraform Cloud) = SaaS HashiCorp : state remote, runs, gouvernance, collab Features : state versionné+locking auto, UI+API, runs sur push Git, RBAC, secrets/Vault, Sentinel/OPA, Run Tasks HIÉRARCHIE : Organisation > Projets > Workspaces (+ Teams, Module Registry privé, Policies) Organisation = top-level (entreprise/client) / SSO SAML, 2FA, éviter "owner" partout Projet = couche intermédiaire org↔workspaces / regroupe par domaine, RBAC par projet Workspace = unité d'exécution ISOLÉE (1 state, vars, runs, logs) / BP : 1 workspace = 1 composant = 1 env PAS de multi-env dans 1 workspace (pas de var env de switch)

WORKSPACES — MODES & VARS CLI-driven : terraform apply local + HCP backend / besoin terraform login + bloc cloud {} VCS-driven (GitOps) : lié à repo Git → push/PR = plan auto (prod, contrôle qualité) API-driven : runs via API REST HCP (CI/CD perso : GitLab CI, GH Actions) Vars : Terraform vars (TF_VAR_, visibles plan) / Env vars (creds) / Sensitive (masquées UI, MAIS dans tfstate) Variable Sets : définir 1 fois → appliquer à plusieurs ws/projets (creds partagés) Portée Var Set : Global (toute l'org) / Project-scoped / Workspace-scoped Précédence : workspace > project > global (var ws ÉCRASE var set) Run Triggers : apply OK sur ws A → plan auto sur ws B (chaînage déploiements) / MAX 20 sources / cycles INTERDITS / lance plan seul (apply selon config) Auto-apply (dev/préprod) vs Manual approval (prod/critique)

VCS / POLICY / CONNEXION VCS supportés : GitHub (+Enterprise), GitLab, Bitbucket Cloud (PAS Server), Azure DevOps / autres via API BP VCS : 1 branche = 1 env, PR+review obligatoire, protection de branche, auto-apply seulement sur branche protégée Sentinel = policy-as-code propriétaire HashiCorp (objets tfplan/tfconfig/tfstate natifs, stack HC) OPA = standard open-source CNCF, langage Rego (cohérence si déjà OPA ailleurs, anti lock-in) Enforcement (Sentinel & OPA) : advisory (warning) / soft-mandatory (bloque, admin override) / hard-mandatory (bloque, AUCUN override) Bloc cloud {} (TF 1.1+, MODERNE, sélection workspace par tags) vs backend "remote" (LEGACY) terraform login : ouvre navigateur → API token dans ~/.terraform.d/credentials.tfrc.json / logout pr révoquer CI/CD : PAS de login → env var TF_TOKEN_app_terraform_io Module Registry privé : modules publiés depuis repo VCS avec tag vX.Y.Z valide, visibles ds tte l'org Explorer : recherche/analyse ressources cross-workspaces (ex "qui utilise TF 1.5 ?") Change Requests : workflow review/approbation d'un plan avant apply (séparation rôles, audit trail, N approbateurs)

PIÈGES EXAM Précédence vars : CLI > tfvars > TF_VAR_ env > default count (index, réindexe) vs for_each (clé stable) → for_each pr ajout/suppr propre sensitive masque l'affichage MAIS reste en clair dans tfstate (≠ ephemeral/write_only) refresh obsolète → plan -refresh-only / import ne génère PAS la config (à écrire avant) child module hérite providers (pas de bloc provider, sauf alias via providers={}) check {} = warning (ne bloque pas) vs pre/postcondition = bloque Var Set précédence : workspace > project > global / cloud {} moderne > backend "remote" legacy terraform_remote_state (data source) : lit outputs d'un autre state distant pr partager des valeurs entre configs