Aller au contenu
Infrastructure as Code medium

Locals Terraform : valeurs locales et leurs pièges

20 min de lecture

logo terraform

Un bloc locals nomme des valeurs calculées, pour ne pas répéter la même expression à dix endroits. C'est simple en apparence, mais trois comportements surprennent et causent des bugs difficiles à diagnostiquer : plusieurs blocs locals fusionnent, un ternaire convertit ses types sans prévenir, et un local dérivé d'une ressource n'est pas connu au plan.

Ce guide part de la base, ce qu'est un local et ce qu'il peut référencer, puis traite ces trois pièges, plus la propagation de la sensibilité. Tous les comportements ont été vérifiés sur Terraform v1.15.4, la plupart dans terraform console, avec local et random.

  • Ce qu'un local peut référencer, et ce qui le distingue d'une variable
  • Pourquoi plusieurs blocs locals fusionnent
  • Le piège de conversion de type du ternaire
  • Pourquoi un local peut être inconnu au plan
  • Comment la sensibilité se propage à travers un local

Un local nomme une valeur calculée. Sa syntaxe se réduit à nom = expression. Il n'accepte ni type, ni description, ni sensitive, ni validation : c'est exactement ce qui le distingue d'une variable, qui, elle, peut être typée et documentée. Un local est une valeur interne, que rien ne surcharge depuis l'extérieur.

locals {
ref = replace(lower(var.enseigne), "_", "-")
}

Un local peut référencer quatre choses, et la documentation les liste précisément : une variable, un attribut de ressource, la sortie d'une fonction, et un autre local. Beaucoup de guides n'illustrent que les variables et les autres locals, mais référencer un attribut de ressource est justement la source du deuxième piège.

On peut écrire autant de blocs locals {} qu'on veut, dans un ou plusieurs fichiers. Terraform les fusionne : un local d'un bloc peut en référencer un autre déclaré ailleurs, tant qu'aucun cycle n'apparaît.

locals {
ref = replace(lower(var.enseigne), "_", "-")
}
locals {
etiquette = "${local.ref}-${var.palier}"
}
Fenêtre de terminal
terraform console
> local.etiquette
"ma-boutique-gold"

local.etiquette, dans le second bloc, consomme local.ref du premier. La règle de style officielle : un local partagé entre plusieurs fichiers va dans locals.tf ; un local propre à un fichier se déclare en haut de ce fichier.

Le piège du ternaire : les types se convertissent en silence

Section intitulée « Le piège du ternaire : les types se convertissent en silence »

C'est l'erreur la plus fréquente, et beaucoup de guides l'énoncent à l'envers. On croit souvent qu'un ternaire dont les deux branches n'ont pas le même type échoue. C'est faux : Terraform convertit vers un type commun sans broncher, et n'échoue que si aucune conversion n'est possible.

> type(true ? 20 : 5)
number
> type(true ? 20 : "5")
string

20 : "5" ne lève aucune erreur, il rend une chaîne. Un local censé porter un nombre se retrouve typé chaîne, et le bug se révèle bien plus loin, là où ce nombre est utilisé dans un calcul. La documentation officielle recommande d'ailleurs d'être explicite en cas de doute :

> true ? tostring(20) : "cinq"
"20"

La règle : ne mettez pas de guillemets autour d'un nombre, et si les branches diffèrent vraiment, convertissez-les avec une fonction (tostring, tonumber).

Un local dérivé d'une ressource est inconnu au plan

Section intitulée « Un local dérivé d'une ressource est inconnu au plan »

Un local se calcule pendant le plan, sauf s'il référence un attribut de ressource qui n'existe pas encore. Sa valeur est alors (known after apply), exactement comme l'attribut dont il dépend. Beaucoup de guides affirment à tort qu'un local est toujours résolu au plan.

resource "random_id" "tirage" {
byte_length = 4
}
locals {
empreinte = upper(random_id.tirage.hex)
}

Sur un plan à froid, un output exposant local.empreinte apparaît en inconnu : random_id.tirage.hex n'est produit qu'à l'apply. Le plan JSON le montre dans output_changes[].after_unknown. C'est aussi ce qui crée une dépendance implicite dans le graphe : le local dépend de la ressource.

Fenêtre de terminal
terraform plan -out=tfplan
terraform show -json tfplan | jq '.output_changes.empreinte.after_unknown'
true

Dernier piège, et il bloque l'apply. Terraform traite comme sensible toute expression qui utilise une valeur sensible. Un local qui assemble une chaîne à partir d'une variable sensitive = true devient donc lui-même sensible :

variable "mot_de_passe" {
type = string
sensitive = true
}
locals {
dsn = "postgres://app:${var.mot_de_passe}@localhost/base"
}

Un output qui expose local.dsn sans sensitive = true fait échouer Terraform, dès validate :

Error: Output refers to sensitive values

La correction est d'annoter la sortie sensitive = true. La sensibilité n'est pas une propriété qu'on choisit : elle se propage toute seule à travers les locals, et c'est le piège le plus courant du sous-objectif 2f.

Les trois servent à des choses différentes. Le tableau les tranche par ce qu'ils acceptent et par leur portée.

Peut être typéSurchargeable de l'extérieurVisible hors du module
variableoui (type, validation)oui (CLI, tfvars)non
localnon (nom = expression)nonnon
outputnonnonoui

Un point souvent mal compris : un local n'est pas lisible depuis un autre module. Pour transmettre une valeur calculée à un module enfant, on la passe en argument, pas en la lisant directement.

Dès qu'une expression est répétée ou assez complexe pour gêner la lecture d'une ressource : une expression for, un ternaire imbriqué, un assemblage de chaîne. La documentation met en garde contre l'abus : « Use local values sparingly, as overuse can make your code harder to understand. » Un local utilisé une seule fois et trivial n'apporte rien.

Ces symptômes viennent tous des particularités ci-dessus. Le tableau relie chacun à sa cause réelle.

SymptômeCauseSolution
Un calcul échoue sur un local censé être un nombreLe ternaire a glissé vers une chaîneRetirer les guillemets, ou convertir avec tonumber
Error: Output refers to sensitive valuesUn local dérive d'une variable sensibleMarquer la sortie sensitive = true
Un output est (known after apply) de façon inattendueLe local dérive d'un attribut de ressourceNormal : la valeur n'existe qu'à l'apply
Un local n'est pas visible dans un autre moduleLes locals sont internes au modulePasser la valeur en argument du module enfant
  1. Un local est un nom pour une expression : ni type, ni description, ni sensitive, contrairement à une variable.
  2. Un local peut référencer une variable, un attribut de ressource, une fonction ou un autre local.
  3. Plusieurs blocs locals fusionnent ; ils se référencent librement.
  4. Le ternaire convertit ses types en silence : 20 : "5" rend une chaîne, pas une erreur.
  5. Un local dérivé d'une ressource est inconnu au plan (known after apply).
  6. La sensibilité se propage : une sortie dérivant d'une variable sensible doit être marquée sensitive.
  7. Un local n'est pas lisible depuis un autre module : passer par un argument.

Les questions ci-dessous portent sur les comportements qui surprennent le plus : la fusion des blocs, la conversion de type du ternaire, et la propagation de la sensibilité.

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