Aller au contenu
Infrastructure as Code medium

Gérer les variables par environnement avec Terraform

35 min de lecture

logo terraform

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.

  • É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

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.

variables.tf
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 :

envs/prod.tfvars
env_name = "prod"
disk_size_gb = 8
retention_jours = 90

Et on le sélectionne à l'exécution :

Fenêtre de terminal
terraform apply -var-file=envs/prod.tfvars

Les 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 :

FichierChargé
terraform.tfvarstoujours, s'il existe
terraform.tfvars.jsontoujours, s'il existe
*.auto.tfvarstous, en ordre lexical
*.auto.tfvars.jsontous, 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 :

commun.auto.tfvars
base_image = "debian-13-genericcloud-amd64.qcow2"
tags = {
equipe = "infra"
projet = "formation-terraform"
}

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 :

RangSourceChargement
1 (le plus faible)default du bloc variableimplicite
2variable d'environnement TF_VAR_<nom>implicite
3terraform.tfvarsautomatique
4terraform.tfvars.jsonautomatique
5*.auto.tfvars et *.auto.tfvars.json, en ordre lexicalautomatique
6 (le plus fort)-var et -var-file, dans l'ordre fourniexplicite

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 :

Fenêtre de terminal
export TF_VAR_env_name=depuis-la-ci

Mais elle perd contre le moindre fichier de valeurs du dépôt. Mesuré, avec la variable exportée et un terraform.tfvars présent :

TFVARS

C'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 :

Fenêtre de terminal
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 }
}

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 :

Fenêtre de terminal
terraform plan -var 'tags={"equipe":"astreinte","criticite":"haute"}'

La syntaxe HCL native fonctionne également, mesurée sur 1.15.4 :

Fenêtre de terminal
terraform plan -var 'tags={equipe="prod"}'

Les guillemets simples autour de l'argument sont ce qui protège les guillemets doubles du shell.

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 :

Fenêtre de terminal
terraform plan -var-file=commun.tfvars -var-file=envs/prod.tfvars

Le 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.

Le mécanisme est en place. Voici ce qui casse en pratique, chaque cas ayant été rejoué sur 1.15.4.

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.

Fenêtre de terminal
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. En inversant les deux options :

Fenêtre de terminal
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 fauteCe que fait Terraform
TF_VAR_variable_inexistanterien du tout, en silence
dans un fichier .tfvarsWarning: Value for undeclared variable, la variable reste à son default
avec -varError: 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 :

Fenêtre de terminal
grep MOT-DE-PASSE-EN-CLAIR terraform.tfstate

Le secret est bien , 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 allowed

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.

SymptômeCause probableCorrection
Error: No value for required variableaucun fichier ni -var ne fournit la valeurajouter -var-file=envs/<env>.tfvars, ou exporter un TF_VAR_
une variable a une valeur inattendueune source plus forte que celle attenduelire terraform show -json tfplan | jq '.variables', puis remonter l'échelle
-var semble ignoréun -var-file placé après l'écraseinverser l'ordre des deux options
Warning: Value for undeclared variableclé mal orthographiée dans un .tfvarscorriger la clé, jamais ajouter un default pour faire taire l'avertissement
Error: Value for undeclared variablemême faute, mais passée par -varcorriger le nom ; ici, au moins, la commande échoue
le plan tourne alors qu'aucun environnement n'est choisiun 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 bloquantechemin inexistant : le message s'affiche, le code reste 0vérifier le chemin, et contrôler la valeur retenue
un secret se retrouve dans l'étatsensitive ne fait que masquer l'affichagesortir la valeur du code, et regarder ephemeral

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.

  • 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.json et les *.auto.tfvars.
  • L'échelle complète compte six rangs, et les TF_VAR_ en occupent un, juste au-dessus du default.
  • Une variable d'environnement perd contre n'importe quel fichier de valeurs.
  • -var ne gagne pas toujours : il partage son rang avec -var-file, et l'ordre des arguments décide.
  • L'ordre des *.auto.tfvars est lexical : 9-x s'applique après 10-x.
  • La valeur retenue se prouve par terraform show -json, jamais à l'écran.
  • Une clé mal orthographiée dans un .tfvars ne produit qu'un avertissement, et laisse la variable à son default.
  • Un -var-file inexistant affiche une erreur mais laisse le plan aboutir en code 0.
  • sensitive masque l'affichage ; la valeur reste dans l'état.

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.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn