🏗️ Terraform Lektion 10/15 ~8 Min. Fortgeschritten

Terraform in CI/CD

Automatisierter Plan/Apply in Pipelines.

📝 Meine Notizen

Lernziele

Nach dieser Lektion kannst du Terraform sinnvoll in CI/CD-Pipelines einbinden: Plan in Pull Requests, Apply in kontrollierten Deploy-Jobs. Du kennst das Pattern mit gespeichertem Plan-Artefakt, OIDC-basierter Authentifizierung ohne Long-Lived-Keys und typische Pipeline-Stages. Außerdem weißt du, wie du Apply-Freigaben und Drift-Checks organisierst.

Das Problem: Terraform auf dem Laptop in Produktion

Der klassische Weg — terraform apply lokal mit Admin-Credentials — hat vier Schwachstellen: Kein auditierbarer Review-Pfad (wer hat approved?), Credentials auf Entwickler-Maschinen, kein reproduzierbarer Plan (lokale Provider-Version, lokale tfvars) und kein Gate zwischen „ich habe reviewed” und „es läuft”.

In Platform-Teams ist Terraform in CI/CD der Mindeststandard: Plan läuft automatisch bei jedem PR, Apply nur aus der Pipeline mit least-privilege-Credentials und — in Produktion — manueller oder policy-basierter Freigabe.

Die Pipeline-Architektur: zwei Jobs, ein Plan

Das bewährte Muster trennt Plan und Apply strikt:

PR geöffnet → terraform init → terraform plan → Plan-Artefakt + PR-Kommentar

PR merged → terraform init → terraform apply (gespeicherter Plan) → nur auf main/protected branch

Kernregel: Der Apply-Job nutzt denselben Plan, der im PR reviewed wurde — nicht einen frisch berechneten.

# Auszug GitHub Actions — vereinfacht
jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform plan -out=plan.tfplan -var-file=env/prod.tfvars
      - run: terraform show -no-color plan.tfplan > plan.txt
      - uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: plan.tfplan

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production   # GitHub Environment Protection Rules
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - uses: actions/download-artifact@v4
        with:
          name: tfplan
      - run: terraform init
      - run: terraform apply -auto-approve plan.tfplan

environment: production aktiviert Required Reviewers — ein Mensch muss explizit freigeben, bevor Apply startet.

Authentifizierung: OIDC statt Access Keys

Long-Lived AWS Access Keys in GitHub Secrets sind ein Sicherheitsrisiko — Rotation vergessen, Keys geleakt, übermäßige Rechte. OIDC Federation erlaubt der Pipeline, kurzlebige Tokens von AWS/Azure/GCP anzufordern:

permissions:
  id-token: write
  contents: read

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-terraform
    aws-region: eu-central-1

Die IAM-Rolle vertraut nur Requests von deinem GitHub-Repo und optional nur vom main-Branch. Kein Secret außer der Role-ARN — und selbst der ist nicht sensibel.

In Terraform selbst ändert sich nichts: Der AWS-Provider nutzt die Credential Chain, die OIDC befüllt hat.

Was in jede Pipeline gehört — und was nicht

Pflicht:

  • terraform fmt -check — Stil-Diff vermeiden
  • terraform validate — Syntax vor Plan
  • terraform plan bei jedem PR
  • Plan-Artefakt speichern und im Apply wiederverwenden
  • TF_IN_AUTOMATION=true — unterdrückt interaktive Prompts

Empfohlen:

  • tflint / checkov — Linting und Security (Lektionen 11–12)
  • Scheduled terraform plan auf main — Drift-Detection ohne Code-Änderung
  • -var-file aus verschlüsselten Secrets oder S3, nicht hardcoded

Nicht in Standard-Pipelines:

  • terraform apply -auto-approve auf Prod ohne Environment-Gate
  • terraform destroy ohne expliziten, separaten Workflow mit Extra-Approval
  • -target als Dauerlösung

Drift-Detection: Plan ohne Code-Änderung

Ein Cron-Job, der täglich terraform plan auf main ausführt und bei „Changes detected” alertet, findet ClickOps und externe Änderungen:

on:
  schedule:
    - cron: '0 6 * * 1-5'

jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform plan -detailed-exitcode -var-file=env/prod.tfvars
        continue-on-error: true
        id: plan
      - if: steps.plan.outcome == 'failure' && steps.plan.outputs.exitcode == 2
        run: echo "Drift detected!" && exit 1

Exit Code 2 bei -detailed-exitcode bedeutet: es gibt Änderungen. Exit 0: No changes. Exit 1: Fehler.

Praxis: Minimale Pipeline lokal simulieren

Du kannst den Plan-Apply-Flow ohne CI nachstellen:

mkdir cicd-lab && cd cicd-lab
cat > main.tf <<'EOF'
resource "random_pet" "server" { length = 2 }
output "name" { value = random_pet.server.id }
EOF

export TF_IN_AUTOMATION=true

terraform init
terraform plan -out=plan.tfplan
terraform show -no-color plan.tfplan > plan.txt   # das wäre der PR-Kommentar
cat plan.txt
terraform apply -auto-approve plan.tfplan
terraform destroy -auto-approve

TF_IN_AUTOMATION=true ist das, was CI setzt — probiere den Flow einmal ohne und einmal mit, um den Unterschied in der Ausgabe zu sehen.

Typische Stolperfallen

Apply ohne Plan-Artefakt: Jeder Apply-Job, der selbst plan berechnet, kann vom reviewed Plan abweichen — durch Provider-Updates, geänderte tfvars oder API-Drift zwischen den Jobs.

Backend-Credentials in CI zu mächtig: Die Pipeline braucht State-Write und Cloud-Apply — aber nicht iam:* auf *. Scoped Roles pro Stack.

Plan in PR kommentieren, aber niemand liest: Automatisierte PR-Kommentare mit terraform show helfen nur, wenn Reviewer den Diff verstehen. Schulung und kleine Stacks sind wichtiger als fancy Bots.

Init in jedem Job vergessen: Plan- und Apply-Job brauchen beide init — der Apply-Job lädt nicht automatisch den State des Plan-Jobs, nur das Plan-Artefakt.

Secrets in Plan-Output: Auch mit sensitive können manche Provider Werte leaken. PR-Kommentare filtern oder nur Summary posten.

Interview-Vorbereitung

„Wie integrierst du Terraform in CI/CD?” — Plan bei PR, Apply nur auf main mit gespeichertem Plan; OIDC statt Long-Lived Keys; Environment-Gates für Prod; fmt/validate/lint vor Plan.

Follow-ups:

  • „Warum Plan und Apply trennen?” — Reviewbarkeit, Reproduzierbarkeit, Apply = exakt der reviewed Diff.
  • „Wie authentifizierst du die Pipeline?” — OIDC Federation → kurzlebige Cloud-Credentials; IAM Role mit minimalen Rechten.
  • „Was ist Drift-Detection?” — Scheduled plan auf unverändertem Code; Exit Code 2 = Abweichung.
  • „Wann -auto-approve?” — Nur in CI nach Plan-Artefakt und Environment-Approval — nie interaktiv auf Prod-Laptops.

Zusammenfassung

Terraform in CI/CD bedeutet: Plan bei jedem Change, Apply nur kontrolliert mit dem gespeicherten Plan-Artefakt. OIDC ersetzt Long-Lived Access Keys; Environment Protection Rules gate Prod-Deploys. fmt, validate und optional Lint laufen vor Plan; Drift-Checks per Scheduled Plan finden ClickOps. Wer das Pattern beherrscht, hat den Übergang von Solo-Hacking zu Team-Betrieb gemeistert.

In der nächsten Lektion schärfen wir die Qualität mit Testing und Validation.