Conditions Terraform : count et expressions
À la fin de ce tutoriel, vous saurez écrire des expressions ternaires
condition ? a : b, activer ou désactiver une ressource aveccount = var.enabled ? 1 : 0, préférer unfor_eachsur map filtrée pour la stabilité d’adresse, utilisercoalesce,try,canetone, éviter les pièges null vs omit et de typage, puis valider un lab léger en Regionca-central-1avec destroy systématique.Niveau : Débutant → Intermédiaire · Temps estimé : 35–45 min · Versions testées : Terraform 1.16.2 (série ≥ 1.9), AWS provider ~> 5.0 · Dernière vérification : 2026-09-10
Slug proposé :
terraform-conditionnels· Série : Terraform · Remplace / fusionne : #1655 (P6 bonus #2 : conditionnels — quasi vide)
Prérequis
- Avoir suivi Boucles (
terraform-boucles-for-each) :count,for_each, expressionfor, adresses stables - Avoir suivi Variables (
terraform-variables) et Locals (terraform-locals) : booléens, maps, valeurs dérivées - Terraform ≥ 1.9, AWS CLI v2, profil de lab (
aws sts get-caller-identityOK)
Coût estimé : quasi 0 € (bucket S3 vide tagué ou user IAM de lab sans clés). Destroy obligatoire. Aucun secret dans le HCL / tfvars.
Auth sans secrets. Utilisez
AWS_PROFILE/ SSO. Jamais d’access_key/secret_keydans le provider. Pour ce lab, une politique limitée (S3 ou IAM users de test) suffit.
Ce que nous allons construire
Lab conditionnels Terraform (ca-central-1)
├── versions.tf / providers.tf → TF ≥ 1.9, aws ~> 5.0, region ca-central-1
├── variables.tf → bool enable_lab_bucket (+ option map filtrée)
├── locals.tf → ternaires, coalesce / try / can / one
├── main.tf → ressource optionnelle via count (ou for_each filtré)
├── outputs.tf → id conditionnel (try / one)
└── init → plan (0 ou 1) → apply → destroy
(Alt : « Conditions Terraform : count conditionnel, ternaires et for_each filtré — lab AWS ca-central-1 ».)
Ce guide remplace le post WordPress #1655 (quasi vide) et aligne P2 sur expressions conditionnelles + présence/absence, actualisé 2026 (TF ≥ 1.9, aws ~> 5.0, ca-central-1).
Étape 1 — Expressions ternaires condition ? a : b
Le ternaire HCL ressemble à celui d’autres langages, avec une contrainte forte : les deux branches doivent produire le même type. C’est l’outil idéal pour choisir une chaîne de tag, un statut de versioning ou un suffixe de nom — pas pour décider du nombre d’instances d’une resource (réservé à count / for_each).
Cas réel : environment == "prod" ? "cc-prod" : "cc-lab" alimente un tag FinOps. Si vous écrivez la même expression dans dix resources, une typo crée un drift de tags. Passez par un local nommé : le plan reste lisible et la revue de code plus courte.
Piège : enchaîner trois ternaires imbriqués. Préférez des locals intermédiaires ou une map de lookup (lookup / try) pour garder le module maintenable.
En HCL, le ternaire choisit une valeur selon un booléen. C’est l’outil de base du terraform conditional : tags optionnels, noms dérivés, arguments calculés — sans créer ou supprimer une ressource à lui seul.
variable "environment" {
type = string
default = "lab"
}
variable "enable_versioning" {
type = bool
default = false
}
locals {
bucket_suffix = var.environment == "prod" ? "prod" : "lab"
versioning_status = var.enable_versioning ? "Enabled" : "Suspended"
# Tags conditionnels : null = souvent « omis » côté provider (voir pièges)
cost_center = var.environment == "prod" ? "cc-prod" : null
}
Règles : branches du même type ; condition en bool (pas "true") ; préférez des locals nommés au chaînage. Le ternaire choisit une valeur, pas le nombre d’instances — pour cela, count / for_each.
Étape 2 — count = var.enabled ? 1 : 0 (ressource optionnelle)
Le toggle 0/1 est le pattern le plus enseigné — et le plus dangereux si on l’applique à une base de données prod sans communication : passer enabled=false détruit l’instance cloud. Pour un bucket de lab, c’est pédagogique. Pour un RDS, c’est un incident. Documentez le flag et préférez parfois un module séparé plutôt qu’un booléen unique « god mode ».
Pour créer 0 ou 1 instance d’une ressource, le pattern classique est :
variable "enable_lab_bucket" {
type = bool
description = "Si true, crée le bucket de lab ; sinon aucune instance."
default = false
}
data "aws_caller_identity" "current" {}
resource "aws_s3_bucket" "optional" {
count = var.enable_lab_bucket ? 1 : 0
bucket = "lab-tf-cond-${data.aws_caller_identity.current.account_id}"
tags = {
Name = "lab-conditional"
ManagedBy = "terraform"
Lab = "conditionnels"
}
}
output "optional_bucket_id" {
description = "Id du bucket si créé, sinon null."
value = try(aws_s3_bucket.optional[0].id, null)
}
Avec enable_lab_bucket = false, le plan montre 0 à créer. Avec true, 1 create. L’adresse devient aws_s3_bucket.optional[0] — d’où l’index [0] dans les références.
Attention : basculer true → false détruit l’objet cloud. C’est voulu pour un flag « feature on/off », pas pour N objets nommés (voir étape 3 et le tuto Boucles).
Étape 3 — for_each avec map filtrée (préférable pour la stabilité)
Dès que plusieurs objets portent un nom métier (logs, tmp, audit), filtrez la map puis itérez avec for_each. Désactiver tmp ne renumérote pas logs : l’adresse state ["logs"] reste stable. C’est exactement le problème que count indexé pose quand on retire l’élément du milieu. Reliez ce réflexe au tuto Boucles.
Quand plusieurs objets peuvent être présents ou absents par clé, filtrez une map plutôt que d’empiler des count :
variable "lab_features" {
type = map(object({
enabled = bool
purpose = string
}))
default = {
logs = { enabled = true, purpose = "logs" }
tmp = { enabled = false, purpose = "scratch" }
}
}
locals {
# Map filtrée : seules les clés enabled = true
active_features = {
for k, v in var.lab_features : k => v if v.enabled
}
}
resource "aws_s3_bucket" "features" {
for_each = local.active_features
bucket = "lab-tf-feat-${each.key}-${data.aws_caller_identity.current.account_id}"
tags = {
Name = each.key
Purpose = each.value.purpose
Lab = "for-each-filtre"
}
}
Adresse …["logs"] stable : désactiver tmp ne réordonne rien. Règle P2 : for_each pour objets nommés ; count pour le toggle 0/1. Une clé enabled = false retire cette instance seulement.
Étape 4 — Fonctions utiles : coalesce, try, can, one
try et one rendent les outputs robustes face à une resource absente (count = 0). coalesce évite les chaînes vides quand un override optionnel est null. can sert surtout dans des validations ou des locals défensifs. Ces fonctions ne remplacent pas un booléen clair var.enable_… pour décider de créer ou non — elles sécurisent la lecture après coup.
Ces fonctions complètent le terraform conditional hors meta-arguments :
| Fonction | Rôle | Exemple typique |
|---|---|---|
coalesce(a, b, …) |
Première valeur non null | Défaut de chaîne / ID |
try(expr, fallback) |
Évalue expr ; si erreur → fallback |
Accéder à [0] optionnel |
can(expr) |
true si expr s’évalue sans erreur |
Valider une forme avant usage |
one(list) |
Exactement 0 ou 1 élément → null ou l’élément ; sinon erreur |
Sortie d’un count 0/1 |
locals {
display_name = coalesce(var.custom_name, "lab-default")
# Équivalent plus sûr que length(...) > 0 ? …[0] : null
bucket_id_safe = try(aws_s3_bucket.optional[0].id, null)
bucket_id_one = one(aws_s3_bucket.optional[*].id)
# can : utile pour valider une structure avant de la consommer
has_purpose = can(var.lab_features["logs"].purpose)
}
variable "custom_name" {
type = string
default = null
}
one(resource[*].attr) est particulièrement lisible avec count 0/1 : vous évitez le ternaire length(…) == 1 ? …[0] : null. try reste le filet de sécurité le plus courant dans les outputs.
Étape 5 — Pièges : null vs omit, types, références
Les erreurs « Missing resource instance key » et « Inconsistent conditional result types » dominent les forums. La première se corrige avec [0] / clé for_each ou try/one. La seconde impose d’homogénéiser les types. Quant aux valeurs unknown au plan : ne basez jamais count/for_each sur un ID cloud encore inconnu ; décidez avec une variable d’entrée.
nullvs omettre l’argument — Passernullà un argument optionnel signifie souvent « utiliser le défaut provider / ne pas envoyer ». Ce n’est pas équivalent à un attribut obligatoire manquant. Vérifiez la doc du resource : certains attributs rejettentnullexplicitement.
- Types des branches ternaires —
var.x ? "a" : 1échoue. Homogénéisez (tostring,tonumber) ou reformulez enlocals.
- Référencer une ressource à
count = 0—aws_s3_bucket.optional.id(sans index) est invalide. Utiliseztry(…[0].id, null),one(…[*].id), ou un ternaire survar.enable_lab_bucket.
- Valeurs inconnues au plan — Une condition dépendant d’un attribut cloud encore inconnu peut bloquer
count/for_each(« value is unknown »). Préférez des variables / locals connus au plan pour décider de la présence.
countetfor_eachensemble — Interdits sur le même bloc. Choisissez l’un ou l’autre.
- Chaîne
"false"— Tapezbooldansvariable; évitez les strings"true"/"false".
Étape 6 — Lab ca-central-1 (toggle bool → ressource légère)
Enchaînez plan/apply avec true puis false pour voir le destroy de [0] dans le plan. C’est le moment où le concept devient concret. Variante pédagogique : recréez le même besoin avec map filtrée et comparez terraform state list. Destroy final obligatoire — un bucket lab oublié pollue le compte.
Objectif : activer / désactiver un bucket S3 via enable_lab_bucket, Region ca-central-1.
# versions.tf
terraform {
required_version = ">= 1.9.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# providers.tf
provider "aws" {
region = "ca-central-1"
# Auth : AWS_PROFILE / SSO — jamais de clés ici
}
# variables.tf
variable "enable_lab_bucket" {
type = bool
description = "Crée (true) ou omet (false) le bucket de lab."
default = true
}
# main.tf — data + resource count (étape 2)
# outputs.tf — try / one (étape 4)
Commandes :
mkdir -p ~/terraform-conditionnels-lab && cd ~/terraform-conditionnels-lab
# collez versions.tf, providers.tf, variables.tf, main.tf, outputs.tf
export AWS_PROFILE=lab
export AWS_REGION=ca-central-1
terraform init
terraform fmt
terraform validate
# Plan avec ressource ON
terraform plan -var='enable_lab_bucket=true'
terraform apply -auto-approve -var='enable_lab_bucket=true'
terraform output
# Plan avec ressource OFF (destroy de l’instance count[0])
terraform plan -var='enable_lab_bucket=false'
terraform apply -auto-approve -var='enable_lab_bucket=false'
terraform destroy -auto-approve
Observez : true → create ; false → destroy de [0]. Variante : comparez avec le for_each filtré (terraform state list).
Étape 7 — Checklist
Avant Dynamic blocks, validez que vous savez choisir : ternaire (valeur), count 0/1 (toggle simple), for_each filtré (objets nommés), et outputs sûrs. Cette grille évite 80 % des anti-patterns conditionnels en revue de module.
- Ternaire = valeur conditionnelle ;
count/for_each= présence d’instances. - Toggle simple 0/1 →
count = var.enabled ? 1 : 0. - Plusieurs objets nommés on/off → map filtrée +
for_each(stabilité). - Outputs sûrs :
try,one, jamais.idnu sur uncount0. coalescepour défauts ;canpour tests d’évaluabilité.- Homogénéité des types ;
null≠ oubli d’attribut obligatoire. - Lab
ca-central-1+ destroy ; aucun secret dans le code.
Nettoyage
cd ~/terraform-conditionnels-lab
terraform destroy -auto-approve
rm -rf ~/terraform-conditionnels-lab
Confirmez l’absence du bucket (ou users) de lab dans le compte.
Erreurs fréquentes
| Symptôme | Cause | Correction |
|---|---|---|
Missing resource instance key |
Référence sans [0] / clé sur ressource count/for_each |
try(…[0].id, null) ou one(…[*].id) |
Inconsistent conditional result types |
Branches ternaires de types différents | Aligner les types ou convertir |
Destroy inattendu en passant enabled=false |
Comportement normal du toggle count |
Documenter le flag ; snapshots / backups si prod |
Invalid count argument / unknown |
Condition dépend d’une valeur cloud inconnue au plan | Décider via variable / local connu |
count et for_each set |
Meta-arguments incompatibles ensemble | Choisir l’un ; préférer map filtrée si N clés |
| Feature « absente » mais adresse qui glisse | Plusieurs toggles via count indexé |
Passer à for_each + clés stables |
Booléen passé en string "false" |
tfvars / CI mal typés | type = bool + vraie valeur HCL |
|---|---|---|
one() échoue avec 2+ éléments |
Splat inattendu | Filtrer avant one ou revoir le count |
| Module enfant ignore le flag | Booléen non passé en input | Propager var.enable_… explicitement |
Quiz (3 questions)
1. Que fait count = var.enable_lab_bucket ? 1 : 0 ?
- A. Change seulement un tag
- B. Crée 0 ou 1 instance selon le booléen
- C. Remplace obligatoirement
for_each
2. Pourquoi filtrer une map puis for_each plutôt que plusieurs count 0/1 ?
- A. C’est plus rapide à appliquer
- B. Les clés stables évitent le réordonnancement et clarifient l’état
- C.
countest déprécié depuis Terraform 1.9
3. Quelle fonction renvoie l’unique élément d’une liste de 0 ou 1 élément, sinon erreur ?
- A.
coalesce - B.
can - C.
one
4. Que se passe-t-il si vous passez enable_lab_bucket=false après un apply true ?
- A. Rien : le bucket reste
- B. Terraform planifie le destroy de l’instance
[0] - C. Seuls les tags changent
5. Pourquoi éviter un ternaire dont les branches sont string et number ?
- A. C’est plus lent
- B. Types inconsistants — erreur de validation HCL
- C. Interdit seulement en workspace prod
Réponses : 1‑B · 2‑B · 3‑C · 4‑B · 5‑B
FAQ
count est-il déprécié ? Non. Il reste pertinent pour 0/1 ou des listages indexés simples. Pour des collections nommées évolutives, for_each est préférable.
Puis-je conditionner un bloc entier dynamic ici ? Les bases sont ici ; la syntaxe dynamic / for_each imbriqué est traitée dans Dynamic blocks. N’anticipez pas en recopiant des blocs opaques.
Comment tester sans créer de bucket ? terraform plan -var='enable_lab_bucket=false' puis true : observez 0 vs 1 create sans apply si vous préférez le mode lecture seule.
Pour aller plus loin
- Doc : conditionals, count, for_each, fonctions
- Sur ce site : Boucles, Variables, Locals ; suite Dynamic blocks
Maillage série Terraform (P2)
| ← Précédent | Boucles (terraform-boucles-for-each) |
| → Suivant | Dynamic blocks (terraform-dynamic-blocks) |
| Aussi | Variables (terraform-variables) · Locals (terraform-locals) |
| Carte | Overview · … · Loops · Conditionals · Dynamic blocks · Modules · Workspaces · … |
Retour parcours Terraform — hub de la série et leçons sœurs.



