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 que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ce qu'un local peut référencer, et ce qui le distingue d'une variable
- Pourquoi plusieurs blocs
localsfusionnent - 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
Prérequis
Section intitulée « Prérequis »- Variables et types (variables Terraform)
- Terraform 1.15 ou plus récent (comportements vérifiés sur la v1.15.4)
Qu'est-ce qu'un local ?
Section intitulée « Qu'est-ce qu'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.
Les blocs locals fusionnent
Section intitulée « Les blocs locals fusionnent »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}"}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")string20 : "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.
terraform plan -out=tfplanterraform show -json tfplan | jq '.output_changes.empreinte.after_unknown'trueLa sensibilité se propage à travers un local
Section intitulée « La sensibilité se propage à travers un local »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 valuesLa 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, si bien qu'un local d'apparence anodine peut rendre sensible une
sortie qui ne l'était pas.
Local, variable ou output ?
Section intitulée « Local, variable ou output ? »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érieur | Visible hors du module | |
|---|---|---|---|
variable | oui (type, validation) | oui (CLI, tfvars) | non |
local | non (nom = expression) | non | non |
output | non | non | oui |
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.
Quand utiliser un local
Section intitulée « Quand utiliser un local »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.
Dépannage
Section intitulée « Dépannage »Ces symptômes viennent tous des particularités ci-dessus. Le tableau relie chacun à sa cause réelle.
| Symptôme | Cause | Solution |
|---|---|---|
| Un calcul échoue sur un local censé être un nombre | Le ternaire a glissé vers une chaîne | Retirer les guillemets, ou convertir avec tonumber |
Error: Output refers to sensitive values | Un local dérive d'une variable sensible | Marquer la sortie sensitive = true |
Un output est (known after apply) de façon inattendue | Le local dérive d'un attribut de ressource | Normal : la valeur n'existe qu'à l'apply |
| Un local n'est pas visible dans un autre module | Les locals sont internes au module | Passer la valeur en argument du module enfant |
Mettre en pratique
Section intitulée « Mettre en pratique »Vous centralisez des expressions dans des blocs locals : normalisation par des fonctions HCL, ternaire typé, liste produite par une expression for. Le cas instructif est le dernier, et il donne son titre au lab : un local dérivé d'un attribut de ressource reste inconnu au plan, ce qui change ce que Terraform peut vous montrer avant l'apply.
À retenir
Section intitulée « À retenir »- Un local est un nom pour une expression : ni
type, nidescription, nisensitive, contrairement à une variable. - Un local peut référencer une variable, un attribut de ressource, une fonction ou un autre local.
- Plusieurs blocs
localsfusionnent ; ils se référencent librement. - Le ternaire convertit ses types en silence :
20 : "5"rend une chaîne, pas une erreur. - Un local dérivé d'une ressource est inconnu au plan (
known after apply). - La sensibilité se propage : une sortie dérivant d'une variable sensible
doit être marquée
sensitive. - Un local n'est pas lisible depuis un autre module : passer par un argument.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : depends_on : dépendances explicites
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
FAQ : questions fréquentes
Section intitulée « FAQ : questions fréquentes »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 qui les sépare
| Typable | Surchargeable | Visible hors du module | |
|---|---|---|---|
variable |
oui | oui (CLI, tfvars) | non |
local |
non | non | non |
Un local, c'est juste une valeur
Sa syntaxe se réduit ànom = expression. Il n'accepte ni type, ni description, ni sensitive, ni validation. C'est une valeur interne, calculée, que rien ne surcharge depuis l'extérieur.Quatre constructions
La documentation les liste : une variable, un attribut de ressource, la sortie d'une fonction, et un autre local.locals {
ref = replace(lower(var.enseigne), "_", "-")
hash = substr(random_id.build.hex, 0, 8)
}
La conséquence à connaître
Référencer un attribut de ressource rend le local inconnu au plan : sa valeur n'existe qu'à l'apply.Terraform fusionne les blocs
locals {
ref = replace(lower(var.enseigne), "_", "-")
}
locals {
etiquette = "${local.ref}-${var.palier}"
}
local.etiquette, dans le second bloc, consomme local.ref du premier. La seule limite est l'absence de dépendance circulaire.La règle de style
Un local partagé entre fichiers va danslocals.tf ; un local propre à un fichier se déclare en haut de ce fichier.Pas une erreur, une conversion silencieuse
> type(true ? 20 : 5)
number
> type(true ? 20 : "5")
string
20 : "5" ne lève aucune erreur, il rend une chaîne. Le bug se révèle bien plus loin, au calcul suivant.La parade
Ne pas mettre de guillemets autour d'un nombre. Si les branches diffèrent vraiment, convertir :true ? tostring(20) : "cinq". La documentation recommande d'être explicite en cas de doute.Le cas général et l'exception
Un local se calcule au plan, sauf s'il dérive d'un attribut de ressource pas encore créé.locals {
empreinte = upper(random_id.tirage.hex)
}
La preuve
terraform show -json tfplan | jq '.output_changes.empreinte.after_unknown'
true
random_id.tirage.hex n'existe qu'à l'apply : le local hérite de cette inconnue et crée une dépendance implicite dans le graphe.La sensibilité se propage
variable "mot_de_passe" {
sensitive = true
}
locals {
dsn = "postgres://app:${var.mot_de_passe}@localhost/base"
}
Un output exposant local.dsn sans sensitive = true échoue :Error: Output refers to sensitive values
La correction
Annoter la sortiesensitive = true. La sensibilité n'est pas un choix : elle se propage toute seule à travers les locals.Les locals sont internes
La documentation est explicite : « You can access local values in the module where you define them, but not in other modules. »La solution officielle
Passer la valeur en argument du module enfant :module "reseau" {
source = "./modules/reseau"
base_name = local.base_name
}
Ce n'est pas la non-surcharge qui est en jeu, mais la portée : un local n'existe que dans son module.