Une ressource a besoin d'un nombre variable de blocs imbriqués identiques (par exemple, des règles d'entrée) pilotés par une liste/carte.
→Utilisez un bloc `dynamic` dont le `for_each` itère sur la collection ; référencez chaque élément via l'itérateur de bloc (nom par défaut = libellé du bloc) à l'intérieur de `content {}`.
Pourquoi: Les blocs dynamiques génèrent des blocs imbriqués répétés sans copier-coller ; l'itérateur maintient chaque bloc généré lié à son élément source.
Référence↗
Créez une ressource par entrée dans une carte d'objets, avec une clé stable afin que le réordonnancement ne force jamais le remplacement.
→Définissez `for_each = var.objects` (une carte). Utilisez `each.key` pour la clé stable et `each.value.<attr>` pour les champs. Évitez `count` ici — les décalages d'index provoquent des perturbations.
Pourquoi: Les clés de carte sont des identités stables dans l'état ; les index de liste sont positionnels et se décalent lorsque des éléments sont ajoutés/supprimés.
Choisissez entre `count` et `for_each` pour plusieurs instances.
→Utilisez `for_each` lorsque les instances ont des identités distinctes (un ensemble/une carte) ; utilisez `count` uniquement pour N copies identiques et non sensibles à l'ordre. Préférez `for_each` pour tout ce qui peut croître/diminuer.
Pourquoi: `for_each` s'adresse par clé (resource["key"]) ; `count` s'adresse par index (resource[0]) ce qui entraîne un réarrangement lors des insertions/suppressions.
Une variable d'entrée est un objet où certains attributs sont optionnels et nécessitent des valeurs par défaut.
→Tapez-le comme `object({ name = string, size = optional(number, 10) })`. `optional(type, default)` fournit la valeur par défaut lorsque l'appelant omet l'attribut.
Pourquoi: `optional()` avec une valeur par défaut maintient les appelants concis tout en garantissant une valeur concrète en aval — pas de gestion de null partout.
Référence↗
Validez une hypothèse sur une ressource avant l'application, ou garantissez un résultat après.
→Utilisez `lifecycle { precondition { ... } }` pour affirmer les entrées avant la création/mise à jour, et `postcondition` pour affirmer les sorties après. Les deux prennent `condition` + `error_message`.
Pourquoi: Les conditions personnalisées échouent rapidement avec un message clair au lieu de produire une application défectueuse ou une erreur en aval confuse.
Référence↗
Une ressource doit être recréée chaque fois qu'une autre ressource ou un attribut change.
→Ajoutez `lifecycle { replace_triggered_by = [aws_x.y.id] }`. Lorsque la valeur référencée change, Terraform force le remplacement de cette ressource.
Pourquoi: Exprime une dépendance de remplacement de manière déclarative, évitant le `-replace` manuel à chaque changement lié.
Le remplacement d'une ressource provoque des temps d'arrêt car l'ancienne est détruite avant que la nouvelle n'existe.
→Définissez `lifecycle { create_before_destroy = true }` afin que Terraform provisionne d'abord le remplacement, puis détruise l'ancien. Assurez-vous d'avoir des noms uniques/pas de conflits majeurs.
Pourquoi: Remplacement sans interruption ; mais attention aux collisions de noms et aux limites de quota pendant que les deux existent brièvement.
Rejetez les valeurs d'entrée invalides tôt (par exemple, un environnement qui n'est pas dev/stage/prod).
→Ajoutez un bloc `validation { condition = contains(["dev","stage","prod"], var.env), error_message = "..." }` à la variable.
Pourquoi: Détecte les mauvaises entrées au moment de la planification avec un message lisible au lieu d'échouer profondément dans un appel de fournisseur.
Référencez une valeur qui pourrait ne pas exister sans faire planter le plan.
→Utilisez `try(local.maybe.value, "default")` pour revenir en arrière en cas d'erreurs, ou `can(expr)` pour obtenir un booléen indiquant si une expression réussit.
Pourquoi: Gestion élégante des données optionnelles/à forme variable ; évite "Error: Unsupported attribute" sur les clés absentes.
Transformez une liste en une carte, ou filtrez/formatez une collection pour un argument de ressource.
→Utilisez une expression `for` : `{ for u in var.users : u.name => u.role if u.active }` (carte) ou `[for x in list : upper(x)]` (liste).
Pourquoi: Les expressions `for` sont la manière idiomatique de remodeler les données ; la clause `if` filtre, la forme `k => v` construit des cartes.
Une variable ou une sortie contient un secret qui ne doit pas apparaître dans la sortie de planification/application.
→Marquez la variable `sensitive = true` (ainsi que les sorties). Terraform la masque dans la sortie CLI, bien qu'elle soit toujours stockée dans l'état.
Pourquoi: Empêche la divulgation accidentelle dans les journaux/sorties CI ; l'état lui-même doit toujours être protégé (backend chiffré, contrôle d'accès).
Gérez des ressources dans deux régions/comptes au sein d'une même configuration.
→Déclarez des fournisseurs aliasés (`provider "aws" { alias = "west" region = "us-west-2" }`) et définissez `provider = aws.west` sur les ressources ou transmettez-les aux modules.
Pourquoi: Les alias permettent à une configuration de cibler plusieurs instances de fournisseurs ; les modules les reçoivent explicitement via l'argument `providers`.
Une dépendance cachée (non exprimée par des références) provoque des problèmes d'ordonnancement.
→Ajoutez `depends_on = [aws_iam_role_policy.x]` pour forcer l'ordonnancement. Utilisez-le avec parcimonie — préférez les dépendances implicites via les références d'attributs.
Pourquoi: `depends_on` explicite gère les dépendances que le graphe ne peut pas inférer, mais une utilisation excessive crée des plans conservateurs et plus lents.
Rendez un fichier de configuration/données utilisateur à partir d'un modèle avec des variables structurées.
→Utilisez `templatefile("${path.module}/tpl.tftpl", { items = local.items })` ; le modèle utilise l'interpolation `%{ for }` / `${}`.
Pourquoi: `templatefile` maintient un rendu pur/au moment de la planification (contrairement au fournisseur de template déprécié) et prend en charge les boucles/conditionnelles.
Construisez une liste plate de toutes les combinaisons (sous-réseau, règle) à alimenter un seul `for_each`.
→Utilisez `setproduct(var.subnets, var.rules)` pour le produit cartésien, ou `flatten([for ...])` pour fusionner les listes imbriquées en une seule.
Pourquoi: Ces fonctions transforment les données imbriquées en la collection plate et à clé unique requise par `for_each`.