[{"data":1,"prerenderedAt":503},["ShallowReactive",2],{"deck:terraform":3},{"deck":4,"reason":501,"status":502},{"id":5,"stem":6,"matiere":7,"name":8,"punchline":9,"deckStatus":10,"customMessages":11,"sources":21,"cards":25},"terraform","decks/terraform","informatique","Terraform","Relis le plan avant le yes","publié",{"low":12,"medium":15,"high":18},[13,14],"Resource, provider, state : le vocabulaire d'abord","Un plan se lit en entier",[16,17],"Le state n'a plus de mystère","for_each, lifecycle, moved : ça rentre",[19,20],"Backend verrouillé, states découpés, plan sauvegardé : prêt pour la prod","No changes. Your infrastructure matches the configuration.",[22],{"title":23,"url":24},"Terraform Documentation (HashiCorp)","https://developer.hashicorp.com/terraform/docs",[26,35,43,51,59,66,73,82,90,97,108,115,124,135,142,149,160,167,174,181,189,196,209,220,232,239,248,257,264,275,286,293,304,311,322,333,347,359,366,373,385,392,400,407,418,429,436,443,452,461,472,483,494],{"id":27,"tags":28,"question":31,"quick_answer":32,"details":33,"difficulty":34},"tfm-047",[7,5,29,30],"iac","concepts","Qu'est-ce qu'une approche **déclarative**, par opposition à impérative ?","On décrit l'état voulu, l'outil calcule les actions ; en impératif, on écrit les actions une par une","Impératif : un script `aws ec2 run-instances …` crée une instance de plus à chaque exécution. Déclaratif : « il existe une instance avec ces attributs », l'outil crée, modifie ou ne fait rien selon ce qui existe.\n\nC'est le modèle de Terraform, de Kubernetes, de SQL (`SELECT` dit quoi, pas comment).",4,{"id":36,"tags":37,"question":38,"quick_answer":39,"details":40,"dependsOn":41,"difficulty":42},"tfm-001",[7,5,29],"Qu'est-ce que **Terraform** ?","Un outil d'infrastructure as code déclaratif : on décrit l'infra en HCL, il applique les changements","Créé par HashiCorp en 2014. Multi-fournisseurs : le même langage pour AWS, GCP, Azure, Cloudflare, Kubernetes, GitHub… via des providers.\n\nOn n'écrit pas « crée une VM », on écrit « il existe une VM avec ces attributs ». Terraform compare à ce qui existe et fait le nécessaire.",[27],3,{"id":44,"tags":45,"question":47,"quick_answer":48,"details":49,"dependsOn":50,"difficulty":42},"tfm-002",[7,5,46],"hcl","Qu'est-ce que le **HCL** ?","HashiCorp Configuration Language : le langage à blocs des fichiers .tf","```\nresource \"aws_s3_bucket\" \"logs\" {\n  bucket = \"mon-app-logs\"\n}\n```\n\nUn bloc = un type, des labels, des arguments. Lisible par un humain, avec expressions, fonctions (`length()`, `format()`), boucles et conditions. Terraform accepte aussi du JSON, rarement utilisé.",[36],{"id":52,"tags":53,"question":55,"quick_answer":56,"details":57,"dependsOn":58,"difficulty":42},"tfm-003",[7,5,54],"providers","Qu'est-ce qu'un **provider** Terraform ?","Le plugin qui traduit les ressources déclarées en appels à l'API d'un service (aws, google, kubernetes…)","Terraform lui-même ne connaît aucun cloud. Le provider `aws` sait créer un `aws_instance`, le provider `helm` sait installer une `helm_release`.\n\nDéclaré dans `required_providers`, téléchargé par `terraform init` depuis le registry. Chaque provider a ses propres arguments (région, identifiants).",[36],{"id":60,"tags":61,"question":62,"quick_answer":63,"details":64,"dependsOn":65,"difficulty":42},"tfm-004",[7,5,46],"Structure d'un bloc **resource** ?","resource \"type\" \"nom_local\" { arguments } ; référencé ailleurs par type.nom_local.attribut","`resource \"aws_instance\" \"web\" { ami = … }` puis `aws_instance.web.id` ou `aws_instance.web.public_ip` dans une autre ressource.\n\nLe nom local n'existe que dans la config Terraform : il ne devient pas le nom de la ressource chez le cloud (c'est l'argument `name` ou un tag qui le fait).",[44],{"id":67,"tags":68,"question":69,"quick_answer":70,"details":71,"dependsOn":72,"difficulty":34},"tfm-005",[7,5,46],"Qu'est-ce qu'un bloc **data** (data source) ?","Une lecture d'information existante, sans la gérer : AMI la plus récente, VPC par défaut, compte courant","`data \"aws_ami\" \"ubuntu\" { most_recent = true ; filter { … } }` puis `data.aws_ami.ubuntu.id`.\n\nTerraform ne crée ni ne détruit jamais une data source. C'est le moyen de lire ce que quelqu'un d'autre (ou un autre state) a créé.",[60],{"id":74,"tags":75,"question":77,"quick_answer":78,"details":79,"dependsOn":80,"difficulty":81},"tfm-006",[7,5,76],"cli","Que fait **terraform init** ?","Télécharge les providers et modules, et configure le backend du state","Première commande dans un dossier de config, et à relancer après avoir ajouté un provider, un module ou changé de backend. Crée le dossier `.terraform/` (à ignorer dans Git) et le fichier `.terraform.lock.hcl` (à commiter).",[52],2,{"id":83,"tags":84,"question":86,"quick_answer":87,"details":88,"dependsOn":89,"difficulty":34},"tfm-010",[7,5,85],"state","Qu'est-ce que le **state** Terraform ?","Le fichier qui associe chaque ressource de la config à son identifiant réel chez le fournisseur","`terraform.tfstate`, du JSON. Sans lui, Terraform ne sait pas que `aws_instance.web` est l'instance `i-0abc…` : il en créerait une deuxième.\n\nIl stocke aussi les attributs lus (IP, ARN) et les dépendances. C'est la mémoire de Terraform entre deux exécutions.",[60],{"id":91,"tags":92,"question":93,"quick_answer":94,"details":95,"dependsOn":96,"difficulty":42},"tfm-007",[7,5,76],"Que fait **terraform plan** ?","Calcule et affiche ce que Terraform ferait (créer, modifier, recréer, détruire), sans rien toucher","Il rafraîchit l'état réel via les API, le compare au state et à la config, et liste les actions : `+` créer, `~` modifier sur place, `-/+` détruire puis recréer, `-` détruire.\n\nUn plan se lit en entier avant tout apply : un `-/+` inattendu sur une base de données, c'est une perte de données.",[74,83],{"id":98,"tags":99,"question":100,"quick_answer":101,"details":102,"dependsOn":103,"difficulty":42,"sources":104},"tfm-008",[7,5,76],"Que fait **terraform apply** ?","Exécute les changements du plan, après confirmation, et met à jour le state","Sans argument, il recalcule un plan et demande `yes`. Avec un fichier de plan (`terraform apply tfplan`), il applique exactement ce qui a été revu, sans question.\n\nLes ressources indépendantes sont créées en parallèle (10 par défaut).",[91],[105],{"title":106,"url":107},"Terraform Docs — terraform apply","https://developer.hashicorp.com/terraform/cli/commands/apply",{"id":109,"tags":110,"question":111,"quick_answer":112,"details":113,"dependsOn":114,"difficulty":81},"tfm-009",[7,5,76],"Que fait **terraform destroy** ?","Détruit toutes les ressources du state, après confirmation","C'est `plan -destroy` puis apply. Si une ressource porte `prevent_destroy`, le plan de destroy échoue en entier : rien n'est détruit tant que le bloc est là. Sur un environnement partagé, c'est la commande à ne jamais lancer sans relire le plan.",[98],{"id":116,"tags":117,"question":119,"quick_answer":120,"details":121,"dependsOn":122,"difficulty":123},"tfm-011",[7,5,85,118],"securite","Pourquoi le **state** est-il une donnée sensible ?","Il contient en clair les attributs des ressources, mots de passe et clés compris","Un `aws_db_instance` avec `password = …` : le mot de passe est dans le state. Un `sensitive = true` masque l'affichage, pas le contenu du fichier.\n\nConséquences : jamais de state dans Git, backend distant chiffré, accès restreint par IAM.",[83],5,{"id":125,"tags":126,"question":127,"quick_answer":128,"details":129,"dependsOn":130,"difficulty":123,"sources":131},"tfm-012",[7,5,85],"Qu'est-ce qu'un **backend** distant ?","Un stockage partagé du state (S3, GCS, HCP Terraform…) avec verrouillage, pour travailler à plusieurs","```\nterraform {\n  backend \"s3\" {\n    bucket = \"tfstate-prod\"\n    key    = \"network/terraform.tfstate\"\n    region = \"eu-west-3\"\n    use_lockfile = true\n  }\n}\n```\n\nUn state local sur un portable ne se partage pas et se perd. Le backend S3 verrouille nativement avec `use_lockfile` (expérimental en 1.10, stable en 1.11, où `dynamodb_table` devient déprécié) ; avant, il fallait une table DynamoDB.",[83],[132],{"title":133,"url":134},"Terraform Docs — S3 backend","https://developer.hashicorp.com/terraform/language/backend/s3",{"id":136,"tags":137,"question":138,"quick_answer":139,"details":140,"dependsOn":141,"difficulty":34},"tfm-013",[7,5,85],"À quoi sert le **verrou** (lock) du state ?","À empêcher deux apply simultanés sur le même state, qui le corrompraient","Terraform pose le verrou au début d'un plan ou apply et le lève à la fin. Un deuxième utilisateur reçoit `Error acquiring the state lock`.\n\nUn verrou resté coincé après un crash se lève avec `terraform force-unlock \u003Cid>`, après s'être assuré que personne ne travaille.",[125],{"id":143,"tags":144,"question":145,"quick_answer":146,"details":147,"dependsOn":148,"difficulty":123},"tfm-014",[7,5,85],"Qu'est-ce que le **drift** ?","Une différence entre l'infra réelle et le state, créée par une modification hors Terraform","Quelqu'un change un security group à la main dans la console : le state ne le sait pas. Au prochain `plan`, Terraform rafraîchit, voit l'écart et propose de **revenir à la config**.\n\nC'est voulu : la config est la vérité. Pour entériner le changement manuel, on modifie la config, pas l'inverse.",[91],{"id":150,"tags":151,"question":152,"quick_answer":153,"details":154,"dependsOn":155,"difficulty":42,"sources":156},"tfm-015",[7,5,46],"Comment déclarer une **variable** d'entrée ?","variable \"nom\" { type = string ; default = … ; description = … }, puis var.nom","Valeurs fournies, par ordre de priorité croissante : `default`, variables d'environnement `TF_VAR_nom`, `terraform.tfvars`, `*.auto.tfvars` (ordre lexical), puis `-var` et `-var-file` au même rang, dans l'ordre de la ligne de commande (le dernier gagne).\n\nUne variable sans `default` est obligatoire : Terraform la demande interactivement si rien ne la fournit.",[44],[157],{"title":158,"url":159},"Terraform Docs — Input variables, definition precedence","https://developer.hashicorp.com/terraform/language/values/variables",{"id":161,"tags":162,"question":163,"quick_answer":164,"details":165,"dependsOn":166,"difficulty":42},"tfm-016",[7,5,46],"À quoi sert un bloc **output** ?","À exposer une valeur (IP, ARN, endpoint) à la fin de l'apply, à un module parent ou à un autre state","`output \"db_endpoint\" { value = aws_db_instance.main.endpoint }`.\n\n`terraform output -json` le lit depuis un script ; un module parent lit `module.db.db_endpoint` ; un autre state le lit via `terraform_remote_state`. C'est l'interface d'un module.",[60],{"id":168,"tags":169,"question":170,"quick_answer":171,"details":172,"dependsOn":173,"difficulty":42},"tfm-017",[7,5,46],"À quoi sert un bloc **locals** ?","À nommer une expression réutilisée dans la config, sans en faire une variable d'entrée","`locals { name_prefix = \"${var.project}-${var.env}\" }` puis `local.name_prefix`.\n\nDifférence avec `variable` : une local se calcule, elle ne se fournit pas de l'extérieur.",[150],{"id":175,"tags":176,"question":177,"quick_answer":178,"details":179,"dependsOn":180,"difficulty":81},"tfm-018",[7,5,46],"Quel fichier de valeurs Terraform charge-t-il **automatiquement** ?","terraform.tfvars (et tout fichier *.auto.tfvars)","Les autres fichiers de valeurs se passent avec `-var-file=prod.tfvars`. Convention courante : un tfvars par environnement, choisi en ligne de commande ou par le wrapper (Terragrunt, Makefile, CI).",[150],{"id":182,"tags":183,"question":185,"quick_answer":186,"details":187,"dependsOn":188,"difficulty":34},"tfm-019",[7,5,184],"modules","Qu'est-ce qu'un **module** Terraform ?","Un dossier de configuration réutilisable, appelé avec des variables, qui renvoie des outputs","```\nmodule \"vpc\" {\n  source  = \"terraform-aws-modules/vpc/aws\"\n  version = \"5.8.1\"\n  cidr    = \"10.0.0.0/16\"\n}\n```\n\nTout dossier avec des `.tf` est un module ; celui où on lance `terraform apply` est le **module racine**. Un module encapsule un ensemble cohérent (un VPC avec ses sous-réseaux, un cluster EKS) derrière une interface.",[150,161],{"id":190,"tags":191,"question":192,"quick_answer":193,"details":194,"dependsOn":195,"difficulty":34},"tfm-020",[7,5,184],"Pourquoi **épingler** la version d'un module ?","Sinon terraform init -upgrade prend la dernière version et la config change sans commit","- Registry : `source = \"terraform-aws-modules/vpc/aws\"` + `version = \"5.8.1\"`\n- Git : `source = \"git::https://…//modules/vpc?ref=v1.4.0\"`\n- Local : `./modules/vpc`, pas de version (c'est le même dépôt)\n\nUne mise à jour de module se fait par un changement de `version` relu en PR, jamais par surprise.",[182],{"id":197,"tags":198,"question":200,"quick_answer":201,"details":202,"dependsOn":203,"difficulty":204,"sources":205},"tfm-021",[7,5,46,199],"pieges","Pourquoi préférer **for_each** à **count** pour créer plusieurs ressources ?","for_each identifie par clé stable ; avec count, supprimer l'élément 0 décale les index et recrée tout","`count = 3` crée `web[0]`, `web[1]`, `web[2]`. Retirer le premier de la liste : `[1]` devient `[0]`, Terraform détruit et recrée deux instances pour « corriger » les index.\n\n`for_each = toset([\"a\", \"b\", \"c\"])` crée `web[\"a\"]`… : retirer `\"a\"` ne touche que `\"a\"`. `count` reste pratique pour un simple interrupteur (`count = var.enabled ? 1 : 0`).",[60],7,[206],{"title":207,"url":208},"Terraform Docs — for_each","https://developer.hashicorp.com/terraform/language/meta-arguments/for_each",{"id":210,"tags":211,"question":212,"quick_answer":213,"details":214,"dependsOn":215,"difficulty":123,"sources":216},"tfm-022",[7,5,46],"Comment Terraform connaît-il l'**ordre** de création des ressources ?","Par les références entre elles : si B utilise A.id, A est créé avant B","Terraform construit un graphe de dépendances à partir des expressions. Pas besoin de déclarer l'ordre. Les ressources sans lien entre elles sont créées **en parallèle**.\n\n`depends_on = [aws_iam_role_policy.x]` sert aux dépendances que la config ne montre pas (une policy qui doit exister avant que l'instance ne démarre, sans qu'aucun attribut ne la référence).",[60],[217],{"title":218,"url":219},"Terraform Docs — depends_on","https://developer.hashicorp.com/terraform/language/meta-arguments/depends_on",{"id":221,"tags":222,"question":224,"quick_answer":225,"details":226,"dependsOn":227,"difficulty":34,"sources":228},"tfm-023",[7,5,223],"lifecycle","Que fait `lifecycle { prevent_destroy = true }` ?","Fait échouer tout plan qui détruirait la ressource","Garde-fou pour une base de données, un bucket de state, une zone DNS. L'erreur survient au plan, avant tout dégât.\n\nLimite : retirer le bloc `resource` de la config retire aussi la protection. Ça protège d'un `-/+` ou d'un `destroy`, pas d'une suppression de code.",[109],[229],{"title":230,"url":231},"Terraform Docs — lifecycle","https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle",{"id":233,"tags":234,"question":235,"quick_answer":236,"details":237,"dependsOn":238,"difficulty":123},"tfm-027",[7,5,76,199],"Que signifie **-/+** (must be replaced) dans un plan ?","L'attribut modifié ne peut pas changer sur place : Terraform détruira la ressource et la recréera","Certains attributs sont immuables côté API (`ami` d'une instance, `engine` d'une base, nom d'un bucket). Le provider les marque **ForceNew**.\n\nSur une base de données, un `-/+` = perte des données. Le plan l'écrit en toutes lettres : `# forces replacement`. À chercher dans chaque plan avant `yes`.",[91],{"id":240,"tags":241,"question":242,"quick_answer":243,"details":244,"dependsOn":245,"difficulty":123,"sources":246},"tfm-024",[7,5,223],"Que fait `lifecycle { create_before_destroy = true }` ?","Lors d'un remplacement, crée la nouvelle ressource avant de détruire l'ancienne","Par défaut Terraform détruit puis recrée : coupure. Avec cette option, pas d'interruption, à condition que les deux puissent coexister (pas de nom unique en conflit).\n\nUsage typique : launch template, certificat, security group référencé ailleurs.",[233],[247],{"title":230,"url":231},{"id":249,"tags":250,"question":251,"quick_answer":252,"details":253,"dependsOn":254,"difficulty":123,"sources":255},"tfm-025",[7,5,223],"Que fait `lifecycle { ignore_changes = [tags] }` ?","Terraform ne propose plus de corriger cet attribut s'il change hors de lui","Pour les attributs modifiés légitimement par autre chose : tags posés par un autoscaler, `desired_capacity` géré par l'auto scaling, dernière date de mise à jour.\n\nÀ utiliser chirurgicalement : chaque attribut ignoré est un bout d'infra que Terraform ne garantit plus.",[143],[256],{"title":230,"url":231},{"id":258,"tags":259,"question":260,"quick_answer":261,"details":262,"dependsOn":263,"difficulty":123},"tfm-026",[7,5,46],"Qu'est-ce qu'un bloc **dynamic** ?","Un bloc imbriqué répété à partir d'une collection (ex. une règle ingress par port)","```\ndynamic \"ingress\" {\n  for_each = var.ports\n  content {\n    from_port = ingress.value\n    to_port   = ingress.value\n    protocol  = \"tcp\"\n  }\n}\n```\n\nC'est le `for_each` des blocs internes, là où `for_each` sur une ressource répète la ressource entière.",[197],{"id":265,"tags":266,"question":267,"quick_answer":268,"details":269,"dependsOn":270,"difficulty":123,"sources":271},"tfm-028",[7,5,85],"Comment mettre sous Terraform une ressource **créée à la main** ?","Avec un bloc import (ou terraform import) : la ressource entre dans le state sans être recréée","```\nimport {\n  to = aws_s3_bucket.logs\n  id = \"mon-app-logs\"\n}\n```\n\nDepuis Terraform 1.5, le bloc `import` est déclaratif et `plan -generate-config-out=` écrit même la config correspondante. Après import, un `plan` doit être vide : sinon la config ne décrit pas fidèlement l'existant.",[83],[272],{"title":273,"url":274},"Terraform Docs — Import","https://developer.hashicorp.com/terraform/language/import",{"id":276,"tags":277,"question":278,"quick_answer":279,"details":280,"dependsOn":281,"difficulty":123,"sources":282},"tfm-029",[7,5,85],"Comment **renommer** une ressource dans la config sans la détruire et la recréer ?","Avec un bloc moved { from = …, to = … } (ou terraform state mv)","Renommer `aws_instance.web` en `aws_instance.frontend` sans rien dire = Terraform voit une ressource disparue et une nouvelle : `-` puis `+`.\n\nLe bloc `moved` (Terraform 1.1+) est versionné dans la config et s'applique partout où elle est déployée ; `state mv` agit sur un seul state, à la main.",[83],[283],{"title":284,"url":285},"Terraform Docs — moved block","https://developer.hashicorp.com/terraform/language/moved",{"id":287,"tags":288,"question":289,"quick_answer":290,"details":291,"dependsOn":292,"difficulty":34},"tfm-030",[7,5,85],"Que fait **terraform state rm** ?","Retire une ressource du state sans la détruire : Terraform l'oublie, elle continue d'exister","Pour confier une ressource à un autre state ou à un autre outil, ou sortir une ressource importée par erreur.\n\nLe pendant déclaratif depuis Terraform 1.7 : le bloc `removed { from = …, lifecycle { destroy = false } }`.",[265],{"id":294,"tags":295,"question":296,"quick_answer":297,"details":298,"dependsOn":299,"difficulty":42,"sources":300},"tfm-031",[7,5,76],"Comment forcer la **recréation** d'une ressource saine ?","terraform apply -replace=aws_instance.web","Remplace l'ancien `terraform taint`, déprécié depuis 0.15.2. Utile pour une instance corrompue ou pour rejouer un `user_data`.\n\nLe plan montre alors `-/+` sur cette seule ressource, et sur celles qui en dépendent par un attribut qui change.",[233],[301],{"title":302,"url":303},"Terraform Docs — terraform taint (deprecated)","https://developer.hashicorp.com/terraform/cli/commands/taint",{"id":305,"tags":306,"question":307,"quick_answer":308,"details":309,"dependsOn":310,"difficulty":123},"tfm-032",[7,5,76,199],"Pourquoi **-target** est-il à réserver aux urgences ?","Il applique un sous-ensemble et laisse le reste potentiellement incohérent avec la config","`terraform apply -target=module.db` ignore tout le reste, y compris des dépendances qui auraient dû changer. Terraform affiche d'ailleurs un avertissement.\n\nUsage légitime : débloquer un apply cassé en deux temps. Usage à proscrire : « déployer juste ce module » au quotidien, c'est le signe que le state est trop gros et devrait être découpé.",[98],{"id":312,"tags":313,"question":314,"quick_answer":315,"details":316,"dependsOn":317,"difficulty":123,"sources":318},"tfm-033",[7,5,85],"Qu'est-ce qu'un **workspace** Terraform ?","Un state séparé pour la même configuration (dev, staging, prod), sélectionné par terraform workspace select","Même code, `terraform.workspace` comme variable, un state par workspace dans le même backend.\n\nLimite : tous les environnements partagent le même backend et les mêmes identifiants, et un oubli de `workspace select` applique en prod ce qu'on voulait en dev. Beaucoup d'équipes préfèrent un **dossier par environnement** avec son propre backend.",[125],[319],{"title":320,"url":321},"Terraform Docs — Workspaces","https://developer.hashicorp.com/terraform/language/state/workspaces",{"id":323,"tags":324,"question":325,"quick_answer":326,"details":327,"dependsOn":328,"difficulty":123,"sources":329},"tfm-034",[7,5,46,199],"Pourquoi les **provisioners** (remote-exec, local-exec) sont-ils déconseillés ?","Ils cassent le modèle déclaratif : pas de plan, pas d'idempotence, pas de retour en arrière","Un `remote-exec` exécute des commandes SSH après création : Terraform ne sait ni ce qu'elles font ni les rejouer. La doc HashiCorp les classe en « dernier recours ».\n\nAlternatives : `user_data` / cloud-init pour l'initialisation d'une VM, une image préconstruite (Packer), un outil de configuration (Ansible) lancé séparément.",[98],[330],{"title":331,"url":332},"Terraform Docs — Provisioners","https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax",{"id":334,"tags":335,"question":336,"quick_answer":337,"details":338,"dependsOn":339,"difficulty":34,"sources":340},"tfm-035",[7,5,118],"Que fait `sensitive = true` sur une variable ou un output ?","Masque la valeur dans les sorties de plan, d'apply et de la liste des outputs, pas dans le state","Affiché `(sensitive value)` dans le plan, l'apply et `terraform output` sans argument. `terraform output nom`, `-raw` et `-json` l'affichent en clair (ils servent aux scripts). Et la valeur est toujours en clair dans le fichier de state : `sensitive` est un cache-misère d'affichage, pas une protection.",[116,150],[341,344],{"title":342,"url":343},"Terraform Docs — terraform output","https://developer.hashicorp.com/terraform/cli/commands/output",{"title":345,"url":346},"Terraform Docs — Sensitive data in state","https://developer.hashicorp.com/terraform/language/state/sensitive-data",{"id":348,"tags":349,"question":351,"quick_answer":352,"details":353,"dependsOn":354,"difficulty":34,"sources":355},"tfm-036",[7,5,350],"versions","À quoi sert **.terraform.lock.hcl** et faut-il le commiter ?","Il fige les versions exactes des providers ; oui, on le commite","Comme un `package-lock.json` : `required_providers` donne une contrainte (`~> 5.0`), le lockfile enregistre la version résolue et son hash. Toute l'équipe et la CI utilisent la même.\n\n`terraform init -upgrade` le met à jour volontairement.",[74],[356],{"title":357,"url":358},"Terraform Docs — Dependency lock file","https://developer.hashicorp.com/terraform/language/files/dependency-lock",{"id":360,"tags":361,"question":362,"quick_answer":363,"details":364,"dependsOn":365,"difficulty":34},"tfm-037",[7,5,350],"Où contraint-on la version de Terraform et des providers ?","Dans le bloc terraform { required_version, required_providers }","```\nterraform {\n  required_version = \">= 1.6\"\n  required_providers {\n    aws = { source = \"hashicorp/aws\", version = \"~> 5.50\" }\n  }\n}\n```\n\n`~> 5.50` accepte 5.50 à 5.x, pas 6.0. Sans contrainte ni lockfile commité, un provider prend la dernière version au prochain `init` sur une machine neuve.",[52],{"id":367,"tags":368,"question":369,"quick_answer":370,"details":371,"dependsOn":372,"difficulty":81},"tfm-038",[7,5,76],"Que vérifie **terraform validate** ?","La syntaxe et la cohérence interne de la config, sans appeler aucune API","Il attrape une référence à une ressource inexistante, un argument inconnu, un type faux ; il ne sait pas si l'AMI existe ni si les identifiants sont bons. Tourne en CI avant le plan, après `init` (il a besoin des schémas des providers).",[74],{"id":374,"tags":375,"question":377,"quick_answer":378,"details":379,"dependsOn":380,"difficulty":42,"sources":381},"tfm-039",[7,5,376],"ecosysteme","Qu'est-ce qu'**OpenTofu** ?","Le fork communautaire de Terraform sous Linux Foundation, créé après le passage de Terraform en licence BSL","En août 2023, HashiCorp a fait passer Terraform de la licence MPL (open source) à la BSL, qui interdit de proposer Terraform en service hébergé concurrent de HashiCorp (IBM depuis février 2025) ; l'usage pour sa propre infra reste libre. OpenTofu (d'abord OpenTF) est né dans la foulée, compatible avec les configs et providers existants, sous licence MPL.\n\nLe binaire s'appelle `tofu`. Les deux projets divergent lentement (OpenTofu a par exemple le chiffrement du state).",[36],[382],{"title":383,"url":384},"OpenTofu — Introduction","https://opentofu.org/docs/intro/",{"id":386,"tags":387,"question":388,"quick_answer":389,"details":390,"dependsOn":391,"difficulty":34},"tfm-040",[7,5,29],"Terraform est-il **idempotent** ?","Oui : réappliquer une config inchangée ne fait rien (No changes)","C'est la conséquence du modèle déclaratif : Terraform compare et n'agit que sur l'écart. Un script impératif (`aws ec2 run-instances`) crée une instance de plus à chaque exécution.\n\nLes exceptions viennent des providers ou de valeurs qui changent à chaque exécution (`timestamp()`, hash d'un fichier régénéré).",[91],{"id":393,"tags":394,"question":395,"quick_answer":396,"details":397,"dependsOn":398,"difficulty":399},"tfm-041",[7,5,76],"Un **apply échoue** à mi-chemin : dans quel état est-on ?","État partiel cohérent : ce qui a été créé est dans le state, le reste non ; on corrige et on relance","Terraform écrit le state au fur et à mesure. Les ressources créées avant l'erreur ne seront pas recréées au prochain apply. Il n'y a pas de rollback automatique.\n\nSi l'erreur vient d'un quota, d'une API indisponible ou d'une faute de config, on corrige et `apply` reprend là où ça s'est arrêté.",[98],6,{"id":401,"tags":402,"question":403,"quick_answer":404,"details":405,"dependsOn":406,"difficulty":123},"tfm-042",[7,5,118],"Où mettre un **secret** dont une ressource a besoin, sans le commiter ?","Hors du code : lu à l'apply depuis un gestionnaire de secrets (data source)","Le réflexe : `data \"aws_secretsmanager_secret_version\" \"db\"` puis `password = data….secret_string`. Le secret n'est ni dans les `.tf` ni dans les `.tfvars` versionnés.\n\nPar défaut il finira quand même dans le state, d'où le backend distant chiffré et restreint ; les arguments write-only (Terraform 1.11+) évitent ça. Pour un secret passé à la main, `TF_VAR_nom` en variable d'environnement ; un `.tfvars` contenant des secrets va dans `.gitignore`.",[116,67],{"id":408,"tags":409,"question":410,"quick_answer":411,"details":412,"dependsOn":413,"difficulty":123,"sources":414},"tfm-043",[7,5,85],"Comment une config lit-elle les **outputs d'un autre state** ?","Avec la data source terraform_remote_state, qui lit le backend de l'autre state","```\ndata \"terraform_remote_state\" \"network\" {\n  backend = \"s3\"\n  config  = { bucket = \"tfstate\", key = \"network/terraform.tfstate\", region = \"eu-west-3\" }\n}\n# data.terraform_remote_state.network.outputs.vpc_id\n```\n\nC'est le lien entre des states découpés (réseau, cluster, applis). Seuls les outputs sont exposés, mais l'appelant doit avoir le droit de lire tout le snapshot du state, secrets compris : alternative plus fine, des data sources ciblées (`data \"aws_vpc\"` par tag).",[161,125],[415],{"title":416,"url":417},"Terraform Docs — terraform_remote_state","https://developer.hashicorp.com/terraform/language/state/remote-state-data",{"id":419,"tags":420,"question":421,"quick_answer":422,"details":423,"dependsOn":424,"difficulty":123,"sources":425},"tfm-044",[7,5,76],"Pourquoi enregistrer le plan dans un fichier (`plan -out=tfplan`) avant l'apply en CI ?","Pour appliquer exactement ce qui a été revu, pas un plan recalculé entre-temps","Entre la revue du plan et le clic « apply », l'infra ou le code peuvent avoir changé. `terraform apply tfplan` refuse d'appliquer si l'état a bougé depuis le plan.\n\nPipeline type : `plan -out` sur la PR, lecture humaine, `apply tfplan` au merge. Le fichier de plan contient des valeurs sensibles : artefact à protéger.",[98,334],[426],{"title":427,"url":428},"Terraform Docs — terraform plan","https://developer.hashicorp.com/terraform/cli/commands/plan",{"id":430,"tags":431,"question":432,"quick_answer":433,"details":434,"dependsOn":435,"difficulty":81},"tfm-045",[7,5,76],"À quoi sert **terraform console** ?","À tester une expression HCL ou lire une valeur du state de façon interactive","`> aws_instance.web.private_ip`, `> cidrsubnet(\"10.0.0.0/16\", 8, 3)`, `> length(var.ports)`. Pratique pour comprendre une fonction ou vérifier un attribut sans relancer un plan.",[83],{"id":437,"tags":438,"question":439,"quick_answer":440,"details":441,"dependsOn":442,"difficulty":204},"tfm-046",[7,5,85],"Pourquoi **découper** un gros state en plusieurs ?","Un plan plus rapide, un rayon d'explosion limité, et des équipes qui n'attendent pas le même verrou","Un state unique de 2 000 ressources met des minutes à rafraîchir, et une erreur de config dans un module peut bloquer l'apply de tout le monde.\n\nDécoupe classique par couche et par environnement : `network/`, `cluster/`, `data/`, `apps/`, chacun avec son backend. Les liens passent par `terraform_remote_state` ou des data sources.",[408,125],{"id":444,"tags":445,"question":446,"quick_answer":447,"details":448,"dependsOn":449,"difficulty":34,"sources":450},"tfm-048",[7,5,46],"Comment créer **plusieurs exemplaires** d'une ressource ?","Avec le méta-argument for_each sur une map ou un set (ou count pour un nombre)","```\nresource \"aws_s3_bucket\" \"b\" {\n  for_each = toset([\"logs\", \"assets\"])\n  bucket   = \"app-${each.key}\"\n}\n```\n\n`each.key` et `each.value` dans le bloc ; les instances s'adressent `aws_s3_bucket.b[\"logs\"]`. Avec `count = 3`, c'est `count.index` et `aws_s3_bucket.b[0]`.",[60],[451],{"title":207,"url":208},{"id":453,"tags":454,"question":455,"quick_answer":456,"details":457,"dependsOn":458,"difficulty":123,"sources":459},"tfm-049",[7,5,46],"Quand utiliser **depends_on** sur une ressource ?","Quand une dépendance existe réellement mais n'apparaît dans aucune référence d'attribut","Exemple classique : une instance dont le script de démarrage lit un bucket S3 ; rien dans l'instance ne référence la policy IAM qui autorise cette lecture. Sans `depends_on = [aws_iam_role_policy.s3]`, Terraform peut démarrer l'instance avant la policy.\n\nÀ n'utiliser que là : un `depends_on` partout masque les vraies dépendances et sérialise les créations.",[210],[460],{"title":218,"url":219},{"id":462,"tags":463,"question":464,"quick_answer":465,"details":466,"dependsOn":467,"difficulty":399,"sources":468},"tfm-050",[7,5,118,85],"Comment passer un secret à une ressource **sans qu'il soit écrit dans le state** ?","Par un argument write-only (Terraform 1.11+), alimenté par une valeur éphémère","`password_wo = ephemeral.aws_secretsmanager_secret_version.db.secret_string` sur un `aws_db_instance` : la valeur est envoyée à l'API puis oubliée, absente du plan et du state. Un `password_wo_version` déclenche la rotation.\n\nLes ressources `ephemeral` (1.10) sont lues à chaque exécution et jamais persistées. Avant ça, tout secret passait par le state.",[116,401],[469],{"title":470,"url":471},"Terraform Docs — Ephemeral values and write-only arguments","https://developer.hashicorp.com/terraform/language/manage-sensitive-data/ephemeral",{"id":473,"tags":474,"question":475,"quick_answer":476,"details":477,"dependsOn":478,"difficulty":123,"sources":479},"tfm-051",[7,5,46],"Comment faire **échouer un plan** sur une valeur de variable invalide ?","Avec un bloc validation dans la variable : condition + error_message","```\nvariable \"env\" {\n  type = string\n  validation {\n    condition     = contains([\"dev\", \"staging\", \"prod\"], var.env)\n    error_message = \"env doit être dev, staging ou prod.\"\n  }\n}\n```\n\nPour des conditions sur des ressources (une AMI qui doit être x86), les blocs `precondition` / `postcondition` dans `lifecycle` (Terraform 1.2+).",[150],[480],{"title":481,"url":482},"Terraform Docs — Custom validation rules","https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules",{"id":484,"tags":485,"question":486,"quick_answer":487,"details":488,"dependsOn":489,"difficulty":34,"sources":490},"tfm-052",[7,5,350],"Que signifie la contrainte de version `~> 5.50` ?","Au moins 5.50, et toute version 5.x au-dessus, jamais 6.0","L'opérateur « pessimiste » : seul le dernier chiffre indiqué peut monter. `~> 5.50` autorise 5.50 à 5.99 ; `~> 5.50.0` n'autorise que 5.50.x. C'est la contrainte habituelle pour un provider : les corrections sans la version majeure qui casse.",[360],[491],{"title":492,"url":493},"Terraform Docs — Version constraints","https://developer.hashicorp.com/terraform/language/expressions/version-constraints",{"id":495,"tags":496,"question":497,"quick_answer":498,"details":499,"dependsOn":500,"difficulty":81},"tfm-053",[7,5,76],"Que fait **terraform fmt** ?","Réécrit les fichiers .tf dans la mise en forme canonique (indentation, alignement des =)","`fmt -check` ne modifie rien et échoue si un fichier n'est pas formaté : c'est ce qu'on met en CI. `fmt -recursive` descend dans les modules. Supprime les débats de style et les diffs de whitespace.",[74],"ok",null,1792059304208]