
Pour servir plusieurs environnements avec un seul code Terraform, on écrit un
fichier de valeurs par environnement et on le sélectionne au moment du plan :
terraform apply -var-file=envs/prod.tfvars. Le code ne change pas, seules les
valeurs changent.
Reste la question qui décide de ce que vous déployez : quand plusieurs sources
donnent une valeur à la même variable, laquelle gagne ? Tout ce qui suit a
été exécuté sur Terraform v1.15.4, et deux idées très répandues n'y survivent
pas, celle du tableau de précédence à cinq niveaux, et celle du drapeau
-var qui surcharge tout.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Écrire un fichier de valeurs par environnement, et le sélectionner
- Reconnaître les fichiers que Terraform charge sans qu'on les demande
- Classer les six sources de valeurs, du plus faible au plus fort
- Prouver quelle valeur a été retenue, sans la lire à l'écran
- Passer une map ou une liste en ligne de commande
- Repérer une clé mal orthographiée qui ne fait échouer aucun plan
Prérequis
Section intitulée « Prérequis »- Terraform 1.11 ou plus récent (installer Terraform). Les mesures viennent de la branche stable du moment, 1.15.x.
- Savoir déclarer une variable (variables Terraform) et connaître les fichiers tfvars.
Un fichier de valeurs par environnement
Section intitulée « Un fichier de valeurs par environnement »Le code déclare ce qui varie sans fixer aucune valeur. Les variables sans
default sont un garde-fou : elles obligent l'appelant à choisir un
environnement.
variable "env_name" { description = "Nom de l'environnement servi." type = string}
variable "disk_size_gb" { description = "Taille du volume, en Go." type = number}
variable "retention_jours" { description = "Duree de retention des sauvegardes, en jours." type = number default = 7}Chaque environnement reçoit alors son fichier, en syntaxe
nom = valeur, sans le mot-clé variable :
env_name = "prod"disk_size_gb = 8retention_jours = 90Et on le sélectionne à l'exécution :
terraform apply -var-file=envs/prod.tfvarsLes fichiers que Terraform charge sans qu'on les demande
Section intitulée « Les fichiers que Terraform charge sans qu'on les demande »Quatre noms sont automatiques, et c'est la source d'une bonne partie des surprises. Terraform charge sans aucune option :
| Fichier | Chargé |
|---|---|
terraform.tfvars | toujours, s'il existe |
terraform.tfvars.json | toujours, s'il existe |
*.auto.tfvars | tous, en ordre lexical |
*.auto.tfvars.json | tous, en ordre lexical |
Tout autre nom, envs/prod.tfvars compris, demande un -var-file explicite.
C'est très utile pour les valeurs partagées par tous les environnements, une image de base ou des étiquettes communes, qu'on n'a alors pas à recopier dans chaque fichier :
base_image = "debian-13-genericcloud-amd64.qcow2"
tags = { equipe = "infra" projet = "formation-terraform"}L'échelle de précédence, mesurée
Section intitulée « L'échelle de précédence, mesurée »Voici l'ordre officiel, du plus faible au plus fort. Chaque marche a été vérifiée en opposant deux sources et en lisant la valeur retenue :
| Rang | Source | Chargement |
|---|---|---|
| 1 (le plus faible) | default du bloc variable | implicite |
| 2 | variable d'environnement TF_VAR_<nom> | implicite |
| 3 | terraform.tfvars | automatique |
| 4 | terraform.tfvars.json | automatique |
| 5 | *.auto.tfvars et *.auto.tfvars.json, en ordre lexical | automatique |
| 6 (le plus fort) | -var et -var-file, dans l'ordre fourni | explicite |
Deux marches manquent à presque tous les tableaux publiés. La deuxième, les
variables d'environnement, et surtout la sixième, où -var et -var-file
partagent le même rang.
Les variables d'environnement existent, et elles perdent
Section intitulée « Les variables d'environnement existent, et elles perdent »Terraform lit toute variable d'environnement préfixée par TF_VAR_. Elle
bat le default, ce qui en fait un moyen commode d'injecter une valeur
depuis un runner :
export TF_VAR_env_name=depuis-la-ciMais elle perd contre le moindre fichier de valeurs du dépôt. Mesuré, avec la
variable exportée et un terraform.tfvars présent :
TFVARSC'est la valeur du fichier qui l'emporte. Une chaîne d'intégration qui pose ses valeurs par l'environnement peut donc être écrasée en silence par un fichier commité.
Vérifier la valeur retenue, sans la lire à l'écran
Section intitulée « Vérifier la valeur retenue, sans la lire à l'écran »C'est le contrôle qui tranche tous les doutes de précédence, et il ne demande
aucun apply. La représentation JSON du plan expose la valeur résolue
de chaque variable racine :
terraform plan -out=tfplan -var-file=envs/prod.tfvarsterraform show -json tfplan | jq '.variables'{ "disk_size_gb": { "value": 8 }, "env_name": { "value": "prod" }, "retention_jours": { "value": 90 }}Passer une map ou une liste en ligne de commande
Section intitulée « Passer une map ou une liste en ligne de commande »Un type complexe se passe très bien par -var, à condition de respecter les
règles d'échappement du shell. La documentation recommande la syntaxe
JSON :
terraform plan -var 'tags={"equipe":"astreinte","criticite":"haute"}'La syntaxe HCL native fonctionne également, mesurée sur 1.15.4 :
terraform plan -var 'tags={equipe="prod"}'Les guillemets simples autour de l'argument sont ce qui protège les guillemets doubles du shell.
Cumuler plusieurs fichiers
Section intitulée « Cumuler plusieurs fichiers »L'option -var-file s'emploie plusieurs fois, ce qui permet de superposer
des valeurs communes et des valeurs d'environnement sans dépendre d'un chargement
implicite :
terraform plan -var-file=commun.tfvars -var-file=envs/prod.tfvarsLe dernier gagne pour les clés que les deux fichiers déclarent, et rien
n'interdit de passer explicitement un fichier déjà chargé automatiquement, comme
-var-file=production.auto.tfvars.
Les pièges mesurés
Section intitulée « Les pièges mesurés »Le mécanisme est en place. Voici ce qui casse en pratique, chaque cas ayant été rejoué sur 1.15.4.
-var ne surcharge pas « toute autre source »
Section intitulée « -var ne surcharge pas « toute autre source » »C'est l'erreur la plus répandue, et elle est facile à démontrer. -var et
-var-file sont au même rang : c'est l'ordre des arguments qui tranche,
pas une priorité intrinsèque.
terraform plan -var 'disk_size_gb=16' -var-file=envs/prod.tfvarsLe plan retient 8, la valeur du fichier, parce qu'il est appliqué après. En inversant les deux options :
terraform plan -var-file=envs/prod.tfvars -var 'disk_size_gb=16'Le plan retient 16. Le même couple d'options donne donc deux résultats, et rien ne le signale à l'écran.
L'ordre des *.auto.tfvars est lexical, pas alphabétique
Section intitulée « L'ordre des *.auto.tfvars est lexical, pas alphabétique »« Ordre alphabétique » est un raccourci qui trompe dès qu'on mélange des chiffres ou des casses. L'ordre est lexical, il compare les octets. Mesuré avec deux fichiers auto chargés :
9-x.auto.tfvars valeur = "NEUF"10-x.auto.tfvars valeur = "DIX"C'est NEUF qui l'emporte, puisque "10-x" précède "9-x" en lexical, donc
9-x s'applique en dernier. Même effet entre B.auto.tfvars et
a.auto.tfvars : c'est a qui gagne, là où un classement insensible à la
casse aurait désigné B.
Une clé mal orthographiée ne fait pas échouer le plan
Section intitulée « Une clé mal orthographiée ne fait pas échouer le plan »Trois comportements différents, selon l'endroit de la faute, et un seul arrête la commande :
| Où se trouve la faute | Ce que fait Terraform |
|---|---|
TF_VAR_variable_inexistante | rien du tout, en silence |
dans un fichier .tfvars | Warning: Value for undeclared variable, la variable reste à son default |
avec -var | Error: Value for undeclared variable, code de retour 1 |
Un disk_size_go au lieu de disk_size_gb dans un fichier d'environnement
produit donc un plan valide, avec la mauvaise taille, et un simple
avertissement dans une sortie que personne ne relit.
Un -var-file qui pointe un fichier absent n'arrête rien
Section intitulée « Un -var-file qui pointe un fichier absent n'arrête rien »Celui-ci surprend, et il a été vérifié trois fois, dont une sans tube ni substitution. Terraform affiche une erreur :
Error: Failed to read variables file
Given variables file absent.tfvars does not exist.Puis il poursuit le plan avec les valeurs par défaut, et sort en 0 :
Changes to Outputs: + valeur = "DEFAUT"Une faute de frappe dans le chemin d'un -var-file ne fait donc pas
échouer un pipeline qui teste le code de retour. Le contrôle utile est de
relire la valeur retenue dans le JSON du plan.
sensitive masque l'affichage, il ne protège pas l'état
Section intitulée « sensitive masque l'affichage, il ne protège pas l'état »Un .tfvars de production est exactement le fichier où l'on est tenté de mettre
un secret. Marquer la variable sensitive = true masque son affichage,
et rien de plus. Mesuré, après un apply avec une variable sensible :
grep MOT-DE-PASSE-EN-CLAIR terraform.tfstateLe secret est bien là, en clair, dans l'état. Et il figure aussi dans le
plan enregistré, que terraform show -json restitue en clair. La
documentation le dit sans détour : « Terraform still stores the values of
sensitive variables in your state. »
Pour qu'une valeur ne rejoigne ni l'état ni les outputs, le mécanisme est
ephemeral = true. Terraform devient alors mécaniquement contraignant :
Error: Ephemeral value not allowedUn fichier de valeurs ne sépare pas les états
Section intitulée « Un fichier de valeurs ne sépare pas les états »Le point à ne pas confondre, et c'est le plus structurant. Un -var-file
change les valeurs, pas l'état. Un seul répertoire garde un état :
appliquer dev.tfvars puis prod.tfvars ne crée pas deux environnements, le
second remplace le premier. La séparation réelle passe par des racines
distinctes, sujet traité dans
séparer dev, staging et prod.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Correction |
|---|---|---|
Error: No value for required variable | aucun fichier ni -var ne fournit la valeur | ajouter -var-file=envs/<env>.tfvars, ou exporter un TF_VAR_ |
| une variable a une valeur inattendue | une source plus forte que celle attendue | lire terraform show -json tfplan | jq '.variables', puis remonter l'échelle |
-var semble ignoré | un -var-file placé après l'écrase | inverser l'ordre des deux options |
Warning: Value for undeclared variable | clé mal orthographiée dans un .tfvars | corriger la clé, jamais ajouter un default pour faire taire l'avertissement |
Error: Value for undeclared variable | même faute, mais passée par -var | corriger le nom ; ici, au moins, la commande échoue |
| le plan tourne alors qu'aucun environnement n'est choisi | un terraform.tfvars auto chargé | le vider, le supprimer, ou assumer qu'il porte les valeurs par défaut |
un -var-file sans effet et aucune erreur bloquante | chemin inexistant : le message s'affiche, le code reste 0 | vérifier le chemin, et contrôler la valeur retenue |
| un secret se retrouve dans l'état | sensitive ne fait que masquer l'affichage | sortir la valeur du code, et regarder ephemeral |
Mettre en pratique
Section intitulée « Mettre en pratique »Le lab la valeur qui gagne remet une configuration servant trois
environnements, avec un terraform.tfvars qui neutralise son propre garde-fou,
deux types laissés à compléter et une clé mal orthographiée. Les tests ne lisent
jamais vos fichiers : ils rejouent des plans et comparent la clé variables
du JSON, ce qui prouve la précédence sans rien supposer. Ils vérifient aussi que
l'ordre des options tranche, et que les *.auto.tfvars s'appliquent en ordre
lexical. Il se joue hors ligne.
À retenir
Section intitulée « À retenir »- Un fichier par environnement paramètre un code unique, sélectionné par
-var-file. - Quatre noms sont chargés sans option :
terraform.tfvars,terraform.tfvars.jsonet les*.auto.tfvars. - L'échelle complète compte six rangs, et les
TF_VAR_en occupent un, juste au-dessus dudefault. - Une variable d'environnement perd contre n'importe quel fichier de valeurs.
-varne gagne pas toujours : il partage son rang avec-var-file, et l'ordre des arguments décide.- L'ordre des
*.auto.tfvarsest lexical :9-xs'applique après10-x. - La valeur retenue se prouve par
terraform show -json, jamais à l'écran. - Une clé mal orthographiée dans un
.tfvarsne produit qu'un avertissement, et laisse la variable à sondefault. - Un
-var-fileinexistant affiche une erreur mais laisse le plan aboutir en code 0. sensitivemasque l'affichage ; la valeur reste dans l'état.
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous portent sur ce qui surprend en pratique : la source qui l'emporte contre toute attente, le fichier chargé sans qu'on l'ait demandé, et la faute de frappe qui ne casse rien.
L'échelle complète
| Rang | Source | Chargement |
|---|---|---|
| 1 (le plus faible) | default du bloc variable |
implicite |
| 2 | TF_VAR_<nom> |
implicite |
| 3 | terraform.tfvars |
automatique |
| 4 | terraform.tfvars.json |
automatique |
| 5 | *.auto.tfvars, ordre lexical |
automatique |
| 6 (le plus fort) | -var et -var-file, dans l'ordre fourni |
explicite |
Les deux rangs que les tableaux oublient
Le deuxième : les variables d'environnement existent bel et bien, et se placent juste au-dessus dudefault.Le sixième : -var et -var-file ne sont pas deux niveaux distincts, ils partagent le même rang.Chaque marche a été vérifiée sur Terraform 1.15.4 en opposant deux sources et en lisant la valeur retenue dans terraform show -json.Mesuré : le même couple d'options, deux résultats
terraform plan -var 'disk_size_gb=16' -var-file=envs/prod.tfvars
Le plan retient 8, la valeur du fichier, parce qu'il est appliqué après.terraform plan -var-file=envs/prod.tfvars -var 'disk_size_gb=16'
Le plan retient 16.Ce que dit la documentation
« Any-var and -var-file options on the command line, in the order they are provided. »Ce n'est donc pas une hiérarchie entre les deux options, c'est l'ordre des arguments. Rien ne le signale à l'écran : le seul contrôle fiable est terraform show -json tfplan | jq '.variables'.Les quatre noms automatiques
| Fichier | Chargé |
|---|---|
terraform.tfvars |
toujours, s'il existe |
terraform.tfvars.json |
toujours, s'il existe |
*.auto.tfvars |
tous, en ordre lexical |
*.auto.tfvars.json |
tous, en ordre lexical |
L'usage qui en découle
Le suffixe.auto.tfvars est fait pour les valeurs partagées par tous les environnements, qu'on n'a alors pas à recopier dans chaque fichier.Le piège
Unterraform.tfvars traînant à la racine peut donner une valeur à une variable délibérément déclarée sans default. Le garde-fou saute sans bruit : terraform plan réussit alors qu'aucun environnement n'a été choisi.Mesuré sur 1.15.4
AvecTF_VAR_valeur=ENVIRONNEMENT exporté et un terraform.tfvars portant valeur = "TFVARS" :terraform plan -out=tfplan
terraform show -json tfplan | jq -r '.variables.valeur.value'
TFVARS
C'est le fichier qui l'emporte.La conséquence en intégration continue
Une chaîne qui pose ses valeurs par l'environnement peut être écrasée en silence par un fichier commité dans le dépôt. Si la valeur du runner doit gagner, il faut la passer explicitement :terraform apply -var "env_name=$CI_ENVIRONMENT"
En revanche, TF_VAR_ bat bien le default : c'est un moyen commode d'injecter une valeur quand aucun fichier ne la fournit.Trois comportements, un seul arrête la commande
| Où | Ce que fait Terraform |
|---|---|
TF_VAR_inexistante |
rien du tout, en silence |
dans un .tfvars |
Warning: Value for undeclared variable |
avec -var |
Error: Value for undeclared variable, code 1 |
Pourquoi c'est coûteux
Undisk_size_go au lieu de disk_size_gb dans un fichier d'environnement ne casse rien : le plan est valide, la variable reste à son default, et la taille déployée est fausse. L'avertissement se perd dans une sortie que personne ne relit.La règle : corriger la clé, jamais ajouter un default pour faire taire le message. Un default transformerait un avertissement visible en valeur silencieusement fausse.Le contrôle qui tranche
terraform plan -out=tfplan -var-file=envs/prod.tfvars
terraform show -json tfplan | jq '.variables'
{
"disk_size_gb": { "value": 8 },
"env_name": { "value": "prod" },
"retention_jours": { "value": 90 }
}
Une subtilité mesurée
Une valeur passée par-var apparaît ici en chaîne ("16"), même pour une variable typée number, alors que la même valeur venue d'un .tfvars s'affiche en nombre. Terraform la convertit bien selon le type déclaré pour l'utiliser : c'est la représentation du plan qui expose l'entrée brute.Ne jugez donc pas une précédence sur le texte affiché par plan, qui ne montre que les changements de ressources.Mesuré, et vérifié trois fois
terraform plan -var-file=absent.tfvars
Error: Failed to read variables file
Given variables file absent.tfvars does not exist.
Changes to Outputs:
+ valeur = "DEFAUT"
Le plan aboutit, avec les valeurs par défaut, et le code de retour vaut 0. Avec -detailed-exitcode, il rend 2, c'est-à-dire « des changements », comme n'importe quel plan.La conséquence
Un pipeline qui se contente de tester le code de retour ne verra rien, et pourra appliquer une configuration entièrement par défaut en croyant déployer la production.Le garde-fou utile est ailleurs : des variables sansdefault, qui font échouer le plan sur No value for required variable dès qu'aucune source ne les fournit.Mesuré : le secret est dans l'état
Après unapply avec une variable marquée sensitive = true :grep MOT-DE-PASSE-EN-CLAIR terraform.tfstate
La valeur est bien là, en clair. Elle figure aussi dans le plan enregistré, que terraform show -json restitue sans masquage.« Terraform still stores the values of sensitive variables in your state. »Ce que sensitive fait vraiment
Il masque la valeur dans la sortie de plan et d'apply. C'est une protection contre la lecture par-dessus l'épaule et contre les logs de CI, pas contre la lecture de l'état.Le mécanisme qui protège
ephemeral = true empêche mécaniquement la valeur d'atteindre l'état ou un output. Mesuré, Terraform refuse à la compilation :Error: Ephemeral value not allowed
Un fichier .tfvars de production reste, lui, un fichier versionné : un secret n'y a pas sa place.Pour aller plus loin
Section intitulée « Pour aller plus loin »- Monorepo vs un repo par stack : un seul dépôt ou un dépôt par périmètre, une fois les valeurs rangées.
- Quiz Organiser les environnements Terraform : un contrôle des acquis sur la section, précédence comprise.
- Les variables d'entrée : la référence officielle : l'ordre de précédence, les
TF_VAR_et les variables non déclarées. - La commande plan : la référence officielle :
-var,-var-filerépétable et les options de planification.