Terraform hat sich als das führende Werkzeug für Infrastructure as Code etabliert: Module beschreiben Ressourcen, Pläne zeigen die geplanten Änderungen und ein apply setzt die Konfiguration in der Cloud um. In der Praxis werden Terraform‑Konfigurationen jedoch oft nur flüchtig geprüft, vielleicht per terraform validate, und der restliche Code verlässt sich auf Peer‑Reviews und gute Absichten. Gerade in regulierten Branchen oder sicherheitskritischen Projekten reicht das nicht aus. Fehler wie falsch gesetzte Defaults, öffentlich erreichbare Ressourcen oder fehlende Verschlüsselung können zu Datenpannen und hohen Kosten führen. In diesem Beitrag lernen Sie einen Ansatz kennen, mit dem Sie Terraform‑Konfigurationen konsequent testen können – ohne echte Ressourcen anzulegen. Grundlage ist der JSON‑Output des terraform plan, der mit dem Open Policy Agent (OPA) und Conftest gegen definierte Regeln geprüft wird.
Warum ist das Testen von Terraform‑Konfigurationen wichtig?
Terraform definiert produktive Infrastruktur. Ein einziger Konfigurationsfehler kann eine Datenbank weltweit erreichbar machen, Verschlüsselung deaktivieren oder durch fehlende Tags die Kostenkontrolle sabotieren. Laut aktuellen Cloud‑Sicherheitsstudien waren 80% der Unternehmen im vergangenen Jahr von Cloud‑Sicherheitsvorfällen betroffen und Gartner prognostiziert, dass bis 2025 99% der Cloud‑Sicherheitsfehler beim Kunden liegen. Insbesondere durch Fehlkonfigurationen. Mehr als 32% der gemeldeten Vorfälle stammen direkt aus Fehlkonfigurationen. Diese Zahlen verdeutlichen, dass Infrastruktur‑Code genauso strengen Prüfungen unterzogen werden muss wie Applikations‑Code.
Besonders gefährlich sind latente Fehlkonfigurationen, die in bestehenden Ressourcen schlummern. Ein AzureStorage‑Account ist zum Beispiel über das Attribut public_network_access_enabled standardmäßig öffentlich erreichbar. Wird diese Eigenschaft nicht explizit deaktiviert, ist das Speicherkonto offen zugänglich. Gleiches gilt für AWS‑S3‑Buckets, Security Groups mit offenen Ports oder fehlende Verschlüsselung. In großen Teams mit vielen Modulen und verschiedenen Umgebungen (Dev, QA, Prod) wird das manuelle Prüfen von Plänen schnell unübersichtlich. Deshalb brauchen wir einen automatisierten, wiederholbaren Testansatz, der vor der Ausführung greift.
Grenzen klassischer terraform test-Ansätze
Seit Version1.6 bringt Terraform einen eigenen Testmechanismus (terraform test). Auf dem Papier klingt das verlockend – in der Praxis offenbaren sich jedoch schnell Schwächen. Tests mit terraform test sind ein zusätzlicher Arbeitsschritt im Workflow: Neben plan und apply muss ein weiterer Befehl in die CI/CD‑Pipeline integriert, gewartet und verstanden werden. Viele Tests erfordern außerdem, dass Ressourcen in einer Testumgebung tatsächlich erstellt werden. Das bedeutet zusätzliche Cloud‑Kosten, Berechtigungsaufwand und potenziell lange Laufzeiten. In streng regulierten Bereichen ist das oft nicht genehmigungsfähig. Es gibt zwar Bemühungen, Tests auf Basis des Plans auszuführen, diese Funktionen sind allerdings noch jung und unterstützen komplexe Compliance‑Regeln nur eingeschränkt. Der Mehrwert gegenüber einer direkten Auswertung des JSON‑Plans bleibt gering.
Ein weiterer Nachteil: terraform test ist eng an die Terraform‑Welt gebunden. Die Regeln sind schwer auf andere Systeme übertragbar; es entsteht eine Insellösung. Für Organisationen, die neben Terraform auch Kubernetes‑Manifeste, Helm‑Charts oder andere IaC‑Formate einsetzen, führt das zu fragmentierten Governance‑Ansätzen. Eine einheitliche Policy‑Ebene fehlt. Hier setzen OPA und Conftest an.
Policy‑as‑Code und Open Policy Agent
Policy‑as‑Code (PaC) ist das Prinzip, Richtlinien als versionierbaren Code zu definieren und automatisiert durchzusetzen. Anstatt sich auf manuelle Prüfungen oder schriftliche Richtlinien zu verlassen, werden Regeln in einer deklarativen Sprache formuliert. Der OpenPolicyAgent (OPA) ist eine universelle Policy‑Engine, die diese Regeln in der Sprache Rego auswertet. OPA wird unter anderem für Kubernetes‑Admission‑Controller, API‑Autorisierung und Cloud‑Governance eingesetzt. Entscheidend ist, dass OPA datengetrieben arbeitet: Es nimmt beliebiges JSON entgegen und liefert eine Entscheidung.
Der Kern des Ansatzes ist einfach: Anstatt nur die geplanten Änderungen zu prüfen, validieren wir den vollständigen zukünftigen Zustand der Infrastruktur, bevor Ressourcen erstellt werden. Terraform bietet hierfür alles, was wir brauchen. Mit terraform plan -out=tfplan erzeugen wir einen Binärplan und konvertieren ihn anschließend mit terraform show -json in eine maschinenlesbare JSON‑Datei. Dieses JSON enthält sowohl den aktuellen State als auch die geplanten Änderungen und das daraus resultierende Endergebnis – inklusive Module, Defaults und Abhängigkeiten.
Conftest ist ein CLI‑Wrapper um OPA, das speziell für die Prüfung strukturierter Dateien wie JSON, YAML oder HCL konzipiert ist. Eine Policy im Rego‑Format wird in einem policy/‑Ordner abgelegt. Beim Ausführen von conftest test gegen die JSON‑Plan‑Datei wertet Conftest diese Policy aus und gibt Verletzungen aus. Damit lassen sich Richtlinien für Terraform, Kubernetes und andere Formate in einem Werkzeug bündeln.
Pipeline-Integration
In einer typischen CI/CD‑Pipeline sieht der Ablauf so aus:
- Plan erstellen:
terraform plan -out=tfplan.binaryerzeugt eine binäre Plan‑Datei. - Plan konvertieren:
terraform show -json tfplan.binary > tfplan.jsonwandelt den Plan in JSON um. - Policies auswerten:
conftest test tfplan.jsonführt die Rego‑Regeln gegen das JSON aus. Jededeny‑Regel führt zu einem Fehler im Pipeline‑Job.
Dieser Ansatz hat mehrere Vorteile. Erstens entstehen keine Cloud‑Kosten, da keine Ressourcen gebaut werden. Zweitens kann der Test früh in der Pipeline ausgeführt werden – noch bevor Merge‑Requests akzeptiert werden. Drittens lassen sich dieselben Policies auch für andere Technologien nutzen.
Beispiel: Azure Storage Account ohne Public Access
Ein konkretes Beispiel veranschaulicht die Methode. Angenommen, jedes Azure‑Storage‑Konto muss den öffentlichen Netzwerkzugriff deaktivieren. Im Terraform‑Modul sehen wir eine Ressource vom Typ azurerm_storage_account:
1resource "azurerm_storage_account" "this" {
2 name = var.storage_account_name
3 resource_group_name = var.resource_group_name
4 location = var.location
5 account_tier = "Standard"
6 account_replication_type = "LRS"
7
8 public_network_access_enabled = false
9}Der folgende Rego‑Code definiert eine Richtlinie, die sicherstellt, dass public_network_access_enabled für jeden geplanten azurerm_storage_accountResource‑Change auf false gesetzt ist:
1package main
2
3deny[msg] {
4 some rc in input.planned_values
5 rc.type == "azurerm_storage_account"
6 rc.change.actions[_] == "create"
7 val := object.get(rc.change.after, "public_network_access_enabled", null)
8 val != false
9 msg := sprintf("%s: public_network_access_enabled muss false sein, gefunden %v", [rc.address, val])
10}Beim Ausführen von conftest test tfplan.json wird der Plan gegen diese Regel geprüft. Ist public_network_access_enabled nicht definiert oder true, schlägt der Test fehl und gibt eine verständliche Fehlermeldung aus. Damit verhindern Sie, dass ein unsicheres Speicherkonto überhaupt im Plan akzeptiert wird.
Erweiterung auf den gesamten zukünftigen State
Obiges Beispiel prüft nur Ressource‑Änderungen. In vielen Projekten ist es aber wichtig, den gesamten zukünftigen Zustand zu validieren. Bestehende Ressourcen, die im aktuellen Plan unverändert bleiben, können trotzdem nicht konform sein. Das JSON‑Objekt planned_values beschreibt den Endzustand nach apply. Eine Policy, die rekursiv durch alle Module läuft und jeden azurerm_storage_account im zukünftigen State prüft, sieht so aus:
1package terraform.azure.storage
2
3# Alle Storage Accounts im geplanten State
4storage_accounts contains sa if {
5 # walk läuft rekursiv durch das gesamte Objekt
6 some path, node
7 walk(input.planned_values.root_module, [path, node])
8
9 # Wir interessieren uns nur für Ressourcen-Objekte mit Typ Storage Account
10 node.type == "azurerm_storage_account"
11
12 sa := node
13}
14
15# Verletzung, wenn public_network_access_enabled nicht exakt false ist
16deny contains msg if {
17 some i
18 sa := storage_accounts[i]
19
20 # Wert lesen (null, falls nicht gesetzt)
21 val := object.get(sa.values, "public_network_access_enabled", null)
22
23 # Alles außer false ist verboten (true oder null / nicht gesetzt)
24 val != false
25
26 msg := sprintf(
27 "Azure Storage Account '%s' hat ungültigen public_network_access_enabled-Wert: %v",
28 [sa.name, val],
29 )
30}Diese Regel durchläuft alle Module, sammelt jede Storage‑Ressource und prüft die Eigenschaften im zukünftigen State. Damit lassen sich latente Fehlkonfigurationen erkennen, selbst wenn die betreffende Ressource im aktuellen Plan nicht geändert wird.
Integration in die CI/CD‑Pipeline
Eine solide Pipeline sollte folgende Schritte beinhalten:
- Code‑Analyse & Formatierung: Verwenden Sie Tools wie
terraform fmtundtflint. - Sicherheits‑Scanner: Tools wie
tfsec,CheckovoderRegulakönnen statische Analysen auf Basis bekannter Best Practices durchführen. - Policy‑Checks mit OPA/Conftest: Führen Sie vor dem
applyden Plan durch OPA/Conftest und blockieren Sie Merge‑Requests bei Verstößen. Achten Sie darauf, die JSON‑Datei nicht im Repository zu versionieren, sondern temporär im Pipeline‑Job zu erzeugen. - Drift Detection: Nutzen Sie
terraform planregelmäßig auch in produktiven Umgebungen, um Abweichungen zwischen Code und Ist‑Zustand zu erkennen. Ein Abgleich mit OPA kann anzeigen, ob Ressourcen nachträglich verändert wurden.
Mit einem solchen mehrstufigen Pipeline‑Design kombinieren Sie Code‑Qualität, Security‑Scanning und Policy‑Enforcement. Bei der Wahl der Tools sollte auf Kompatibilität und Wartbarkeit geachtet werden. OPA und Conftest lassen sich in GitHub Actions, GitLab CI, Jenkins, Azure DevOps oder Terraform Enterprise integrieren und erzeugen maschinenlesbare Reports.
Fazit
Infrastruktur als Code bringt viele Vorteile, birgt aber auch Risiken. Klassische Tests in Terraform sind entweder zu eng an das Ökosystem gebunden oder erfordern das Anlegen realer Ressourcen. Durch die Kombination aus terraform plan, Open Policy Agent und Conftest lassen sich Infrastruktur‑Pläne frühzeitig, kostenneutral und umfassend überprüfen. Der Ansatz validiert nicht nur Änderungen, sondern den gesamten zukünftigen Zustand – ein entscheidender Vorteil für Audit‑ und Compliance‑Anforderungen. Zugleich werden Fehlkonfigurationen nach wie vor als eine der Hauptursachen für Sicherheitsvorfälle genannt. Deshalb sollte das Testen von Terraform mit OPA in jede moderne DevOps‑Pipeline integriert werden. Auf diese Weise wird aus „Infrastructure as Code“ eine „Infrastructure with Guarantees“ – sicher, compliant und nachvollziehbar.
FAQ
Was ist der Unterschied zwischen terraform validate und OPA/Conftest‑Tests?terraform validate prüft nur die Syntax und Grundstruktur des Codes. OPA/Conftest ermöglichen dagegen semantische Prüfungen: Sie erkennen, ob bestimmte Attribute korrekt gesetzt, Tags vorhanden oder Sicherheitsvorgaben eingehalten werden. So lassen sich Compliance‑Regeln automatisiert durchsetzen, bevor Ressourcen erstellt werden.
Muss ich für OPA‑Tests den Plan anwenden?
Nein. Der große Vorteil von OPA/Conftest ist, dass Sie den Terraform‑Plan als JSON exportieren und ohne Cloud‑Zugriff prüfen können. Es werden keine Ressourcen erstellt, sodass keine Kosten entstehen. Der Plan repräsentiert den zukünftigen Zustand, gegen den die Policies ausgewertet werden.
Wie integriere ich OPA in meine Pipeline?
Sie können OPA/Conftest in beliebige CI/CD‑Tools einbinden. In GitHub Actions genügt ein zusätzlicher Job, der terraform plan, terraform show -json und conftest test ausführt. GitLab CI und Azure DevOps haben ähnliche Mechanismen. Wichtig ist, dass der Job den Build abbricht, wenn conftest eine Verletzung meldet.
Können dieselben Policies auch für Kubernetes oder andere Tools verwendet werden?
Ja. OPA ist nicht auf Terraform beschränkt. Die Policies prüfen beliebige JSON- oder YAML‑Strukturen, etwa Helm‑Charts, Kubernetes‑Manifeste oder CloudFormation‑Templates. So schaffen Sie eine einheitliche Governance‑Schicht über verschiedene Plattformen hinweg.
Was passiert, wenn sich meine Terraform‑Module ändern?
Da der Test auf dem JSON‑Plan basiert, spielt es keine Rolle, wie Ihre Module intern aufgebaut sind. Solange die Ressourcen im Plan auftauchen, werden sie geprüft. Bei neuen Modulen müssen Sie möglicherweise zusätzliche Regeln definieren, um neue Ressourcentypen abzudecken.
Wie gehe ich mit komplexen Regeln um?
Rego ist eine mächtige Sprache, erlaubt aber auch Komplexität. Für komplexe Regeln empfiehlt es sich, Policies modular aufzubauen, Helper‑Funktionen zu nutzen und eigene Tests für die Policies zu schreiben. Der OPA‑Playground und die Community‑Regeln (z. B. für Kubernetes) bieten hilfreiche Beispiele.

.png)