🏗️ Terraform Lektion 14/15 ~7 Min. Fortgeschritten

Best Practices

Production-ready Terraform Projekte.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kennst du die Muster production-ready Terraform-Projekte: Repository-Struktur, Secret-Handling, Versionierung, Blast-Radius-Kontrolle und operativer Betrieb. Du kannst ein Projekt anhand einer Checkliste bewerten und typische Anti-Patterns in bestehenden Codebases erkennen.

Das Problem: Tutorial-Terraform in Produktion

Viele Teams starten mit einem main.tf in einem Repo — und wachsen nie darüber hinaus. Drei Jahre später: 2.000 Zeilen in einer Datei, Secrets in tfvars auf Laptops, ein State für alles, kein Linting, Provider unpinned, und niemand traut sich an terraform apply in Prod.

Best Practices sind keine pedantischen Regeln — sie sind die Antwort auf Incidents, die andere Teams schon hatten. Du musst sie nicht alle am ersten Tag umsetzen, aber du musst sie benennen können — im Interview und im Architektur-Review.

Repository-Struktur: lesbar und reviewbar

Ein bewährtes Layout:

platform-infra/
├── modules/           # wiederverwendbare Bausteine
│   ├── vpc/
│   └── eks/
├── env/
│   ├── dev/
│   ├── staging/
│   └── prod/
│       ├── main.tf
│       ├── backend.tf
│       ├── variables.tf
│       └── terraform.tfvars   # nicht für Secrets!
├── .terraform.lock.hcl
├── .gitignore           # *.tfstate, .terraform/
└── .github/workflows/

Prinzipien:

  • Ein State pro Umgebung/Stack — nicht ein Monolith
  • Module für Wiederverwendung, dünne Root-Module pro Env
  • Keine Secrets in Git — weder in .tf noch in .tfvars
  • Lock-Datei committen — reproduzierbare Provider

Dateinamen sind Konvention, nicht Gesetz — aber main.tf + variables.tf + outputs.tf pro Modul hilft Reviewern.

Secrets: sensitive ist kein Vault

variable "db_password" {
  type      = string
  sensitive = true
}

sensitive = true versteckt den Wert in der CLI-Ausgabe — nicht im State. Der State liegt unverschlüsselt lesbar in S3, wenn du encrypt = false setzt.

Production-Patterns:

  • Secrets aus Vault, AWS Secrets Manager oder SSM Parameter Store via Data Source zur Laufzeit
  • CI injects Secrets als TF_VAR_* — nie in Repo
  • State-Bucket: SSE-KMS, Zugriff nur für Pipeline-Rolle
  • Keine Passwörter in terraform.tfvars.gitignore reicht nicht, wenn es schon committed war

Blast Radius: kleine States, klare Grenzen

Je größer der State, desto länger Plan/Apply, desto höher das Risiko eines fehlgeschlagenen Applies mitten in 200 Ressourcen.

Stack-Schnitt typischerweise entlang von:

  • Lifecycle: Netzwerk (selten) vs. Apps (häufig)
  • Team-Ownership: Platform-Team vs. Produktteam
  • Blast Radius: Datenbank-Stack getrennt von Compute

terraform apply sollte in unter wenigen Minuten durchlaufen — wenn nicht, ist der Stack zu groß.

Versionierung und Upgrade-Disziplin

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
  • Terraform CLI und Provider separat pinnen
  • Upgrades in Staging zuerst; Changelog lesen
  • moved-Blöcke statt manuellem state mv bei Resource-Renames (ab TF 1.1)
  • Dependabot/Renovate für Provider-Updates mit CI-Plan als Gate

Operativer Betrieb: mehr als Apply

PraxisZweck
Drift-Detection (scheduled plan)ClickOps finden
State-Backup (S3-Versionierung)Recovery bei Korruption
prevent_destroy auf kritische ResourcesSchutz vor terraform destroy
Runbooks für State Lock, Import, RecoveryOn-Call-tauglich
CODEOWNERS auf /env/prod/Pflicht-Review für Prod

Tagging-Standard: Jede Resource bekommt Environment, ManagedBy = terraform, Team — über default_tags am Provider oder ein Label-Modul. Ohne Tags ist Cost Allocation und Incident-Scoping unmöglich.

Anti-Patterns — explizit benennen

  • terraform apply lokal auf Prod — CI-only für Prod
  • -target als Workflow — technische Schuld
  • Monolith-State — alles in einem Apply
  • Copy-Paste statt Module — Drift garantiert
  • Kein Plan-Review — Blind Apply
  • Provider unpinned — Breaking Changes über Nacht
  • State in Git — Security-Incident waiting to happen
  • count/for_each mit hartem Index[0] bricht bei Reorder

Praxis: Projekt-Checkliste durchgehen

Nimm ein bestehendes Lab oder Repo und prüfe:

# Struktur
ls -la env/ modules/ 2>/dev/null || echo "Keine env/modules-Trennung"

# Secrets in Git?
git log -p -- '*.tfvars' | head -20

# State lokal?
test -f terraform.tfstate && echo "WARN: lokaler State" || echo "OK"

# Lockfile?
test -f .terraform.lock.hcl && echo "OK: Lockfile" || echo "WARN: kein Lock"

# fmt
terraform fmt -check -recursive && echo "OK: fmt" || echo "FIX: fmt"

Dokumentiere Findings — das ist ein realistisches Platform-Engineering-Audit.

Typische Stolperfallen

Best Practices als Big-Bang: Alles auf einmal refactoren blockiert Features. Inkrementell: erst Remote State, dann Module, dann CI — eine Sache pro Sprint.

Golden Path ohne Escape Hatch: Teams umgehen Terraform, wenn der offizielle Weg zu langsam ist. Dokumentierter Escape mit Zeitlimit schlägt verstecktes ClickOps.

Dokumentation in Confluence, Code in Git: Runbooks veralten. README im Repo mit Backend-Setup, Apply-Prozess und Notfall-Kontakten.

prevent_destroy überall: Schützt zwar, blockiert aber legitime Replacements. Nur auf wirklich kritische Stateful Resources.

Interview-Vorbereitung

„Wie sieht production-ready Terraform aus?” — Struktur (Module, env-Stacks), Remote State + Lock, Secrets external, CI Plan/Apply, Version Pinning, Drift-Detection, Tagging, kleine Blast Radius.

Follow-ups:

  • „Wo liegen Secrets?” — Vault/SSM/Secrets Manager; nie im State unverschlüsselt blind vertrauen.
  • „Wie groß soll ein State sein?” — Klein genug für schnellen Plan; getrennt nach Lifecycle und Ownership.
  • „Größtes Anti-Pattern?” — Local apply auf Prod, Monolith-State, State in Git — eines konkret ausarbeiten.
  • „Wie upgradest du Provider?” — Changelog, Staging, plan, Lockfile update.

Zusammenfassung

Production-ready Terraform bedeutet: klare Repo-Struktur mit Modulen und getrennten Env-Stacks, Remote State mit Verschlüsselung, Secrets außerhalb von Git, gepinnte Versionen, CI-gated Plan/Apply, Drift-Detection und kleine Blast Radii. Anti-Patterns wie Monolith-State, lokaler Prod-Apply und unpinned Provider sind bewusst zu vermeiden — nicht aus Pedanterie, sondern weil die Alternativen teuer sind.

In der abschließenden Lektion bündeln wir alles für die Interview-Vorbereitung — typische Fragen, Antwortstrukturen und Troubleshooting-Szenarien aus dem gesamten Lernpfad.