Sägetstrasse 18, 3123 Belp, Switzerland +41 79 173 36 84 info@ict.technology

    Terraform @ Scale - Teil 3a: Blast-Radius Management

    🔥 Ein einziger terraform destroy - und plötzlich sind 15 Kundensysteme offline 🔥

    Der "Freitagnachmittag-Destroyer" hat wieder zugeschlagen. 

    In diesem zweiteiligen Artikel durchleuchten wir eines der größten strukturellen Infrastrukturprobleme, aber auch unterschätzten Risiken von Infrastructure-as-Code aus der Management-Perspektive.  Denn wir helfen Unternehmen dabei, Blast-Radius-Risiken systematisch zu minimieren. 

    Denn die beste Explosion ist die, die gar nicht erst passiert.

    Die Anatomie einer Terraform-Katastrophe

    Szenario: Der Freitagnachmittag-Destroyer

    Es ist 16:30 Uhr am Freitag. Ein Entwickler möchte schnell eine Testumgebung aufräumen und führt terraform destroy aus. Was er nicht weiß: Die zu löschende VPC wird über Remote State von drei anderen States referenziert, die produktive Workloads für verschiedene Kunden verwalten.

    ⚠️ Das Ergebnis:

    • 15 Kundensysteme fallen gleichzeitig aus
    • Datenbank-Connections brechen ab
    • Load Balancer verlieren ihre Targets
    • Monitoring-Systeme melden Totalausfall
    • Das Wochenende ist ruiniert

    Dieses Szenario ist nicht konstruiert – es passiert in der Realität häufiger, als man denkt. Die Ursache liegt meist in einer Kombination aus:

    • Monolithische States: Zu viele Ressourcen in einem State führen zu unüberschaubaren Abhängigkeiten
    • Unklare Ownership: Niemand weiß genau, wer für welche Ressourcen verantwortlich ist
    • Fehlende Guardrails: Keine technischen Sicherungen gegen versehentliche Löschungen

    Infrastructure-as-Code verspricht Kontrolle, Reproduzierbarkeit und Effizienz. Doch wenn Terraform-States schlecht strukturiert sind, kann ein scheinbar harmloser terraform destroy oder terraform apply zur Katastrophe werden. Der sogenannte Blast Radius – der Umkreis ungewollter Auswirkungen – wird dann schnell größer als erwartet.

    Nach unserem letzten Artikel über das Goldilocks-Prinzip für optimale State-Größen widmen wir uns nun einem der kritischsten Aspekte beim Skalieren von Terraform: dem Management des Blast-Radius. Denn die Frage ist nicht, ob etwas schiefgeht, sondern wie viel davon betroffen ist, wenn es passiert.

    Was ist der Blast-Radius in Terraform?

    Der Begriff Blast Radius stammt ursprünglich aus der Sprengstofftechnik und beschreibt den Umkreis, in dem eine Explosion Schäden verursacht. In der Terraform-Welt bezeichnet er den Bereich der Infrastruktur, der von Änderungen, Fehlern oder Ausfällen betroffen sein kann.

    Die Verbindung zwischen einzelnen Ressourcen ist die Abhängigkeit der Ressourcen untereinander. Terraform erstellt intern dynamisch einen Dependency Graph, der nicht immer für den Engineer vollständig und intuitiv korrekt sichtbar ist.

    Manche Abhängigkeiten sind offensichtlich. Zum Beispiel benötigt ein virtueller Server auch ein Netzwerk, in welchem er platziert wird. Dieses Netzwerk wird in der Regel in der zum Server gehörenden Compute-Ressource durch den Engineer verdrahtet, diese Abhängigkeit ist also nicht nur logisch, sondern auch im Terraform-Modul-Code damit dokumentiert.

    Aber was ist mit indirekten und dynamisch erzeugten Abhängigkeiten, die sich erst durch die internen Berechnungen Terraforms ergeben? Zum Beispiel ist die IP-Adresse eines Webservice nicht dem virtuellen Server direkt zugewiesen, sondern einer IP-Adressen-Ressource, die wiederum in einer VNIC-Ressource verdrahtet ist, die vielleicht - oder vielleicht auch nicht - einem oder mehreren virtuellen Servern, oder einem Loadbalancer, oder vielleicht einer Firewall zugewiesen wurde. Da wird es jetzt schon schwieriger, spontan zu erkennen, wie sich die Änderung einer Netzmaske beim Subnetz auswirkt, aus dessen Pool von IP-Adressen die zugewiesene IP-Adresse stammt. 

    IT-Infrastrukturen werden, vorwiegend nach einer gehörigen Prise historischen Wachstums und Veränderungen, schnell komplexer als während der Designphase vorgesehen. Dann hängt man schnell davon ab, dass jeder Engineer und die Automatisierung in der Pipeline den von terraform plan erstellen und im Shell-Fenster vorbeirauschenden Ausführungsplan nicht nur tatsächlich liest, sondern ihn auch vollständig durchdenkt und auf Korrektheit überprüft - was aufgrund von mangelnder globaler Perspektive, Zeit und Motivation kaum ein menschlicher Operator macht, und die durchschnittliche CI/CD-Pipeline schon gar nicht.

    Blast-Radius Visualisierung:

    • Kleine States = wenige direkte und indirekte Abhängigkeiten = Kleiner Blast-Radius: Ein Fehler betrifft nur wenige Ressourcen

    • Große States = mehr direkte und indirekte Abhängigkeiten = Großer Blast-Radius: Ein Fehler kann weitreichende, unvorhersehbare Auswirkungen haben.

     

    In der Praxis manifestiert sich ein unkontrollierter Blast-Radius gerne auf verschiedene Weisen:

    • Ungewollte Löschungen: Ein terraform destroy entfernt nicht nur die gewünschten Ressourcen, sondern auch abhängige Komponenten in anderen Systemen. Das heißt, Sie schrauben an  einer Ecke in der Infrastruktur-Landschaft, und dann knallt es in einem ganz anderen, unerwarteten Bereich. Das passiert vorrangig in großen, monolithischen States.
    • Kaskadische Ausfälle: Eine Änderung an einer zentralen Ressource führt zu Ausfällen in scheinbar unabhängigen Services oder Ressourcen. Derartiges kann nicht nur wegen Abhängigkeiten passieren, sondern auch wegen Organisation von Ressourcen in falschen Datentypen, also Fehlern im Code. Ich hatte zum Beispiel einmal einen Kunden, bei dem etliche hundert DNS-Records als jeweils eigene Ressource mittels count anstelle von for_each in einer Liste gespeichert waren. Jetzt wurde im Rahmen eines terraform apply einer der ersten Einträge in dieser Liste gelöscht ... und mehrere hundert nachfolgende DNS-Einträge auch, weil deren Index sich änderte und Terraform in diesem Fall die Ressource eben löscht und neu anlegt. Das wurde aber erst  so richtig schlimm, weil der Anbieter der Public Cloud dann durch eng bemessene API-Limits auch dafür sorgte, dass die neuen DNS-Records dann nur sehr gemächlich und in Blöcken zu maximal 5 Stück neu angelegt wurden, anstelle schnell und in einem Rutsch. Damit waren dann fast alle unbeteiligten Services des Kunden für mehr als eine Stunde auf Tauchstation. Wie erklärt man einen solchen Vorfall dem Change Management und dem Pressesprecher? 
    • Cross-State-Abhängigkeiten: Remote State Referenzen führen zu unerwarteten Nebenwirkungen beim apply oder destroy. Das bedeutet, dass eine Terraform-Instanz mit Daten arbeitet, die aus einem anderen Statefile als dem eigenen stammen. "Ups, in dem Netz stand ja noch eine produktive Bare Metal eines anderen Kunden …"
    • Mandanten-Überschneidungen: Änderungen für einen Kunden betreffen versehentlich auch noch andere Mandanten. Denken Sie zum Beispiel an dynamisch verwaltete Firewallregeln in einer gesharten Umgebung.  
    • Das absolute Chaos: So richtig interessant wird es dann, wenn mehrere der hier genannten Szenarien gleichzeitig zutreffen. Wenn Sie Glück haben, ist dann der gesamte Standort noch redundant woanders aufgebaut.

    Typische Blast-Radius Szenarien

    1. Das Remote State Desaster


    # State A: Netzwerk-Foundation
    resource "aws_vpc" "main" {
    cidr_block = "10.0.0.0/16"
    }
    output "vpc_id" {
    value = aws_vpc.main.id
    }
    # State B: Application (referenziert State A)
    data "terraform_remote_state" "network" {
    backend = "s3"
    config = {
    bucket = "terraform-states"
    key = "network/terraform.tfstate"
    }
    }
    resource "aws_instance" "app" {
    [...]
    subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0] [...]
    }

    ⚠️ Das Problem: Wenn das VPC in State A zerstört wird, verliert State B seine Referenzen. Das führt zu inkonsistenten Zuständen und erfordert manuelles terraform import oder terraform state rm zur Reparatur. In vielen Unternehmen führt dies dann auch noch zusätzlich zu organisatorisch bedingten Problemen, denn Netzwerke und Serverinstanzen sind oft in unterschiedlichen Silos aufgehängt und die zeitnahe, konstruktive Kommunikation bei einem solchen Incident nicht immer sichergestellt.

    2. Das Multi-Tenant Chaos

    Ein besonders kritisches Szenario entsteht, wenn mehrere Mandanten (Kunden) Ressourcen in demselben State teilen:

    Beispiel: Shared Infrastructure State


    ├── shared-infrastructure/
    │   ├── customer-a-resources.tf
    │   ├── customer-b-resources.tf 
    │   ├── customer-c-resources.tf
    │   └── shared-services.tf

    Eine Änderung für Customer A kann ungewollt Customer B und C beeinträchtigen. Noch schlimmer: Ein terraform destroy könnte alle Kunden gleichzeitig betreffen.

    Deshalb ist es sehr wichtig, alle Mandanten vollständig voneinander zu isolieren. Ja, das kostet anfangs vielleicht mehr Geld. Ein Ausfall ist aber teurer.

    3. Die Dependency-Hölle

    Komplexe Abhängigkeitsketten zwischen Ressourcen können zu unvorhersehbaren Kaskade-Effekten führen:

    Abhängigkeitskette: 

    Eine Änderung am Anfang der Kette kann Auswirkungen bis zum Ende haben. Dies führt zu unerwarteten Seiteneffekten, vorwiegend wegen im Code unsichtbaren, von Terraform errechneten Abhängigkeiten.

    In der Verantwortung sind hier eigentlich die jeweiligen Terraform-Provider. Abhängigkeiten erfordern nämlich auch, dass der Provider nicht nur Eingabewerte ungeprüft und unkorreliert an die API des Cloudanbieters weiterreicht, sondern auch ein Mindestmaß an Logik und Verstand implementiert. Und glauben Sie mir: Viele Provider sind in dieser Hinsicht sehr, sehr schlecht. 

     

    Blast Radius Minimierung: Strategien und Best Practices

    Jetzt habe ich Ihnen vermutlich ausgiebig die Sorgenfalten auf die Stirn getrieben. Also lassen Sie uns jetzt darüber sprechen, wie man solche Probleme vermeidet.

    Ansatz 1: State-Segmentierung nach Blast-Radius

    Die effektivste Methode zur Blast-Radius-Kontrolle ist eine durchdachte State-Segmentierung. Dabei orientieren wir uns bei der Architektur einer Automatisierungslösung an folgenden Prinzipien:

    Isolation nach Impact-Level:

    • Critical States: Kerninfrastruktur mit hohem Blast-Radius (Netzwerk, IAM, DNS)
    • Service States: Anwendungsspezifische Ressourcen mit mittlerem Blast-Radius
    • Ephemeral States: Temporäre Ressourcen mit minimalem Blast-Radius

    # Beispiel einer blast-radius-optimierten Struktur in AWS
    ├── foundation/               # Kritische Infrastruktur
    │   ├── network-core/         # VPCs, Transit Gateways (hoher Blast Radius)
    │   ├── security-baseline/    # IAM, KMS Keys (hoher Blast Radius)
    │   └── dns-zones/            # Route53 Zones (mittlerer Blast Radius)
    │
    ├── platform/                 # Platform Services
    │   ├── kubernetes-cluster/   # EKS/OKE Cluster (mittlerer Blast Radius)
    │   ├── databases/            # RDS, DocumentDB (mittlerer Blast Radius)
    │   └── monitoring/           # CloudWatch, Grafana (niedriger Blast Radius)
    │
    └── applications/             # Anwendungslayer
        ├── frontend-dev/         # Development Environment (niedriger Blast Radius)
        ├── frontend-prod/        # Production Environment (mittlerer Blast Radius)
        └── batch-jobs/           # Batch Processing (niedriger Blast Radius)
    

    Wichtig ist, dass Sie dabei nach Möglichkeit auch die Rate of Change berücksichtigen. Das heißt, dass Sie vermeiden sollten, dass sich häufig ändernde States von anderen States referenziert werden, die sich nicht so häufig ändern dürfen. Wenn eine Ressource referenziert wird, die sich regelmäßig alle paar Wochen oder Monaten verändern könnte, sollte diese Ressource vielleicht besser keine kritische Rolle in der Infrastruktur übernehmen können und stattdessen in ein eigenes Ephemeral State ausgelagert werden.
    Hohe Änderungsraten und kritische Infrastruktur schließen einander aus - aber wo die Grenzen liegen, ist Ihre eigene Entscheidung, und diese Entscheidung sollten Sie frühestmöglich treffen und dokumentieren.
    Über die Rate of Change und wie man diese unter Kontrolle hält, sprechen wir noch separat in einem späteren Artikel dieser Serie.

    Ansatz 2: Dependency Inversion mit Remote State

    Statt direkte Abhängigkeiten zu schaffen, entschärfen wir die Situation mittels Data Sources und Lifecycle Preconditions. Lassen Sie es mich an einem Beispiel erklären.

    Das Problem: Direkte Abhängigkeiten (Anti-Pattern)


     # Anti-Pattern: Direkte Abhängigkeit
    resource "aws_instance" "app" {
      subnet_id = aws_subnet.main.id  
    }
    

    Schauen wir uns dies genauer an:

    • Die EC2-Instanz referenziert direkt eine Subnet-Ressource im gleichen State. Das bedeutet: Beide Ressourcen sind im selben terraform.tfstate File gekoppelt
    • Ein terraform destroy auf das Subnetz löscht beide Ressourcen gleichzeitig. Das kann auch bei einem terraform apply passieren, denn falls eine Änderung zu einem temporären Löschen und Neuaufbau des Subnetzes zwingt, wird die aws_instance ebenfalls vernichtet und neu ausgerollt.
    • Daraus folgt: Auch Änderungen am Subnetz können ungewollt die EC2-Instanz beeinflussen.

    Das ist gefährlich, weil:

    • Hoher Blast Radius: Eine Änderung am Netzwerk kann die Anwendung zerstören, im schlimmsten Fall auch die innerhalb der EC2-Instanz lokal gespeicherten Daten.
    • Gekoppelte Lebenszyklen: Subnetz und EC2-Instanz müssen immer zusammen verwaltet werden. Ein Change am Netz erfordert automatisch auch einen Change Request für die Applikation.
    • Konflikte von Verantwortungsbereichen: Netzwerk-Team und Application-Team blockieren sich im schlimmsten Fall gegenseitig.
    • Rollback-Probleme: Man kann die EC2-Instanz im Fehlerfall nicht mehr aus dem Backup wiederherstellen, weil die Netzwerkkonfiguration aus dem Backup dann nicht mehr garantiert zur aktuellen Situation passt.

     

    Eine Teillösung: Dependency Inversion (Best Practice)

    Gegen Änderungen (bzw. Mutationen) ist man noch weitgehend machtlos. Aber man kann sich durch Dependency Inversion gegen einen versehentlichen destroy schützen, indem man die EC2-Instanz von den Netzen entkoppelt.


    # App State (konsumiert Network Outputs)
    data "terraform_remote_state" "network" {
      backend = "s3"
      config = {
        bucket = "terraform-states"
        key    = "foundation/network/terraform.tfstate"
      }
    }
    

    Was ist jetzt anders?

    • Das VPC und das Subnetz liegen in einem separaten State (foundation/network/).
    • Die App referenziert das Subnetz nicht mehr direkt, sondern über die Remote State Data Source.
    • Deshalb gibt es jetzt keine direkte ReRessource-zu-Ressource-Abhängigkeiton der EC2-Instanz zu dem Subnetz mehr.

    Die App hängt nicht mehr direkt von der Subnet-Ressource ab, sondern von einem abstrakten Output des Netzwerk-States. Indirekt aber durchaus noch.

    Remote State Dependencies sind kein vollständiger Blast Radius Schutz. Sie sind eher ein "Circuit Breaker" der Ihnen Zeit gibt zu reagieren, aber letztlich müssen Abhängigkeiten  trotzdem aufgelöst werden.

    Deswegen ist das jetzt nur der erste Schritt, denn wenn sich die Netzwerk-Ressource im Remote State ändert, hat dies ja immer noch Auswirkungen auf unsere EC2-Instanz. Deshalb gehen wir jetzt noch einen Schritt weiter und implementieren eine Validierung und ziehen gegebenenfalls die Notbremse.

    Die Validierung und Defensive Programmierung

    In locals.tf definieren wir:


    locals {
      vpc_id = try(data.terraform_remote_state.network.outputs.vpc_id, null)
      subnet_ids = try(data.terraform_remote_state.network.outputs.subnet_ids, [])
    }

    Was passiert hier?

    Damit implementieren wir ein defensives Verhalten: Die Funktion try() verhindert Fehler, wenn das Remote State nicht existiert. Sollte dies der Fall sein, bekommt die vpc_id den Wert null, bzw. die subnet_ids eine leere Liste (ein VPC kann mehrere Subnetze enthalten, deshalb eine Liste).

    Jetzt gibt es mehrere mögliche Ansätze.

    Option 1: Detect-Only Pattern 

    Unsere EC2-Instanz deklarieren wir jetzt mit einem Lifecycle:


    resource "aws_instance" "app" {
    
    lifecycle { precondition { condition = can(data.terraform_remote_state.network.outputs.vpc_id) error_message = "WARNING: Network state not available - using fallback configuration" } }

    subnet_id = try( data.terraform_remote_state.network.outputs.private_subnet_ids[0], "subnet-fallback-12345" # Fallback auf bekannte, stabile Subnet-ID ) }

     Das Beispiel ist jetzt sehr simplifiziert, damit Sie das Grundprinzip verstehen können. In der Praxis lässt es sich noch verbessern und verfeinern, das bestreite ich nicht. Aber das Vorgehen bleibt ähnlich:

    • Ist im Remote State kein VPC mehr definiert (local.vpc_id ist dann null), bricht terraform plan mit der Warnung als Fehlermeldung ab.
    • Falls das VPC zwar existiert, aber die Subnet ID nicht, wird ein Fallback auf eine andere Subnet-ID durchgeführt. Diese kann hardcodiert sein (hässlich) oder aus einer anderen Quelle stammen - der Einfachheit Willen zeige ich hier ersteres. 

    Das würde in diesem konkreten Fall dann aber einen Umzug des Servers in ein anderes Subnetz bewirken, also auch eine destroy-Operation mit anschließendem Neuaufbau. Hilft uns nicht wirklich, jedenfalls nicht bei diesem Ressourcentyp. Es könnte sich daher anbieten, auch für die Subnet-ID eine Lifecycle Precondition zu definieren und einen Abbruch während der plan-Phase zu erzwingen. Oder, als Alternative, man verbietet Terraform einfach, die EC2-Instanz abzureißen, wie wir jetzt als Nächstes anschauen.

    Option 2: Prevent Destroy Pattern

    So verbietet man Terraform, eine Ressource zu zerstören und stattdessen mit einer Fehlermeldung abzubrechen:


    resource "aws_instance" "app" {
    
      lifecycle {
        prevent_destroy = true  # Verhindert versehentliche Löschung
        precondition {
          condition = can(data.terraform_remote_state.network.outputs.vpc_id)
          error_message = "WARNING: Network state not available - using fallback configuration"
        }
      }
    
      subnet_id = try(
        data.terraform_remote_state.network.outputs.private_subnet_ids[0],
        "subnet-fallback-12345"  # Fallback auf bekannte, stabile Subnet-ID
      )
    }

    Hier kam jetzt lediglich die Zeile prevent_destroy = true noch hinzu, der Rest des Beispiels ist zum vorherigen identisch.

    Es gibt auch noch eine dritte Variante, die sich mit dieser Lösung kombinieren lässt.

    Option 3: Explicit Confirmation Pattern

    Hier setzen Sie eine Boolean-Variable auf den Wert true, um den automatischen Abriss und Neuaufbau zu genehmigen. 

    Warnung: Dies schützt aber nicht vor einem Abriss der EC2-Instanz aufgrund Änderungen an den Netzwerken! Dieser Ausdruck in der for_each-Schleife schützt Sie nur vor sich selbst, indem Sie über var.confirm_network_dependency_removal explizit bestätigen, dass Sie sich dieses Risikos bewusst sind. Verfolgen Sie diesen Ansatz daher nur, falls eine stabile CI/CD-Pipeline für Sie wichtiger ist als die Existenz der Serverinstanz, zum Beispiel in Dev-Umgebungen oder bei rigoros durchgesetzter Immutability.


    variable "confirm_network_dependency_removal" {
      type        = bool
      default     = false
    }
    
    data "terraform_remote_state" "network" {
      backend = "s3"
      config = {
        bucket = "terraform-states"
        key    = "foundation/network/terraform.tfstate"
        region = "eu-central-1"
      }
    }
    
    locals {
      vpc_ok = can(data.terraform_remote_state.network.outputs.vpc_id)
      subnet_ok = can(data.terraform_remote_state.network.outputs.private_subnet_ids[0])
    
      deploy_app = (
        local.vpc_ok || var.confirm_network_dependency_removal
      ) && local.subnet_ok
    
      app_instances = local.deploy_app ? { "main" = true } : {}
    }
    
    resource "aws_instance" "app" {
      for_each = local.app_instances
    
      ami           = "ami-12345678"
      instance_type = "t3.micro"
    
      subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0]
    
      lifecycle {
        prevent_destroy = true
        precondition {
          condition     = local.vpc_ok
          error_message = "VPC missing – deployment denied"
        }
        precondition {
          condition     = local.subnet_ok
          error_message = "Subnet missing – deployment denied"
        }
      }
    
      tags = {
        Name = "BlastRadiusProtected"
      }
    }
    

     

    Ansatz 3: Blast-Radius Guardrails mit Lifecycle Rules

    Ich habe es in den Ausführungen zu Ansatz 2 bereits angedeutet: Terraform bietet mehrere Mechanismen, um versehentliche Löschungen zu verhindern. 

    Option 1: prevent_destroy für kritische Ressourcen

    Wir haben es weiter oben bereits erklärt, lassen Sie es uns aber noch einmal ins Gedächtnis rufen:


    # Prevent Destroy für kritische Ressourcen
    resource "aws_vpc" "main" {
      cidr_block = "10.0.0.0/16"
      lifecycle {
        prevent_destroy = true
      }
    tags = { Name = "Production-VPC" Environment = "production" BlastRadius = "high" }
    }

     

    Beachten Sie, was genau wir hier tun: Wir schützen abhängige Ressourcen wie das Subnetz und die EC2-Instanz, indem wir das VPC zur kritischen Ressourcen erklären und vor dem Löschen schützen. Dies ist natürlich nur dann möglich, wenn das VPC auch von uns verwaltet wird und nicht aus einem Remote State stammt. 

    Option 2: create_before_destroy for stateful-Ressourcen

    Sobald ein Provider festlegt, dass es nicht möglich, die Argumente einer Ressource im laufenden Betrieb zu updaten, muss diese zerstört und neu gebaut werden. Da viele Ressourcen in einer Infrastruktur nur einmal existieren dürfen (wie zum Beispiel IP-Adressen), wird eine Ressource von Terraform erst zerstört und dann neu gebaut.  

    Es ist aber möglich, das Zerstören einer Ressource unter der Bedingung zu erlauben, dass zuerst die neue Ressource bereitgestellt werden muss, bevor die alte Ressource abgebaut wird:


    resource "aws_db_instance" "main" {
    
      lifecycle {
        create_before_destroy = true
        ignore_changes = [
          password,  # Ignore password changes to prevent unnecessary replacements
        ]
      }

    [...]
    }

    Hier sind dann allerdings Sie selbst in der Verantwortung, zu gewährleisten, dass es nicht zu Konflikten kommt. Die alte Ressource und die neue Ressource müssen nebeneinander existieren können; im Falle einer IP-Adresse, die im gleichen Netz ja ein Unikat sein muss, wäre es dann also sinnvoll, diese neue Ressource ohne die IP-Adresse zu erzeugen und erst danach die IP-Adresse von der alten Ressourcen zur neuen Ressource umzuziehen. Das erhöht also mitunter die Komplexität und kann nur selektiv eingesetzt werden.

    Option 3: Conditional Destroy mit Preconditions

    Die dritte Option ist hier, wieder eine Lifecycle Precondition einzusetzen und damit ein Flag abzufragen, welches regelt, ob eine Ressource gelöscht werden darf oder stattdessen während terraform plan eine Fehlermeldung ausgegeben und abgebrochen wird:


    variable "confirmed_destroy" {
      type        = bool
      default     = false
      description = "Explicitely allows destruction of resources if set to `true`."
    }
    
    resource "aws_instance" "app" {
    
      lifecycle {
        precondition {
          condition = var.environment != "production" || var.confirmed_destroy == true
          error_message = "Production resources require explicit confirmation for destruction."
        }
      }

    [...]
    }

     

    Ansatz 4: Professioneller Ansatz mittels Policy-as-Code (Terraform Enterprise)

    Die bisherigen Lösungsansätze haben eine Gemeinsamkeit: Sie sind irgendwie alles nur halbgare Basteleien und erfordern Kompromisse. Wenn man eine professionelle Lösung sucht, bleibt der Griff zu Terraform Enterprise.  Für Enterprise-Umgebungen bietet Terraform Enterprise mit Sentinel tiefgehende Policy-as-Code Funktionalitäten:


    # Sentinel Policy: Blast Radius Kontrolle
    import "tfplan/v2" as tfplan
    
    # Verhindere Löschung von Ressourcen mit hohem Blast Radius
    high_blast_radius_resources = [
        "aws_vpc",
        "aws_route53_zone", 
        "aws_iam_role"
    ]
    
    main = rule {
        all tfplan.resource_changes as _, resource {
            resource.type not in high_blast_radius_resources or
            resource.change.actions not contains "delete"
        }
    }

    In diesem Beispiel werden im ersten Schritt AWS VPCs, IAM-Rollen und Route 53 DNS-Zonen zu kritischer Infrastruktur mit hohem BlBlast-Radiusrklärt.

    Im zweiten Schritt werden dann alle Versuche, Ressourcen mit einem dieser Ressourcentypen zu löschen, durch eine entsprechende Regel abgefangen. Sollte diese Regel getriggert werden, fängt Terraform dann einen terraform apply direkt nach der plan-Phase ab und verweigert die weitere Ausführung.

    Fazit (bisher)

    Der Blast Radius bei Terraform sollte unbedingt frühzeitig beachtet werden, da man ansonsten einem großen Risiko ausgesetzt ist. Die Gefahr lässt sich mit der kostenlosen Version von Terraform durch einige Tricks und Bastelarbeiten bei der Implementierung der Module etwas mindern, aber nicht zuverlässig verhindern.  

    Wenn Sie eine Lösung auf professionellem Niveau suchen, die sich auch unternehmensweit erzwingen und auditieren lässt, sollten Sie Terraform Enterprise mit Sentinel-Modul in Betracht ziehen. Falls ihr Risikomanagement das Risiko und die Kosten eines Ausfalls beziffern kann, stellt eine entsprechende Lizenz für Terraform Enterprise eine Versicherung dar, deren Kosten und Nutzen sich miteinander vergleichen lassen. 

     
    Ausblick

    Wir haben jetzt sehr ausführlich darüber gesprochen, was wir unternehmen können, um das Risiko eines zu großen Blast Radius zu reduzieren.

    Aber was tun wir, wenn es dennoch passiert? Welche Möglichkeiten haben wir, den dann entstehenden Schaden zu mildern und die Infrastruktur wieder in einen betriebsfähigen Zustand zu bringen?

    Das schauen wir uns im nächsten Teil dieser Artikelserie an.