Eine Ressource benötigt eine variable Anzahl identischer verschachtelter Blöcke (z. B. Ingress-Regeln), die von einer Liste/Map gesteuert werden.
→Verwenden Sie einen `dynamic`-Block, dessen `for_each` die Sammlung iteriert; referenzieren Sie jedes Element über den Block-Iterator (Standardname = Blocklabel) innerhalb von `content {}`.
Warum: Dynamische Blöcke generieren wiederholte verschachtelte Blöcke ohne Copy-Paste; der Iterator bindet jeden generierten Block an sein Ursprungselement.
Referenz↗
Erstellen Sie eine Ressource pro Eintrag in einer Map von Objekten, stabil schlüsselbasiert, sodass eine Neuanordnung niemals einen Ersatz erzwingt.
→Setzen Sie `for_each = var.objects` (eine Map). Verwenden Sie `each.key` für den stabilen Schlüssel und `each.value.<attr>` für Felder. Vermeiden Sie hier `count` – Indexverschiebungen verursachen unnötige Änderungen.
Warum: Map-Schlüssel sind stabile Identitäten im Zustand; Listenindizes sind positionsbezogen und verschieben sich, wenn Elemente hinzugefügt/entfernt werden.
Entscheiden Sie zwischen count und for_each für mehrere Instanzen.
→Verwenden Sie `for_each`, wenn Instanzen unterschiedliche Identitäten haben (ein Set/Map); verwenden Sie `count` nur für N identische, reihenfolge-unempfindliche Kopien. Bevorzugen Sie for_each für alles, was wachsen oder schrumpfen kann.
Warum: for_each adressiert nach Schlüssel (resource["key"]); count adressiert nach Index (resource[0]), was sich bei Einfügungen/Löschungen neu anordnet.
Eine Eingabevariable ist ein Objekt, bei dem einige Attribute optional sind und Standardwerte benötigen.
→Typisieren Sie es als `object({ name = string, size = optional(number, 10) })`. `optional(type, default)` liefert den Standardwert, wenn der Aufrufer das Attribut weglässt.
Warum: optional() mit einem Standardwert hält Aufrufer prägnant und garantiert gleichzeitig einen konkreten Wert nachgelagert – kein Null-Handling überall.
Referenz↗
Validieren Sie eine Annahme über eine Ressource vor dem Anwenden oder garantieren Sie ein Ergebnis danach.
→Verwenden Sie `lifecycle { precondition { ... } }`, um Eingaben vor dem Erstellen/Aktualisieren zu prüfen, und `postcondition`, um Ausgaben danach zu prüfen. Beide akzeptieren `condition` + `error_message`.
Warum: Benutzerdefinierte Bedingungen schlagen schnell mit einer klaren Meldung fehl, anstatt einen fehlerhaften Apply oder einen verwirrenden nachgelagerten Fehler zu erzeugen.
Referenz↗
Eine Ressource muss neu erstellt werden, wann immer eine andere Ressource oder ein Attribut geändert wird.
→Fügen Sie `lifecycle { replace_triggered_by = [aws_x.y.id] }` hinzu. Wenn sich der referenzierte Wert ändert, erzwingt Terraform den Ersatz dieser Ressource.
Warum: Drückt eine Ersatz-Abhängigkeit deklarativ aus und vermeidet manuelles `-replace` bei jeder zugehörigen Änderung.
Das Ersetzen einer Ressource führt zu Ausfallzeiten, da die alte zerstört wird, bevor die neue existiert.
→Setzen Sie `lifecycle { create_before_destroy = true }`, damit Terraform zuerst den Ersatz bereitstellt und dann den alten zerstört. Stellen Sie eindeutige Namen/keine harten Konflikte sicher.
Warum: Ersatz ohne Ausfallzeiten; achten Sie jedoch auf Namenskollisionen und Quotenlimits, während beide kurzzeitig existieren.
Ungültige Eingabewerte frühzeitig ablehnen (z. B. eine Umgebung, die nicht dev/stage/prod ist).
→Fügen Sie einen `validation { condition = contains(["dev","stage","prod"], var.env), error_message = "..." }`-Block zur Variable hinzu.
Warum: Fängt fehlerhafte Eingaben zur Planzeit mit einer lesbaren Meldung ab, anstatt tief in einem Provider-Aufruf zu scheitern.
Referenzieren Sie einen Wert, der möglicherweise nicht existiert, ohne den Plan abstürzen zu lassen.
→Verwenden Sie `try(local.maybe.value, "default")`, um bei Fehlern zurückzufallen, oder `can(expr)`, um einen Booleschen Wert zu erhalten, ob ein Ausdruck erfolgreich ist.
Warum: Anmutige Handhabung optionaler/variabler Daten; vermeidet "Error: Unsupported attribute" bei fehlenden Schlüsseln.
Wandeln Sie eine Liste in eine Map um oder filtern/formen Sie eine Sammlung für ein Ressourcenargument.
→Verwenden Sie einen `for`-Ausdruck: `{ for u in var.users : u.name => u.role if u.active }` (Map) oder `[for x in list : upper(x)]` (Liste).
Warum: for-Ausdrücke sind der idiomatische Weg, Daten umzugestalten; die `if`-Klausel filtert, die Form `k => v` erstellt Maps.
Eine Variable oder Ausgabe enthält ein Geheimnis, das nicht im Plan/Apply-Output erscheinen sollte.
→Markieren Sie die Variable als `sensitive = true` (und auch Ausgaben). Terraform schwärzt sie im CLI-Output, obwohl sie weiterhin im Zustand gespeichert ist.
Warum: Verhindert unbeabsichtigte Offenlegung in Logs/CI-Output; der Zustand selbst muss weiterhin geschützt werden (verschlüsseltes Backend, Zugriffskontrolle).
Ressourcen in zwei Regionen/Accounts innerhalb einer Konfiguration verwalten.
→Deklarieren Sie aliased Provider (`provider "aws" { alias = "west" region = "us-west-2" }`) und setzen Sie `provider = aws.west` für Ressourcen oder übergeben Sie sie an Module.
Warum: Aliase ermöglichen einer Konfiguration, mehrere Provider-Instanzen anzusprechen; Module empfangen sie explizit über das `providers`-Argument.
Eine versteckte Abhängigkeit (nicht durch Referenzen ausgedrückt) verursacht Reihenfolgeprobleme.
→Fügen Sie `depends_on = [aws_iam_role_policy.x]` hinzu, um die Reihenfolge zu erzwingen. Sparsam verwenden – bevorzugen Sie implizite Abhängigkeiten über Attributreferenzen.
Warum: Explizites depends_on behandelt Abhängigkeiten, die der Graph nicht ableiten kann, aber übermäßiger Gebrauch führt zu konservativen, langsameren Plänen.
Eine Konfigurationsdatei/Benutzerdaten aus einer Vorlage mit strukturierten Variablen rendern.
→Verwenden Sie `templatefile("${path.module}/tpl.tftpl", { items = local.items })`; die Vorlage verwendet `%{ for } / ${}`-Interpolation.
Warum: templatefile hält das Rendering rein/zur Planzeit (im Gegensatz zum veralteten Template-Provider) und unterstützt Schleifen/Bedingungen.
Erstellen Sie eine flache Liste jeder (Subnetz, Regel)-Kombination, um einen einzelnen for_each zu versorgen.
→Verwenden Sie `setproduct(var.subnets, var.rules)` für das Kreuzprodukt oder `flatten([for ...])`, um verschachtelte Listen zu einer zusammenzufassen.
Warum: Diese Funktionen wandeln verschachtelte Daten in die flache, eindeutig schlüsselbare Sammlung um, die for_each benötigt.