
Une variable Terraform paramètre une configuration : au lieu de coder une
valeur en dur, on la déclare une fois et on l'alimente depuis l'extérieur.
Déclarer une variable est simple. Ce qui fait échouer, ce sont quatre
comportements rarement enseignés : le type par défaut qui laisse tout
passer, le null explicite qui écrase un default, le sensitive pris pour
une protection alors qu'il ne masque que l'affichage, et l'ordre de
précédence des sources.
Ce guide part de la base, la déclaration et le typage, puis traite les types
complexes, la validation, le piège nullable, la vraie portée de sensitive, et
la précédence des sources. Tous les comportements ont été vérifiés sur
Terraform v1.15.4.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Déclarer une variable, et pourquoi l'absence de
typevautany - Les types complexes :
object({...}),optional(),map,set,tuple - La
validationqui rejette une valeur au plan, avant tout provider - Le piège du
nullqui écrase undefault, etnullable = false - Pourquoi
sensitivene protège pas le secret, et ce qui le protège - L'ordre de précédence exact des sources de valeurs
Prérequis
Section intitulée « Prérequis »- Un projet Terraform initialisé (installer Terraform)
- Terraform 1.15.x, la série stable courante
Déclarer une variable, et le type any
Section intitulée « Déclarer une variable, et le type any »Un bloc variable porte un nom, et des arguments qui vont bien au-delà du trio
description / type / default. Il accepte aussi validation, sensitive,
nullable (défaut true), ephemeral (1.10+) et, depuis 1.15, deprecated.
variable "region" { type = string default = "eu-west-3"}Sans contrainte de type, une variable vaut any : elle accepte n'importe
quoi, et l'erreur n'apparaît qu'à l'usage, souvent loin de la déclaration.
Typez toujours vos variables. Quelques noms sont réservés et interdits :
source, version, providers, count, for_each, lifecycle,
depends_on, locals.
Les types complexes : object et optional()
Section intitulée « Les types complexes : object et optional() »Le sujet ne s'arrête pas à list(string) et map(string). Le type le plus
utile en pratique est object({...}), souvent dans une map, avec des
attributs optional() porteurs d'un défaut :
variable "nodes" { type = map(object({ size = string replicas = optional(number, 1) public = optional(bool, false) }))}Fournissez une entrée incomplète, et Terraform la complète avec les défauts
des optional() :
nodes = { web = { size = "small" } # replicas = 1, public = false ajoutes par Terraform}Le second argument d'optional() est le défaut ; sans lui, l'attribut absent
vaut null. Les types set(...), tuple([...]) et les objets imbriqués
suivent la même logique.
La validation rejette avant tout provider
Section intitulée « La validation rejette avant tout provider »Un bloc validation vérifie une condition et échoue au plan, avant le
moindre appel de provider. C'est la barrière la moins chère contre une valeur
absurde :
variable "env" { type = string
validation { condition = contains(["dev", "staging", "prod"], var.env) error_message = "env doit valoir dev, staging ou prod." }}terraform plan -var env=chaosError: Invalid value for variableAucune ressource n'est sollicitée : l'erreur tombe à la validation, ce qui en fait un garde-fou gratuit sur les entrées.
Le piège du null : nullable
Section intitulée « Le piège du null : nullable »Voici le piège le plus rentable du sujet, et il attrape même des profils
avancés. Une variable a un default, on se croit donc protégé. Mais passer
explicitement null (souvent depuis un fichier de valeurs généré)
écrase le default et propage null :
variable "retention_days" { type = number default = 7}Avec retention_days = null dans un terraform.tfvars, la variable vaut
null, pas 7. Pour forcer le retour au défaut, on pose
nullable = false :
variable "retention_days" { type = number default = 7 nullable = false}Désormais, un null explicite retombe sur 7. C'est le comportement attendu
d'une variable qui ne doit jamais valoir null.
sensitive masque l'affichage, pas le state
Section intitulée « sensitive masque l'affichage, pas le state »Voici l'erreur la plus dangereuse, parce qu'elle donne un faux sentiment de
sécurité. sensitive = true ne protège pas le secret. Il masque la valeur
dans la sortie humaine de plan, apply et terraform output, rien de plus.
La documentation est explicite :
Terraform still records sensitive values in the state, so anyone who can access your state data can access your sensitive values.
Concrètement, terraform show -json et terraform output -json rendent la
valeur en clair, et le fichier d'état la stocke telle quelle. Un
sensitive = true sur une variable de mot de passe n'empêche personne ayant le
state de le lire.
L'ordre de précédence des sources
Section intitulée « L'ordre de précédence des sources »Quand plusieurs sources donnent une valeur à la même variable, Terraform les applique dans un ordre fixe, du plus faible au plus fort :
| Rang | Source |
|---|---|
| 1 | le default du bloc variable |
| 2 | la variable d'environnement TF_VAR_<nom> |
| 3 | le fichier terraform.tfvars |
| 4 | le fichier terraform.tfvars.json |
| 5 | les *.auto.tfvars (et .json), en ordre lexical |
| 6 | les options -var et -var-file, dans l'ordre fourni |
Deux surprises fréquentes, et ce sont elles que l'examen interroge. D'abord, un
*.auto.tfvars l'emporte sur un TF_VAR_ exporté : beaucoup croient
l'inverse. Ensuite, entre deux *.auto.tfvars, c'est l'ordre alphabétique
des noms de fichiers qui tranche, pas l'ordre d'écriture :
zz-override.auto.tfvars gagne sur a-defaults.auto.tfvars. La ligne de
commande, elle, gagne toujours.
Nouveautés utiles depuis 1.10
Section intitulée « Nouveautés utiles depuis 1.10 »Deux arguments récents complètent le tableau, et tombent sous le sous-objectif données sensibles :
ephemeral = true(1.10+) : la valeur existe pendant l'opération mais n'est ni écrite dans le state, ni dans le plan. C'est la vraie réponse au besoin quesensitivene couvre pas.deprecated = "message"(1.15+) sur unevariableou unoutput: Terraform émet un avertissement auvalidatequand quelqu'un l'utilise encore. Pratique pour retirer une variable d'un module sans casser ses appelants du jour au lendemain.
Dépannage
Section intitulée « Dépannage »Ces symptômes viennent presque tous d'un des quatre pièges ci-dessus. Le tableau associe chacun à sa cause et à sa correction.
| Symptôme | Cause | Solution |
|---|---|---|
| Une variable accepte une valeur d'un type inattendu | Pas de type : elle vaut any | Ajouter une contrainte de type |
Une variable à default vaut quand même null | Un null explicite a écrasé le défaut | Poser nullable = false |
| Un secret apparaît en clair dans le state | sensitive ne masque que l'affichage | Chiffrer le state, restreindre son accès, ou ephemeral = true |
Invalid value for variable au plan | Un bloc validation a rejeté la valeur | Corriger la valeur, elle est hors des bornes autorisées |
Une valeur d'*.auto.tfvars gagne sur un TF_VAR_ | Ordre de précédence normal | Utiliser -var pour forcer, il est au-dessus de tout |
À retenir
Section intitulée « À retenir »- Sans
type, une variable vautany: typez toujours. object({...})avecoptional(x, defaut)complète les entrées partielles.- Un bloc
validationrejette au plan, avant tout provider. - Un
nullexplicite écrase undefault;nullable = falsele fait retomber sur le défaut. sensitivemasque l'affichage, pas le state : la valeur y reste en clair.ephemeral = truel'exclut réellement.- Précédence :
default<TF_VAR_<terraform.tfvars<*.auto.tfvars(ordre lexical) <-var. - Certains noms sont réservés et ne peuvent pas nommer une variable.
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »Les questions ci-dessous reprennent les confusions les plus fréquentes sur les
variables : le piège du null, la vraie portée de sensitive, et l'ordre de
précédence des sources.
Un masque d'affichage, pas du chiffrement
La documentation est explicite : « Terraform still records sensitive values in the state, so anyone who can access your state data can access your sensitive values. »sensitive = true masque la valeur dans la sortie humaine de plan, apply et terraform output. C'est tout.Vérifié sur 1.15.4
terraform show -json et terraform output -json rendent la valeur en clair, et le fichier d'état la stocke telle quelle.La vraie protection
Chiffrer le state et restreindre son accès. Pour exclure réellement une valeur du state et du plan,ephemeral = true (Terraform 1.10+).Le null explicite écrase le default
variable "retention_days" {
type = number
default = 7
}
Avec retention_days = null dans un terraform.tfvars, la variable vaut null, pas 7.La parade : nullable = false
variable "retention_days" {
type = number
default = 7
nullable = false
}
Un null explicite retombe alors sur 7. nullable vaut true par défaut, d'où la surprise.Du plus faible au plus fort
- le
defaultdu blocvariable - la variable d'environnement
TF_VAR_<nom> terraform.tfvarsterraform.tfvars.json- les
*.auto.tfvars(et.json), en ordre lexical - les options
-varet-var-file, dans l'ordre fourni
Les deux surprises
Un*.auto.tfvars l'emporte sur un TF_VAR_ exporté (beaucoup croient l'inverse). Et entre deux *.auto.tfvars, c'est l'ordre alphabétique des noms qui tranche, pas l'ordre d'écriture. La ligne de commande gagne toujours.object et optional()
variable "nodes" {
type = map(object({
size = string
replicas = optional(number, 1)
public = optional(bool, false)
}))
}
Une entrée incomplète est complétée par Terraform :nodes = {
web = { size = "small" } # replicas = 1, public = false ajoutes
}
Le défaut d'optional()
Le second argument est le défaut. Sans lui, l'attribut absent vautnull. set(...), tuple([...]) et les objets imbriqués suivent la même logique.Le type par défaut est any
Une variable déclarée sanstype accepte n'importe quoi : une chaîne, un nombre, une liste, un objet. Terraform ne rejette rien à la déclaration.Pourquoi c'est un piège
Le bug n'apparaît qu'à l'usage de la valeur, souvent dans une ressource éloignée, avec un message obscur. Une contrainte detype fait échouer au plan, au bon endroit.variable "region" {
type = string
}
Le bloc validation
variable "env" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.env)
error_message = "env doit valoir dev, staging ou prod."
}
}
Rejet au plan
terraform plan -var env=chaos
# Error: Invalid value for variable
L'erreur tombe avant tout provider : aucune ressource n'est sollicitée. C'est la barrière la moins chère contre une valeur absurde.Deux portées différentes
sensitive = true: masque l'affichage deplan,apply,output. La valeur reste en clair dans le state et le plan.ephemeral = true(1.10+) : la valeur existe pendant l'opération mais n'est ni dans le state, ni dans le plan.
Comment choisir
Contre une fuite dans les logs :sensitive suffit. Pour un secret qui ne doit jamais toucher le disque : ephemeral. sensitive ne remplace jamais un state chiffré et à accès restreint.Pour aller plus loin
Section intitulée « Pour aller plus loin »- Les outputs Terraform : Expose une valeur calculée, symétrique des variables d'entrée.
- Les valeurs locales : Nomme une valeur intermédiaire au lieu de la répéter.
- Conditions personnalisées : Étend la validation des variables aux
preconditionet au bloccheck.