Aller au contenu
Infrastructure as Code medium

Variables Terraform : types, validation, nullable et précédence

20 min de lecture

logo terraform

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.

  • Déclarer une variable, et pourquoi l'absence de type vaut any
  • Les types complexes : object({...}), optional(), map, set, tuple
  • La validation qui rejette une valeur au plan, avant tout provider
  • Le piège du null qui écrase un default, et nullable = false
  • Pourquoi sensitive ne protège pas le secret, et ce qui le protège
  • L'ordre de précédence exact des sources de valeurs
  • Un projet Terraform initialisé (installer Terraform)
  • Terraform 1.15.x, la série stable courante

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.

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.

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."
}
}
Fenêtre de terminal
terraform plan -var env=chaos
Error: Invalid value for variable

Aucune ressource n'est sollicitée : l'erreur tombe à la validation, ce qui en fait un garde-fou gratuit sur les entrées.

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.

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.

Quand plusieurs sources donnent une valeur à la même variable, Terraform les applique dans un ordre fixe, du plus faible au plus fort :

RangSource
1le default du bloc variable
2la variable d'environnement TF_VAR_<nom>
3le fichier terraform.tfvars
4le fichier terraform.tfvars.json
5les *.auto.tfvars (et .json), en ordre lexical
6les 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.

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 que sensitive ne couvre pas.
  • deprecated = "message" (1.15+) sur une variable ou un output : Terraform émet un avertissement au validate quand quelqu'un l'utilise encore. Pratique pour retirer une variable d'un module sans casser ses appelants du jour au lendemain.

Ces symptômes viennent presque tous d'un des quatre pièges ci-dessus. Le tableau associe chacun à sa cause et à sa correction.

SymptômeCauseSolution
Une variable accepte une valeur d'un type inattenduPas de type : elle vaut anyAjouter une contrainte de type
Une variable à default vaut quand même nullUn null explicite a écrasé le défautPoser nullable = false
Un secret apparaît en clair dans le statesensitive ne masque que l'affichageChiffrer le state, restreindre son accès, ou ephemeral = true
Invalid value for variable au planUn bloc validation a rejeté la valeurCorriger la valeur, elle est hors des bornes autorisées
Une valeur d'*.auto.tfvars gagne sur un TF_VAR_Ordre de précédence normalUtiliser -var pour forcer, il est au-dessus de tout
  1. Sans type, une variable vaut any : typez toujours.
  2. object({...}) avec optional(x, defaut) complète les entrées partielles.
  3. Un bloc validation rejette au plan, avant tout provider.
  4. Un null explicite écrase un default ; nullable = false le fait retomber sur le défaut.
  5. sensitive masque l'affichage, pas le state : la valeur y reste en clair. ephemeral = true l'exclut réellement.
  6. Précédence : default < TF_VAR_ < terraform.tfvars < *.auto.tfvars (ordre lexical) < -var.
  7. Certains noms sont réservés et ne peuvent pas nommer une variable.

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.

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