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

    Terraform @ Scale - Teil 7: Best Practices bei der Modulversionierung

    "Versionierung? Ach, ich nehme einfach immer die neueste Version - was soll schon schiefgehen?"

    Genau diese Einstellung ist einer der Gründe, warum manche Engineers um 3 Uhr nachts aus dem Bett geklingelt werden. Und warum am nächsten Morgen im Daily Stand-up betreten auf die Schuhe gestarrt wird.

    Lassen Sie uns im letzten Teil unserer Serie "Terraform @ Scale" kurz darüber reden, bevor es auch Ihnen passiert.

    Nachdem wir in den vorherigen Teilen dieser Serie über Abhängigkeiten, Blast-Radius und Testing gesprochen haben, widmen wir uns heute einem Thema, das oft unterschätzt wird: der professionellen Versionierung von Terraform-Modulen. Denn ein Modul ohne saubere Versionierung ist wie ein Auto ohne Jahresinspektion: Es mag eine Weile funktionieren, aber früher oder später gibt's richtig Ärger.

    Warum Modul-Versionierung existenziell ist

    Stellen Sie sich vor: Ein zentrales Netzwerk-Modul wird von 42 verschiedenen Projekten verwendet. Der ursprüngliche Entwickler hat das Team verlassen, seine Dokumentation bestand aus "siehe Code", und jetzt muss das Modul dringend angepasst werden. Ohne klare Versionierung wissen Sie nicht:

    • Welche Projekte welche Version des Moduls verwenden
    • Ob ein Update Breaking Changes für bestehende Deployments bedeutet
    • Wie Sie ein Rollback durchführen können, wenn etwas schiefgeht
    • Wann das Modul überhaupt das letzte Mal geändert wurde und warum

    Das Ergebnis? Die berühmte 3-Uhr-Eskalation, weil niemand mehr weiß, welche Abhängigkeiten wo bestehen. Und dann beginnt das große Rätselraten, während die Infrastruktur brennt.

    Professionelle Modul-Versionierung ist kein "Nice-to-Have" für Perfektionisten. Es ist die Grundlage dafür, dass Ihre Infrastructure-as-Code auch bei 100+ Projekten noch wartbar bleibt, ohne dass Sie ein archäologisches Studium benötigen, um herauszufinden, was wann warum gemacht wurde.

    Semantic Versioning - Die Lingua Franca der Versionierung

    Semantic Versioning (SemVer) ist der De-facto-Standard für Software-Versionierung und damit auch für Terraform-Module die einzig sinnvolle Wahl. Das Schema ist simpel: MAJOR.MINOR.PATCH (z.B. v1.3.7).

    Semantic Versioning Explained

    Die drei Komponenten im Detail

    MAJOR Version (1.x.x) - Die große Keule:

    Wird erhöht bei Breaking Changes. Das sind Änderungen, die bestehenden Code zwingend kaputt machen. Beispiele:

    • Eine required Variable wird umbenannt oder entfernt
    • Ein Output wird gelöscht, auf den andere Module angewiesen sind
    • Die Resource-Struktur ändert sich so, dass ein terraform state mv nötig wird
    • Provider-Anforderungen ändern sich inkompatibel (z.B. von AWS Provider 4.x auf 5.x)

    Faustregel: Wenn Nutzer des Moduls ihre Terraform-Konfiguration anpassen MÜSSEN, ist es ein Major-Update.

    MINOR Version (x.1.x) - Die freundliche Erweiterung:

    Wird erhöht bei neuen Features, die rückwärtskompatibel sind. Beispiele:

    • Neue optionale Variablen mit sinnvollen Defaults
    • Zusätzliche Outputs, die niemand bisher vermisst hat
    • Neue optionale Sub-Module oder Ressourcen
    • Performance-Verbesserungen ohne funktionale Änderungen

    Faustregel: Bestehender Code funktioniert weiterhin, aber es gibt neue Möglichkeiten. Ein Update ist optional, aber empfohlen.

    PATCH Version (x.x.1) - Der Bugfix:

    Wird erhöht bei Bugfixes, die nichts Funktionales ändern. Beispiele:

    • Korrektur von Tippfehlern in Outputs oder Variablen-Descriptions
    • Fixes von race conditions oder timing issues
    • Dokumentations-Updates
    • Korrektur von Default-Werten, die offensichtlich falsch waren

    Faustregel: Reine Fehlerbereinigung. Updates sollten immer sicher sein.

    Pre-Release und Build-Metadata

    SemVer erlaubt zusätzliche Kennzeichnungen für Beta-Versionen oder Build-Informationen:


    v1.2.3-beta.1       # Beta-Version 
    v1.2.3-rc.2         # Release Candidate
    v1.2.3+build.42     # Build-Metadata (will be ignored during version comparisons)
    v2.0.0-alpha.1      # Alpha-Version of a major updates

    Diese Pre-Release-Versionen sind für Development und Testing gedacht. In produktiven Umgebungen haben sie nichts zu suchen - auch wenn manche Teams das anders sehen und dann entsprechend leiden.

    Module Upgrade-Szenarien und ihre Tücken

    Die Theorie ist schön und gut. Die Praxis ist, wie üblich, komplizierter. Schauen wir uns an, was passiert, wenn Sie ein Modul von Version v1.0.0 auf verschiedene neuere Versionen upgraden wollen.

    Module Upgrade Scenarios

    Das sanfte Patch-Update (v1.0.0 → v1.0.1)

    Das sollte die einfachste aller Übungen sein. Ein Bugfix, keine Breaking Changes, nichts Dramatisches. In der Theorie führen Sie einfach ein terraform init -upgrade aus und fertig.

    In der Praxis stolpern Sie möglicherweise über:

    • Provider-Lock-Files: Ihr .terraform.lock.hcl enthält möglicherweise gelockte Hashes, die mit dem Bugfix inkompatibel sind
    • State-Drift: Der Bugfix korrigiert einen Fehler, der bereits im State manifestiert ist - jetzt will Terraform Ressourcen ändern
    • Downstream-Dependencies: Andere Module, die Ihr Modul nutzen, haben ihre eigenen Lock-Files

    Best Practice: Selbst bei Patch-Updates sollten Sie einen Plan-Run in einer Test-Umgebung durchführen. Nicht aus Paranoia, sondern aus Erfahrung.

    Das freundliche Minor-Update (v1.0.0 → v1.1.0)

    Neue Features, rückwärtskompatibel. Hört sich gut an. Aber "rückwärtskompatibel" bedeutet nicht "ohne Auswirkungen". Neue optionale Variablen haben Defaults, und diese Defaults werden angewendet, ob Sie das wollen oder nicht.

    Beispiel-Szenario:


    # v1.0.0 - Original Version
    resource "oci_core_vcn" "this" {
      compartment_id = var.compartment_id
      cidr_block     = var.cidr_block
      display_name   = var.display_name
    }
    
    # v1.1.0 - New optional Features
    resource "oci_core_vcn" "this" {
      compartment_id = var.compartment_id
      cidr_block     = var.cidr_block
      display_name   = var.display_name
      
      # NEW: Optional DNS Configuration
      dns_label = var.dns_label # Default: null
      
      # NEW: Optional IPv6 Support  
      is_ipv6enabled = var.enable_ipv6 # Default: false
    }

    Was passiert beim Update? Terraform sieht neue Argumente in der Ressource. Auch wenn die Defaults "null" oder "false" sind, muss Terraform evaluieren, ob sich etwas ändert. Je nach Provider und Ressource kann das zu einem In-Place-Update oder gar einem Replacement führen.

    Provider-Besonderheit: Bei einigen Providern kann ein neuer "false"-Default trotzdem eine Planänderung erzeugen, wenn der Provider den "unset"-Zustand anders behandelt als explizit "false". Dies ist besonders bei Boolean-Attributen relevant, die nachträglich zu bestehenden Ressourcen hinzugefügt wurden.

    Best Practice: Minor-Updates in DEV-Umgebungen ausgiebig testen. Prüfen Sie den Plan-Output auf unerwartete Änderungen. Und ja, das bedeutet tatsächlich, den Plan zu LESEN, nicht nur zu überfliegen.

    Das gefürchtete Major-Update (v1.5.0 → v2.0.0)

    Hier wird es ernst. Breaking Changes bedeuten: Ihr bestehender Code WIRD brechen. Die Frage ist nur, wo genau und wie schlimm.

    Typische Szenarien bei Major-Updates:

    • Variable wurde umbenannt: subnet_ids heißt jetzt subnet_id_list
    • Output-Struktur hat sich geändert: Was früher eine Liste war, ist jetzt ein Map
    • Ressourcen-Adressen haben sich verschoben: State-Migration erforderlich
    • Provider-Requirements sind gestiegen: OCI Provider 5.x → 6.x

    Das Symbol mit dem roten Fragezeichen in der Grafik oben ist kein Zufall. Bei Major-Updates kann es passieren, dass ein direktes Upgrade von v1.5.0 auf v2.0.0 schlichtweg nicht funktioniert. Der Grund: Zu viele Breaking Changes auf einmal.

    Best Practice: Lesen Sie die Release Notes. Alle. Vollständig. Erstellen Sie eine Migrations-Checkliste. Testen Sie in isolierten Umgebungen. Planen Sie Rollback-Strategien. Und erwarten Sie das Unerwartete.

    Version Pinning - Die Kunst des kontrollierten Chaos

    Jetzt wird's praktisch. Wie pinnen Sie Module auf spezifische Versionen? Terraform bietet mehrere Mechanismen, die je nach Quelle des Moduls variieren.

    Git-basierte Module mit ref-Parameter

    Für Module in Git-Repositories ist der ?ref=-Parameter der Standardweg:


    # Via Git-Tag (minimum for production environments)
    module "vpc" {
      source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=v1.2.3"
      
      compartment_id = var.compartment_id
      cidr_block     = "10.0.0.0/16"
    }
    
    # Via Branch (only fpr development!)
    module "vpc_dev" {
      source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=develop"
      
      compartment_id = var.dev_compartment_id
      cidr_block     = "172.16.0.0/16"
    }
    
    # Via Commit Hash (for maximum security, recommended)
    module "vpc_immutable" {
      source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=a1b2c3d4"
      
      compartment_id = var.compartment_id
      cidr_block     = "192.168.0.0/16"
    }

    Wichtig: Das ?ref= kommt NACH dem Pfad zum Modul (//vpc), nicht davor. Dieser Fehler wird überraschend oft gemacht und führt zu kryptischen Fehlermeldungen.

    Hinweis zu Commit-Hashes: Bei Verwendung von Commit-Hashes sollten Sie den vollständigen Hash verwenden. Kurze 7-Zeichen-Hashes können je nach Git-Server-Konfiguration nicht auflösbar sein und zu Fehlern beim terraform init führen.

    Terraform Registry mit Version Constraints

    Für Module aus Public oder Private Terraform Registries nutzen Sie den version-Parameter:


    # Exact Version (maximum pinning)
    module "vpc" {
      source  = "oracle-terraform-modules/vcn/oci"
      version = "3.5.4"
      
      compartment_id = var.compartment_id
      vcn_cidr       = "10.0.0.0/16"
    }
    
    # Pessimistic Constraint Operator (recommended)
    module "vpc_safe" {
      source  = "oracle-terraform-modules/vcn/oci"
      version = "~> 3.5.0"  # Allows 3.5.x, but not 3.6.0
      
      compartment_id = var.compartment_id
      vcn_cidr       = "10.0.0.0/16"
    }
    
    # Range Constraints (allows more flexibility)
    module "vpc_range" {
      source  = "oracle-terraform-modules/vcn/oci"
      version = ">= 3.5.0, < 4.0.0"  # All 3.x Versions, starting with 3.5.0
      
      compartment_id = var.compartment_id
      vcn_cidr       = "10.0.0.0/16"
    }

    Version Constraint Operators - Das Kleingedruckte

    Terraform unterstützt verschiedene Operatoren für Version Constraints:

    • = oder keine Angabe: Exakte Version (z.B. = 1.2.3 oder 1.2.3)
    • !=: Alle Versionen außer dieser (z.B. != 1.2.3)
    • >, >=, <, <=: Vergleichsoperatoren (z.B. >= 1.2.0)
    • ~>: Pessimistic Constraint - erlaubt nur Inkremente der rechtesten angegebenen Versionskomponente (z.B. ~> 1.2.0 erlaubt >= 1.2.0 und < 1.3.0)

    Der ~>-Operator verdient besondere Aufmerksamkeit. Er erlaubt nur Inkremente der rechtesten angegebenen Komponente:


    ~> 1.2     # Allows >= 1.2.0 and < 2.0.0 (all minor- and patch versions in Major 1)
    ~> 1.2.0   # Allows >= 1.2.0 and < 1.3.0 (all patches in minor 1.2)
    ~> 1.2.3   # Allows >= 1.2.3 und < 1.3.0 (all patches starting with 1.2.3 in minor 1.2)

    Für Produktiv-Umgebungen empfehlen wir: Exakte Versionen oder ~> mit Patch-Spezifikation (z.B. ~> 3.5.0), um nur Patch-Releases innerhalb eines Minor zuzulassen. Wenn Sie Minor-Upgrades ebenfalls automatisch zulassen möchten, verwenden Sie ~> 3.5 - das erlaubt aber alle Versionen bis < 4.0.0. Alles andere führt früher oder später zu unerwarteten Überraschungen.

    Version Pinning in großen Umgebungen - Die harte Realität

    In kleinen Setups können Sie jedes Modul manuell auf eine spezifische Version pinnen. Aber das resultiert in Mikromanagement und skaliert nicht.

    Bei 5 Projekten ist das noch überschaubar.

    Bei 50 Projekten wird es mühsam.

    Bei 500 Projekten ist es unmöglich.

    Das Git-URL-Pinning-Minimum

    Bei Terraform Open Source bleibt Ihnen für Git-basierte Module nur der ?ref=-Parameter. Das ist die absolute Minimallösung. Sie funktioniert, aber Sie müssen manuell:

    • Jeden Modul-Aufruf mit einem ?ref= versehen
    • Bei Updates alle betroffenen Stellen im Code finden und ändern
    • Hoffen, dass niemand vergisst, das ?ref= hinzuzufügen

    Für ernsthafte Umgebungen ist das nicht nachhaltig. Es ist wie mit einem Hammer Schrauben in die Wand zus chlagen - technisch ist es zwar möglich, in absoluten Ausnahmen vielleicht auch tolerierbar, die generelle Anwendung wäre jedoch idiotisch. Aber einer gewissen Größenordnung der IaC-Codebasis braucht es professionellere Lösungen.

    Die Registry-Lösung mit Version Constraints

    Besser, aber auch nicht perfekt: Eine Private Terraform Registry mit Version Constraints. Sie können gezielt steuern, welche Versionen erlaubt sind:


    # In versions.tf
    terraform {
      required_version = ">= 1.10.0"
      
      required_providers {
        oci = {
          source  = "oracle/oci"
          version = "~> 6.0"
        }
      }
    }
    
    # In main.tf
    module "vcn" {
      source  = "your-company.com/modules/oci-vcn"
      version = "~> 2.1.0"  # Allows Patches, but not minor Upgrades
      
      compartment_id = var.compartment_id
      cidr_block     = "10.0.0.0/16"
    }

    Das Problem: Sie müssen trotzdem durch alle Repositories gehen und die version-Constraints aktualisieren, wenn Sie ein Breaking-Change-Update durchführen wollen. Bei 100+ Repositories ist das ein Vollzeitjob.

    Version Constraints für Module - Der Enterprise-Ansatz

    In Terraform Enterprise oder Terraform Cloud können Sie auf Workspace-Ebene oder sogar global Version Constraints für Module definieren. Das ist die professionelle Lösung für große Umgebungen:


    # Sentinel policy for module versioning
    import "tfconfig/v2" as tfconfig
    
    # Define allowed versions per module
    allowed_module_versions = {
      "oracle-terraform-modules/vcn/oci": {
        "min_version": "3.5.0",
        "max_version": "3.9.9",
      },
      "your-company.com/modules/oci-compute": {
        "min_version": "2.0.0",
        "max_version": "2.9.9",
      },
    }
    
    # Verify all modul calles
    import "versions"
    
    mandatory_version_constraint = rule {
      all tfconfig.module_calls as name, module {
    
        # Only for registry sources which we want to manage explicitely
        has_key(allowed_module_versions, module.source) implies (
          module.version is not null and
          func() {
            v = allowed_module_versions[module.source]
            c = versions.constraint(">= " + v.min_version + ", <= " + v.max_version)
            versions.matches(c, module.version)
          }()
        )
      }
    }
    

    Mit Sentinel können Sie zentral steuern, welche Modul-Versionen in welchen Workspaces erlaubt sind. Ein Update auf ein neues Major-Release wird dann über eine Sentinel-Policy-Änderung ausgerollt, nicht durch Änderungen in jedem einzelnen Repository.

    Das ist Enterprise-Grade Version Management. Kostet entsprechend Geld und Setup-Aufwand, aber skaliert tatsächlich auf Hunderte von Projekten.

    Testing von Modul-Upgrades mit Terraform Testing Framework

    Sie haben ein Modul aktualisiert. Jetzt müssen Sie testen, ob alles noch funktioniert. Manuelles Testen ist für Amateure. Profis automatisieren.

    Das Terraform Testing Framework (ab Terraform 1.6, massiv verbessert in 1.10+) bietet dafür genau die richtigen Werkzeuge. Hier ein praxisnahes Beispiel für Module-Upgrade-Tests:

    Zusätzlicher Vorteil: Das Testing Framework kann auch als Guardrail gegen zu große Batches fungieren - durch Tests, die die Anzahl geplanter Ressourcen-Änderungen limitieren. Dies entschärft indirekt API-Limit-Probleme, die wir in früheren Teilen dieser Serie ausführlich behandelt haben.

    Test-Szenario: VCN-Modul-Upgrade von v2.5.0 auf v3.0.0


    # tests/upgrade_v2_to_v3.tftest.hcl
    variables {
      compartment_id = "ocid1.compartment.oc1..example"
      vcn_cidr       = "10.0.0.0/16"
      vcn_name       = "test-upgrade-vcn"
    }
    
    # Test 1: Existing v2.5.0 funktionality
    run "test_v2_baseline" {
      command = plan
      
      module {
        source  = "oracle-terraform-modules/vcn/oci"
        version = "2.5.0"
      }
      
      assert {
        condition     = length(output.vcn_id.value) > 0
        error_message = "VCN ID should be generated in v2.5.0"
      }
      
      assert {
        condition     = output.vcn_cidr_block.value == var.vcn_cidr
        error_message = "CIDR block mismatch in v2.5.0"
      }
    }
    
    # Test 2: v3.0.0 breaking changes
    run "test_v3_migration" {
      command = plan
      
      module {
        source  = "oracle-terraform-modules/vcn/oci"
        version = "3.0.0"
      }
      
      # v3.0.0 renamed 'vcn_name' to 'display_name'
      variables {
        display_name = var.vcn_name  # Neuer Parameter-Name
      }
      
      assert {
        condition     = length(output.vcn_id.value) > 0
        error_message = "VCN ID should still be generated in v3.0.0"
      }
      
      assert {
        condition     = output.vcn_display_name.value == var.vcn_name
        error_message = "Display name not correctly migrated to v3.0.0"
      }
    }
    
    # Test 3: Backwards Compatibility Check
    run "test_output_compatibility" {
      command = plan
      
      module {
        source  = "oracle-terraform-modules/vcn/oci"
        version = "3.0.0"
      }
      
      variables {
        display_name = var.vcn_name
      }
      
      # Check if critical outputs still exist
      assert {
        condition = (
          can(output.vcn_id.value) &&
          can(output.vcn_cidr_block.value) &&
          can(output.default_route_table_id.value)
        )
        error_message = "Critical outputs missing in v3.0.0 - breaking downstream dependencies!"
      }
    }

    Diese Tests laufen mit terraform test und brechen ab, wenn ein Upgrade Breaking Changes hat, die nicht dokumentiert oder nicht behandelt wurden.

    Integration mit CI/CD

    Tests sind nur so gut wie ihre Automatisierung. Hier ein GitLab CI/CD-Beispiel:


    # .gitlab-ci.yml
    stages:
      - test
      - plan
      - apply
    
    terraform_test:
      stage: test
      image: hashicorp/terraform:1.10
      script:
        - terraform init
        - terraform test -verbose
      only:
        - merge_requests
        - main
    
    terraform_plan:
      stage: plan
      image: hashicorp/terraform:1.10
      script:
        - terraform init
        - terraform plan -out=tfplan
      dependencies:
        - terraform_test
      only:
        - main
      artifacts:
        paths:
          - tfplan
    
    terraform_apply:
      stage: apply
      image: hashicorp/terraform:1.10
      script:
        - terraform init
        - terraform apply tfplan
      dependencies:
        - terraform_plan
      only:
        - main
      when: manual

    Ohne erfolgreiche Tests kein Plan. Ohne erfolgreichen Plan kein Apply. Das ist die Pipeline, die Sie wollen.

    Ergänzender Tipp: Zusätzlich zu den Tests sollten Sie automatisierte Plan-Scans auf destruktive Änderungen (delete/destroy) implementieren, wie wir sie in den vorherigen Teilen dieser Serie bereits behandelt haben. Diese Frühwarnsysteme ergänzen die Testing-Guardrails und fangen potenzielle Blast-Radius-Probleme ab, bevor sie Production erreichen.

    Sentinel Policies für Version Enforcement

    Tests sind gut. Policies sind besser. Denn Tests kann man umgehen, wenn man wirklich will (oder zu faul ist). Sentinel Policies in Terraform Enterprise kann man nicht umgehen - außer man ist Administrator, und dann ist es ein ganz anderes Problem.

    Policy: Erzwinge Modul-Versionierung


    import "tfconfig/v2" as tfconfig
    import "strings"
    
    # Helper function: is the source a VCS (git)?
    is_vcs = func(src) {
      strings.has_prefix(src, "git::") or strings.contains(src, "://")
    }
    
    # Helper function: Extract ref parameter from URL
    ref_of = func(src) {
      idx = strings.index_of(src, "?ref=")
      idx >= 0 ? strings.substring(src, idx+5, -1) : ""
    }
    
    # Policy: All non-local modules MUST be versioned
    mandatory_versioning = rule {
      all tfconfig.module_calls as name, module {
        # Local modules (./ oder ../) are exempt
        not (strings.has_prefix(module.source, "./") or 
             strings.has_prefix(module.source, "../")) implies (
          # For VCS sources: ?ref= must exist
          is_vcs(module.source) implies length(ref_of(module.source)) > 0
          and
          # For registry sources: version must be set
          not is_vcs(module.source) implies module.version is not null
        )
      }
    }
    
    # Main policy
    main = rule {
      mandatory_versioning
    }

    Diese Policy lehnt jeden terraform plan oder apply ab, bei dem ein Modul nicht ordentlich versioniert ist. Keine Ausnahmen. Keine Gnade.

    Policy: Verbiete bekannte fehlerhafte Versionen


    import "tfconfig/v2" as tfconfig
    
    # Deny-list for modules with known bugs
    forbidden_module_versions = {
      "oracle-terraform-modules/vcn/oci": ["3.2.0", "3.2.1"],  # Bug in DHCP-Options
      "your-company.com/modules/compute": ["1.5.0"],          # Security Issue
    }
    
    # Policy: Disallow known faulty versions
    deny_forbidden_versions = rule {
      all tfconfig.module_calls as name, module {
        has_key(forbidden_module_versions, module.source) implies (
          module.version not in forbidden_module_versions[module.source]
        )
      }
    }
    
    
    main = rule {
      deny_forbidden_versions
    }

    Jetzt haben Sie einen Mechanismus, um kritische Modul-Versionen global zu sperren. Wenn Version 3.2.0 des VCN-Moduls einen schweren Bug hat, sperren Sie sie in der Policy. Fertig. Niemand kann sie mehr verwenden, auch nicht "versehentlich".

    Praktische Workflows für Modul-Updates

    Theorie ist schön. Praxis ist besser. Hier sind bewährte Workflows für verschiedene Update-Szenarien.

    Workflow 1: Patch-Update (z.B. v2.3.1 → v2.3.2)

    1. Kommunikation: Ankündigung im Team-Chat, auch wenn es "nur" ein Patch ist
    2. Test in DEV: Update in Development-Umgebung, terraform init -upgrade, Plan prüfen
    3. Automatisierte Tests: terraform test durchlaufen lassen
    4. Staging-Deployment: Rollout in Staging, Monitoring für 24h
    5. Production-Rollout: Rollout in Produktion während Geschäftszeiten (nicht Freitag 16:00)
    6. Dokumentation: Update in CHANGELOG und Confluence/Wiki dokumentieren

    Dauer: 1-3 Tage, je nach Größe der Umgebung

    Hinweis: Die angegebenen Dauerangaben sind Erfahrungswerte und können je nach Compliance-Anforderungen (SOX, ISO, DSGVO) und Change-Management-Prozessen deutlich variieren.

    Workflow 2: Minor-Update (z.B. v2.3.2 → v2.4.0)

    1. Release Notes Review: Vollständige Durchsicht aller neuen Features und Changes
    2. Impact-Analyse: Welche neuen Defaults werden aktiv? Gibt es unerwartete Seiteneffekte?
    3. Test-Plan erstellen: Dokumentierte Test-Szenarien für neue Features
    4. DEV-Testing: Ausgiebiges Testing in Development (mindestens 1 Woche)
    5. Canary-Deployment: Rollout in einer kleinen Production-Subset
    6. Monitoring-Phase: 1 Woche Monitoring der Canary-Deployment
    7. Full Rollout: Schrittweiser Rollout über mehrere Tage/Wochen
    8. Postmortem-Review: Lessons Learned dokumentieren

    Dauer: 2-4 Wochen, je nach Risiko-Bewertung

    Workflow 3: Major-Update (z.B. v2.4.0 → v3.0.0)

    1. Breaking Changes Inventar: Vollständige Liste aller Breaking Changes erstellen
    2. Migrations-Guide: Schritt-für-Schritt Anleitung für jede Breaking Change
    3. Dependency-Analyse: Welche Downstream-Module sind betroffen?
    4. Test-Umgebung aufsetzen: Dedizierte Umgebung für Migration-Testing
    5. State-Migration testen: Terraform State Surgery falls nötig
    6. Rollback-Strategie: Dokumentierter Plan für den Worst Case
    7. DEV-Migration: Vollständige Migration in Development mit Tests
    8. Staging-Migration: Migration in Staging mit 2 Wochen Monitoring
    9. Go/No-Go Meeting: Formale Entscheidung basierend auf Test-Ergebnissen
    10. Production-Migration: Geplantes Wartungsfenster, alle Hände an Deck
    11. Post-Migration Monitoring: Intensive Überwachung für 2 Wochen
    12. Documentation Update: Alle Docs auf neue Version aktualisieren

    Dauer: 1-3 Monate, abhängig von Komplexität und Größe

    Checkliste: Professionelle Modul-Versionierung

    ✅ Grundlagen

    [ ] Semantic Versioning wird konsequent für alle Module eingesetzt
    [ ] Git-Tags werden für jede Version erstellt (nicht nur für Major-Releases)
    [ ] CHANGELOG.md wird bei jedem Release aktualisiert
    [ ] Release Notes sind verständlich und vollständig (nicht nur "minor fixes")
    [ ] Breaking Changes sind explizit gekennzeichnet und dokumentiert

    ✅ Version Pinning

    [ ] Alle Modul-Aufrufe nutzen explizites Version-Pinning (kein "latest")
    [ ] Git-Module nutzen ?ref= mit konkreten Tags, nicht Branches
    [ ] Registry-Module nutzen version-Constraints (idealerweise ~>)
    [ ] Development/Test-Umgebungen dürfen lockerer gepinnt sein als Production
    [ ] Lock-Files (.terraform.lock.hcl) sind im Git versioniert

    ✅ Testing & Validation

    [ ] Terraform Testing Framework ist für alle kritischen Module implementiert
    [ ] Upgrade-Tests existieren für Major- und Minor-Updates
    [ ] Tests laufen automatisch in CI/CD-Pipeline
    [ ] Backwards-Compatibility wird vor jedem Release getestet
    [ ] Regression-Tests für bekannte Bugs sind vorhanden

    ✅ Governance

    [ ] Sentinel Policies erzwingen Modul-Versionierung (Terraform Enterprise)
    [ ] Deny-Liste für fehlerhafte Modul-Versionen ist implementiert
    [ ] Version-Update-Prozess ist dokumentiert und wird gelebt
    [ ] Änderungen an Modulen durchlaufen Review-Prozess
    [ ] Major-Updates erfordern formales Go/No-Go-Meeting

    ✅ Dokumentation

    [ ] README.md erklärt Versioning-Strategie des Moduls
    [ ] CHANGELOG.md ist gepflegt und folgt Keep-a-Changelog-Format
    [ ] Migration-Guides existieren für alle Major-Updates
    [ ] Known Issues sind dokumentiert
    [ ] Deprecation-Notices werden mindestens 2 Minor-Versionen vorher kommuniziert

    ✅ Monitoring & Incident Response

    [ ] Modul-Versionen in Deployments sind nachvollziehbar (Logs, Tags)
    [ ] Alerting bei unerwarteten Modul-Updates ist aktiv
    [ ] Rollback-Prozeduren sind dokumentiert und getestet
    [ ] Incident-Response-Playbook für Modul-Probleme existiert
    [ ] Postmortem-Prozess nach fehlgeschlagenen Updates ist etabliert

    Abschließende Gedanken

    Modul-Versionierung ist nicht sexy. Es ist nicht das Thema, über das auf Konferenzen gesprochen wird. Es gibt keine fancy Grafiken oder beeindruckende Demos. Aber es ist der Unterschied zwischen einer Infrastruktur, die auch nach drei Jahren noch wartbar ist, und einem unüberschaubaren Chaos, das niemand mehr anfassen will.

    Wenn Sie aus diesem Artikel nur eine Sache mitnehmen, dann diese: Investieren Sie jetzt in saubere Versionierung, oder zahlen Sie später den zehnfachen Preis in Form von Incidents, Rollbacks und Wochenend-Einsätzen.

    Die 3-Uhr-Eskalationen werden dadurch vielleicht nicht komplett verschwinden. Aber sie werden deutlich seltener. Und das ist doch auch schon was.

    Denn die einzig brauchbare Version ist die, die Sie kontrollieren können.