Infrastructure as Code braucht Code-Reviews
Sobald Infrastruktur Code ist, entscheidet ein kleiner Diff über Sicherheit, Kosten und Ausfallrisiko – deshalb gehört sie in denselben Review-Prozess wie Anwendungscode.
Sobald Infrastruktur Code ist, entscheidet ein kleiner Diff über Sicherheit, Kosten und Ausfallrisiko. Genau deshalb gehört sie in denselben Review-Prozess wie Anwendungscode – mit einem anderen Blick auf das, was in der Pull Request eigentlich passiert.
Warum ich das gerade jetzt aufschreibe
Der konkrete Anlass ist das AWS CDK, das diesen Sommer stabil wurde. Damit beschreibe ich Cloud-Infrastruktur nicht mehr in tausend Zeilen YAML, sondern in TypeScript – mit Typen, Funktionen und Abstraktionen. Das fühlt sich sofort produktiver an, und genau da liegt der Punkt, den ich hier festhalten will.
Denn eine Änderung an der Infrastruktur sieht jetzt aus wie jede andere Pull Request. Ein paar geänderte Zeilen, ein grüner Build, ein Merge. Was in diesem harmlosen Diff steckt, ist aber kein Feature-Flag, sondern eine IAM-Policy, eine Security-Group oder die Speicherzuweisung einer Funktion, die pro Aufruf abgerechnet wird. Die Reibung beim Ändern ist gesunken – das Risiko pro Änderung nicht. Diese Schere ist das eigentliche Thema.
Was sich mit Infrastructure as Code wirklich ändert
Vorher lebte Infrastruktur in Klick-Sessions in der Konsole und im Kopf von zwei, drei Leuten. Niemand konnte eine Änderung reviewen, weil es keinen Diff gab. Wer eine Regel in einer Security-Group aufmachte, hinterließ keine nachvollziehbare Spur. Ausfälle wurden zu archäologischen Ausgrabungen: Wer hat wann was verstellt, und warum?
Infrastructure as Code dreht das um. Jede Änderung wird zu einem Commit, jeder Commit ist versioniert, jede Version ist überprüfbar. Das ist der eigentliche Gewinn – nicht die schönere Syntax, sondern die Tatsache, dass Infrastruktur endlich denselben Kontrollmechanismen unterliegt wie der Rest des Systems. Rollback wird ein git revert, Nachvollziehbarkeit wird ein git blame, Wissen wird ein lesbares Repository statt eines Stammtisch-Gesprächs.
Nur: Die Review-Disziplin ist mit dieser Verschiebung nicht automatisch mitgewachsen. Wir reviewen Anwendungscode seit Jahren mit einem klaren Blick für Nebenwirkungen, Grenzfälle und Sicherheit. Bei Infrastruktur-Code lesen viele Teams denselben Diff mit halber Aufmerksamkeit, weil er kurz aussieht und der Build grün ist. Ein grüner Build heißt bei IaC aber nur, dass die Synthese durchlief – nicht, dass die entstehende Infrastruktur sicher, bezahlbar und zurückbaubar ist.
Der Diff, den ich wirklich reviewen muss
CDK arbeitet mit Constructs: Bausteinen, die eine oder mehrere CloudFormation-Ressourcen samt sinnvoller Defaults kapseln. Das ist die Stärke und zugleich die Falle. Ein High-Level-Construct kann aus drei Zeilen TypeScript ein Dutzend Ressourcen erzeugen – eine Funktion, eine Log-Gruppe, eine Execution-Rolle, ein paar Berechtigungen. Der TypeScript-Diff zeigt mir die Absicht. Was tatsächlich provisioniert wird, zeigt er nicht.
Deshalb ist für mich das eigentliche Review-Artefakt nicht der Code allein, sondern der cdk diff: die Gegenüberstellung von aktuellem und geplantem CloudFormation-Zustand. Erst da wird sichtbar, dass eine unscheinbare Zeile eine Rolle mit einer Wildcard-Berechtigung anlegt oder eine Security-Group öffnet.
const handler = new lambda.Function(this, "ApiHandler", {
runtime: lambda.Runtime.NODEJS_10_X,
handler: "index.handler",
code: lambda.Code.fromAsset("dist/api")
});
Diese vier Zeilen sind bewusst klein. Sie erzeugen aber mehr als eine Funktion: eine Log-Gruppe, eine Execution-Rolle mit Basisrechten und die Verdrahtung dazwischen. Die Review-Frage lautet nicht „läuft der Handler?", sondern „welche Rechte bekommt diese Rolle, wie lange bleiben die Logs liegen, und was kostet ein Aufruf?". Ein gutes Construct reduziert Wiederholung und kapselt gute Defaults – es darf aber nicht verstecken, welche Ressourcen, Rechte und Kosten entstehen. Genau diese Grenze prüfe ich im Review.
Die teuerste Zeile ist oft eine Berechtigung
Der klassische Fehler ist die bequeme Berechtigung. Es funktioniert sofort, wenn ich einer Funktion breiten Zugriff gebe, und niemand merkt im Demo, dass die Grenze zu weit gezogen ist.
// convenient, and wrong
bucket.grantReadWrite(handler);
table.grant(handler, "dynamodb:*");
Das läuft. Es ist auch die Art von Zeile, die ein Jahr später in einem Security-Audit rot markiert wird. Least Privilege ist keine Fleißaufgabe für später, sondern eine Review-Entscheidung im Moment des Merges – denn danach ist die Rolle in Betrieb, und aus einem „mach ich noch enger" wird selten etwas. Im Review frage ich deshalb konkret: Braucht diese Funktion wirklich Schreibrechte, oder nur Lesen? Braucht sie den ganzen Table oder eine Aktion? Jede Wildcard ist eine Behauptung, die belegt werden muss.
Das Muster wiederholt sich bei öffentlichen Buckets, offenen Security-Groups und geteilten Secrets. Keiner dieser Fehler ist technisch schwierig. Alle sind billig zu verhindern, wenn jemand den Diff mit der richtigen Frage liest – und teuer, wenn niemand es tut.
Kosten gehören in den Review
Bei serverloser Infrastruktur ist die Rechnung ein direktes Ergebnis von Codezeilen. Die Speichergröße einer Funktion, provisionierte Nebenläufigkeit, ein NAT-Gateway, ein zu groß gewählter Instanztyp, eine Retention-Einstellung, die Logs für immer aufhebt – all das sind einzeilige Änderungen mit einer laufenden monatlichen Konsequenz.
Ich habe erlebt, wie eine einzige geänderte Zeile die Cloud-Rechnung eines Teams verdoppelt hat, weil ein Default unbedacht übernommen wurde. Der Diff war unauffällig, der Build grün, der Review ein Daumen-hoch. Kostentransparenz im Review heißt nicht, jede Rechnung vorherzusagen. Es heißt, die Frage überhaupt zu stellen: Was ändert diese Zeile an den laufenden Kosten – einmalig, pro Aufruf, oder pro Monat? Serverless verschiebt Betriebsarbeit, es entfernt sie nicht. Rechte, Logs, Kaltstarts, Timeouts, Kosten und Rollback-Fähigkeit müssen weiterhin explizit entworfen werden.
Wonach ich im Infrastruktur-Review sehe
Über die einzelnen Beispiele hinweg prüfe ich immer dieselben Grenzen. Ist die Berechtigung so eng wie möglich, oder öffnet der Diff mehr, als die Aufgabe braucht? Wie groß ist der Blast Radius, wenn diese Ressource falsch konfiguriert ist – trifft es einen Service oder das ganze Konto? Ist die Änderung idempotent und rückbaubar, oder erzeugt ein zweites Deployment einen anderen Zustand als das erste? Was passiert bei einem fehlerhaften Deployment – gibt es einen sauberen Weg zurück, oder muss jemand nachts von Hand reparieren? Und produziert der Betrieb genug Signale – Logs, Metriken, Alarme –, um einen Fehler später überhaupt zu erklären?
Diese Fragen wirken trocken. Sie sind aber der Unterschied zwischen einem System, das im Demo sauber aussieht, und einem, das unter Wiederholung, Teilausfällen, alten Daten und falschen Berechtigungen trägt. Der glückliche Pfad ist nie das Problem. Interessant wird Infrastruktur genau dort, wo etwas schiefgeht.
Der Trade-off der Abstraktion
CDK bietet Constructs auf mehreren Ebenen: dünne Hüllen um einzelne CloudFormation-Ressourcen, komfortable Bausteine mit guten Defaults und ganze Muster aus mehreren Ressourcen. Je höher die Ebene, desto weniger Code schreibe ich – und desto mehr Entscheidungen delegiere ich an Defaults, die ich nicht sehe.
Das ist kein Argument gegen Abstraktion. Es ist ein Argument dafür, die Abstraktionsebene bewusst zu wählen und im Review sichtbar zu machen. Ein High-Level-Construct ist ein guter Tausch, wenn seine Defaults zu meinen Anforderungen an Sicherheit und Kosten passen. Er ist ein schlechter Tausch, wenn ich seine Defaults nie geprüft habe und sie in Produktion überrascht kennenlerne. Die methodische Konsequenz ist einfach: Der cdk diff gehört in jede Pull Request, damit die Ebene, auf der ich delegiere, nicht implizit bleibt.
Wo der Review in die Pipeline gehört
flowchart LR Source --> Synth Synth --> Diff["cdk diff"] Diff --> Review["Review<br/>Rechte · Kosten · Rollback"] Review --> Deploy Deploy --> Observe
Der Diff ist keine Formalie am Ende, sondern der Ort, an dem der Review greift. Er läuft vor dem Deployment, als sichtbares Artefakt in der Pull Request. Das verschiebt den Fehler aus dem Betrieb in die Review-Phase – und das ist der ganze Sinn. Ein Konfigurationsfehler, der im Diff auffällt, kostet zehn Minuten Diskussion. Derselbe Fehler in Produktion kostet einen Incident, eine Nachtschicht und Vertrauen.
Was die Maschine prüft, was der Mensch prüft
Ein guter Review-Prozess verschwendet keine menschliche Aufmerksamkeit an Dinge, die eine Regel erledigen kann. Vieles am Infrastruktur-Review ist mechanisch und lässt sich automatisieren: offene Security-Groups, unverschlüsselte Buckets, Wildcard-Policies, fehlende Log-Retention. Statische Analyse auf dem synthetisierten CloudFormation-Template – etwa mit cfn_nag – fängt diese Klasse von Fehlern in der Pipeline ab, bevor überhaupt ein Mensch draufschaut.
Weil die Infrastruktur jetzt Code ist, kann ich außerdem Annahmen als Test festschreiben. CDK erlaubt Assertions gegen das synthetisierte Template: „diese Rolle darf keine Wildcard-Action enthalten", „dieser Bucket ist nicht öffentlich". Ein solcher Test schlägt fehl, sobald jemand die Grenze aufweicht – nicht erst im Audit ein Jahr später. Das ist derselbe Reflex, den ich von Anwendungscode kenne: Was einmal richtig war und richtig bleiben muss, wird ein Test, kein Merksatz.
Die Automatisierung nimmt dem menschlichen Review nicht die Arbeit ab, sie fokussiert sie. Wenn die Maschine die immer gleichen Regeln durchsetzt, kann der Mensch sich auf das konzentrieren, was keine Regel entscheiden kann: Ist diese Abstraktionsebene die richtige? Ist der Blast Radius vertretbar? Passt die Kostenkonsequenz zur Aufgabe? Genau diese Fragen sind der Grund, warum es den Review überhaupt gibt.
Der Fehler, das Werkzeug für die Lösung zu halten
Ein Werkzeug erzeugt keine gute Architektur, es kann sie nur ausdrücken. CDK ist kein Sicherheitskonzept. CloudFormation ist kein Berechtigungsmodell. Serverless ist keine Betriebsstrategie. Diese Werkzeuge machen es leichter, gute Grenzen zu ziehen – sie ziehen sie nicht von selbst. Wer glaubt, mit dem Umstieg auf Infrastructure as Code sei die Sicherheits- und Kostenfrage erledigt, hat nur das Medium gewechselt, in dem dieselben Entscheidungen jetzt schneller und unauffälliger getroffen werden.
Genau deshalb braucht Infrastructure as Code den Review mehr, nicht weniger. Die niedrige Reibung, die den ganzen Ansatz so attraktiv macht, ist gleichzeitig ihr größtes Risiko. Ändern ist billig geworden – Verstehen muss es bleiben.
Was das dem Team bringt
Der Nutzen ist messbar, auch wenn er sich als Abwesenheit zeigt. Fehler, die früher als Incident auftraten, tauchen jetzt als Kommentar in einer Pull Request auf. Die Cloud-Rechnung bleibt vorhersehbar, weil Kostenänderungen benannt statt entdeckt werden. Onboarding wird kürzer, weil neue Leute die Infrastruktur lesen statt raten. Und die Durchlaufzeit sinkt am Ende sogar – nicht, weil weniger geprüft wird, sondern weil weniger repariert werden muss.
Ein Review kostet Minuten. Ein offener Bucket, eine Wildcard-Rolle oder ein verdoppeltes NAT-Gateway kostet Tage und im schlimmsten Fall Kundendaten. Infrastructure as Code hat die Infrastruktur endlich in ein Format gebracht, in dem sich dieser Handel lohnt. Es wäre schade, den Review als einzigen Schritt auszulassen, der aus einem Diff eine Entscheidung macht.
Weiterführende Quellen
- AWS CDK Developer Guide: https://docs.aws.amazon.com/cdk/latest/guide/home.html
- AWS CDK Constructs: https://docs.aws.amazon.com/cdk/latest/guide/constructs.html
Die Quellen helfen beim Gegenprüfen von Plattformverhalten und Defaults. Die eigentliche Aussage bleibt aber nicht in der Doku, sondern in der Architekturentscheidung im konkreten Diff – und darin, dass jemand sie liest, bevor sie live geht.
Kommentare