CommunityCodierung & Entwicklunggithub.com

daily-dreamer/terraform-standardize

Standardisiert rohen, Brownfield- oder per Terraformer importierten Terraform-Code (AWS, GCP, Azure) in produktionsreife HashiCorp-Standards: isoliert provider.tf, versions.tf und backend.tf, parametrisiert hartcodierte Strings in typisierte variables.tf und locals.tf, ersetzt statische Cloud-IDs durch dynamische Referenzen, erzwingt ForceNew- und Zero-Drift-Schutz durch moved-Blöcke und führt kanonische HCL-Formatierung, Validierung und Plan-Diff-Zusammenfassungen durch.

Was ist terraform-standardize?

Diese Fertigkeit leitet den Agenten an, rohen, Brownfield- oder importierten Terraform-Code (z.B. von Terraformer oder `terraform import`) sowie Legacy-Terraform-Code in modulare, produktionsreife HashiCorp-Standardarchitektur umzustrukturieren. Sie wird aktiviert, wenn Benutzer um Standardisierung, Refaktorisierung, Bereinigung oder Modularisierung von Terraform-Konfigurationen bitten. Die Fertigkeit führt einen 5-Phasen-Workflow durch: Audit und Erkennung von Code-Gerüchen, Einrichtung einer Standard-Dateistruktur, Umwandlung von Literalen in Variablen und Locals, Ersetzung statischer Werte durch dynamische Referenzen (inkl. moved-Blöcke und ForceNew-Schutz) sowie Formatierung, Validierung und Drift-Prüfung. Sie unterstützt AWS, GCP und Azure und behandelt Terraformer-spezifische Bereinigungen wie das Entfernen von schreibgeschützten Zustandsattributen und das Ersetzen von Legacy-Interpolationen durch direkte Ressourcenverweise.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/daily-dreamer/terraform-standardize/tree/HEAD/skills/terraform-standardize

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Terraform-Standardisierungsfertigkeit

Diese Fertigkeit leitet den Agenten an, rohen, Brownfield-, importierten (z.B. von Terraformer oder terraform import) oder Legacy-Terraform-Code in modulare, produktionsreife HashiCorp-Standardarchitektur umzustrukturieren.

Wann diese Fertigkeit verwenden

Aktiviere diese Fertigkeit, wenn:

  • Der Benutzer darum bittet, Terraform-Konfigurationen zu "standardisieren", "refaktorisieren", "bereinigen" oder "modularisieren".
  • Roher Terraform-Code hartcodierte Strings, magische Zahlen oder statische Cloud-IDs enthält (ami-*, vpc-*, subnet-*, sg-*, arn:*).
  • Monolithische Einzeldatei-Konfigurationen (main.tf mit allem) in standardmäßige Multi-Datei-Layouts getrennt werden müssen (provider.tf, variables.tf, locals.tf, outputs.tf, backend.tf).
  • Cloud-Ressourcen standardisierte Tagging-Taxonomien benötigen (z.B. Environment, Project, ManagedBy = "terraform").
  • Der Codebasis Variablendefinitionen, Typenbeschränkungen oder Beschreibungsblöcke fehlen.

Standardisierungs-Workflow

Befolge dieses 5-Phasen-Verfahren bei der Standardisierung einer beliebigen Terraform-Codebasis:

┌────────────────────────────────────────────────────────┐
│ Phase 1: Audit & Erkennung von Code-Gerüchen           │
│ • Hartcodierte Literale, IDs, Regionen und CIDRs erkennen │
│ • Vorhandene Dateistruktur vs. Standard-Layout prüfen  │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ Phase 2: Einrichtung der Standard-Dateistruktur        │
│ • provider.tf, versions.tf und backend.tf extrahieren  │
│ • variables.tf, locals.tf und outputs.tf erstellen      │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ Phase 3: Literale in Variablen & Locals parametrisieren │
│ • Umgebungs-/Regionsvariablen mit Typen extrahieren    │
│ • local.common_tags für einheitliches Ressourcen-Tagging │
│ • terraform.tfvars.example erstellen                    │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ Phase 4: Dynamische Referenzverknüpfung & Umbenennung   │
│ • Statische Ressourcen-IDs durch dynamische Referenzen  │
│ • Ersetze hartcodierte AMIs/Subnetze durch Datenquellen │
│ • Ressourcen-Labels standardisieren (snake_case, keine Typen) │
└──────────────────────────┬─────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────┐
│ Phase 5: Formatieren, Validieren und Prüfen            │
│ • `terraform fmt -recursive` ausführen                 │
│ • `terraform validate` ausführen                       │
│ • Mit `terraform plan` verifizieren (Zero Drift)       │
└────────────────────────────────────────────────────────┘

Schritt-für-Schritt-Anleitung

1. Phase 1: Audit & Erkennung

Vor dem Bearbeiten das Zielverzeichnis scannen, um Folgendes zu identifizieren:

  1. CLI-Verfügbarkeit: Führe terraform version (oder tofu version für OpenTofu) aus, um zu ermitteln, ob CLI-Formatierung, Validierung und Plan-Prüfungen in Phase 5 ausgeführt werden können.
  2. Fehlende Standarddateien: Prüfe, ob versions.tf, provider.tf, variables.tf, locals.tf und outputs.tf existieren.
  3. Hartcodierte IDs & Literale:
    • Cloud-Identifikatoren: ami-[a-f0-9]+, vpc-[a-f0-9]+, subnet-[a-f0-9]+, sg-[a-f0-9]+, igw-[a-f0-9]+, vollständige GCP-Selflinks (https://www.googleapis.com/compute/...), statische project-IDs
    • CIDR-Blöcke: "10.0.0.0/16", "0.0.0.0/0"
    • Instanztypen: "t3.micro", "m5.large", "e2-standard-4"
    • Statische Regionen & Kontonummern: "us-east-1", "us-central1", "123456789012"
    • Rohe Anmeldeinformationen oder unverschlüsselte Geheimnisse
  4. Formatierungs-Baseline: Wenn Terraform installiert ist, prüfen, ob die Dateien terraform fmt -check -recursive entsprechen.

2. Phase 2: Standard-Kerndateien erstellen

Sicherstellen, dass die Zielkonfiguration in separate Dateien mit Einzelverantwortung aufgeteilt ist:

provider.tf / versions.tf

Zentrale Terraform-Einstellungen, minimale CLI-Version und erforderliche Provider (angepasst für AWS, GCP oder Azure):

# versions.tf
terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    # GCP-Beispiel:
    # google = {
    #   source  = "hashicorp/google"
    #   version = "~> 5.5"
    # }
  }
}

# provider.tf (AWS)
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = local.common_tags
  }
}

# provider.tf (GCP-Alternative)
# provider "google" {
#   project        = var.project_id
#   region         = var.region
#   default_labels = local.common_labels
# }

Einheitliche Tagging-/Labeling-Strategie: Verwende entweder provider-weite default_tags / default_labels (wobei nur ressourcenspezifische Schlüssel zu einzelnen Ressourcen hinzugefügt werden) oder merge(local.common_tags, ...) / merge(local.common_labels, ...) auf jeder Ressource – niemals beide duplizieren.

backend.tf (falls Remote-State verwendet wird)

Separate State-Speicherkonfiguration (s3 für AWS, gcs für GCP, azurerm für Azure):

terraform {
  backend "s3" {
    bucket         = "myorg-terraform-state"
    key            = "services/app/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

# GCP-Beispiel:
# terraform {
#   backend "gcs" {
#     bucket = "myorg-terraform-state"
#     prefix = "services/app"
#   }
# }

3. Phase 3: Einfache Strings in Variablen und Locals umwandeln

Alle hartcodierten Parameter in variables.tf und locals.tf extrahieren:

variables.tf-Regeln:

  • Immer type deklarieren: (string, number, bool, list(string), map(string)).
  • Immer description hinzufügen: Zweck und erwartetes Format erklären.
  • default nur angeben, wenn sinnvoll: Konfigurationsoptionen wie environment oder aws_region können Standardwerte haben, aber sensible Werte dürfen keine hartcodierten Standardwerte haben.
  • Geheimnisse als sensitive = true markieren: Passwort-, Token- und Geheimnisvariablen müssen als sensibel markiert werden.

Beispiel variables.tf:

variable "aws_region" {
  type        = string
  description = "Die AWS-Region, in der Ressourcen bereitgestellt werden."
  default     = "us-east-1"
}

variable "environment" {
  type        = string
  description = "Name der Bereitstellungsumgebung (z.B. dev, staging, prod)."
  default     = "production"

  validation {
    condition     = contains(["dev", "staging", "production"], var.environment)
    error_message = "Umgebung muss eine der folgenden sein: dev, staging, production."
  }
}

variable "instance_type" {
  type        = string
  description = "EC2-Instanzgröße für den Anwendungscluster."
  default     = "t3.medium"
}

variable "vpc_cidr" {
  type        = string
  description = "Basis-CIDR-Block für die Virtual Private Cloud."
  default     = "10.0.0.0/16"
}

locals.tf-Regeln:

Verwende locals für Werte, die intern abgeleitet werden, Umgebungspräfixe und gemeinsame Tags:

locals {
  name_prefix = "${var.project_name}-${var.environment}"

  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

terraform.tfvars.example

Immer eine Beispieldatei mit Variablenwerten erstellen, damit Teammitglieder wissen, wie Werte bereitgestellt werden:

aws_region   = "us-east-1"
environment  = "production"
project_name = "checkout-service"

4. Phase 4: Statische Werte durch dynamische Referenzen ersetzen (Zero-Drift-Schutz)

Statische Cloud-IDs und getrennte Ressourcenreferenzen in native HCL-Ausdrücke umwandeln und gleichzeitig die Live-Infrastruktur vor versehentlichem Austausch schützen:

Immutability & ForceNew-Schutz (Kritisch für Brownfield / Terraformer)

  • Terraform-Adressen von Cloud-name-Attributen unterscheiden: Das Umbenennen eines Terraform-Ressourcenlabels (tfer--prod-main-vpc → main) mit einem moved {}-Block ist sicher und ohne Ausfallzeiten. Das Ändern eines Cloud-name-Attributs (z.B. google_compute_network.name, google_compute_subnetwork.name, google_compute_firewall.name, google_compute_instance.name oder aws_s3_bucket.bucket) ist ForceNew und zerstört und erstellt die Live-Infrastruktur neu. Vorhandene Cloud-Namen in variables.tf / terraform.tfvars parametrisieren, anstatt sie in "${local.name_prefix}-..." umzuschreiben.
  • Importierte Images/AMIs bei Brownfield-Importen fixieren: Das Wechseln eines hartcodierten ami-* oder debian-12-bookworm-v20240515 zu einem most_recent = true / Familien-data-Lookup ersetzt vorhandene VMs, wenn ein neueres Image existiert. Das genaue importierte Image/AMI Name oder ID über eine Variable/einen data-Filter fixieren, wenn Zero Drift erforderlich ist, und most_recent = true nur aktivieren, wenn der Benutzer sich für Image-Upgrades entscheidet.
  • Sicherheitshärtung getrennt von Zero-Drift-Refaktorisierung halten: Verhaltens-/Sicherheitsattribute (z.B. http_tokens "optional" → "required", public_access_prevention "inherited" → "enforced" oder VM-OAuth-scopes) während einer Zero-Drift-Migration nicht stillschweigend ändern. Härtung als explizites Opt-In behandeln oder jede beabsichtigte Abweichung klar auflisten.
  • Legacy-Tag/Label-Schlüssel bei der Migration beibehalten: Wenn vorhandene Ressourcen env = "prod" verwenden, vorhandene Schlüssel beibehalten (oder standardisierte Schlüssel daneben hinzufügen), es sei denn, der Austausch von Schlüsseln wurde genehmigt.

Terraformer-spezifische Bereinigungs-Checkliste

  • Berechnete schreibgeschützte Zustandsattribute entfernen (tags_all bei AWS; effective_labels und terraform_labels bei GCP) sowie leere/Standardattribute ("", [], {}, false, 0).
  • Konfliktträchtige, aus dem Zustand ausgegebene Unterblöcke entfernen (z.B. primäre network_interface bei EC2, wenn subnet_id gesetzt ist).
  • Legacy ${data.terraform_remote_state.*.outputs.*}-Interpolationen und vollständige Self-Link-URLs durch direkte Ressourcenverweise (z.B. google_compute_network.main.id) oder data-Lookups ersetzen.
  • Nummerierte doppelte Ressourcen (vm-01, vm-02) in eine einzelne for_each-Ressource zusammenführen, gepaart mit moved-Blöcken (to = google_compute_instance.api["01"]).
  • Inline-\n-escapede user_data / startup-script-Strings in scripts/-Dateien extrahieren, die über file() oder templatefile() geladen werden.
  • Bei Mutation von GCP-VM-scopes oder machine_type sicherstellen, dass allow_stopping_for_update = true gesetzt ist, wenn In-Place-Updates beabsichtigt sind.

Anti-Pattern (Roher / hartcodierter Code):

# SCHLECHT: Hartcodierte IDs und Literal-Strings
resource "aws_instance" "tfer--i-0123456789abcdef0" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"
  subnet_id     = "subnet-01234567"
  vpc_security_group_ids = [
    "sg-0abcdef1234567890"
  ]
}

Standard-Muster (Refaktorisiert):

# 1. Zustandserhaltung: Terraform mitteilen, dass diese Ressource umbenannt wurde (verhindert Zerstörung & Neuerstellung)
moved {
  from = aws_instance.tfer--i-0123456789abcdef0
  to   = aws_instance.web
}

# 2. Dynamisches Lookup für Basis-Image (genauen Namen für Zero-Drift-Brownfield fixieren oder most_recent für Greenfield)
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"] # Canonical

  filter {
    name   = "name"
    values = [var.ami_name_pattern]
  }
}

# 3. Standardisierte Ressourcendefinition mit dynamischen Referenzen
resource "aws_instance" "web" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.private[0].id
  vpc_security_group_ids = [aws_security_group.web.id]

  # common_tags hier weglassen, wenn provider.tf bereits default_tags setzt
  tags = {
    Name = var.instance_name
  }
}

5. Phase 5: Formatierung, Drift-Prüfung und Validierung

  1. Pre-Plan Drift-Prüfungs-Checkliste (immer durchführen, besonders wenn die terraform-CLI nicht lokal installiert ist):

    • Vergleiche bei jeder Ressource die Cloud-name, image/ami, location/zone, tags/labels und Sicherheits-/Netzwerkeinstellungen mit der importierten Quelle.
    • Stelle sicher, dass keine ForceNew-Felder versehentlich geändert wurden und dass jede tfer--*-Ressource einen passenden moved {}-Block hat.
    • Trenne und melde alle beabsichtigten Abweichungen (z.B. Sicherheitshärtung) von der Zero-Drift-Refaktorisierung.
  2. Terraform-CLI-Prüfungen ausführen (wenn installiert):

# 1. Code in kanonische HCL-Einrückung formatieren
terraform fmt -recursive

# 2. Provider-Plugins initialisieren (--backend=false bei Offline-Validierung/ohne State verwenden) und Syntax validieren
terraform init -backend=false
terraform validate

# 3. Wenn in einer aktiven Umgebung mit State gearbeitet wird, sicherstellen, dass der Plan KEINE unbeabsichtigten Abweichungen erzeugt
terraform plan
  1. terraform plan-Änderungen zusammenfassen (wenn eine Abweichung erkannt wird): Wenn terraform plan etwas anderes als No changes meldet, vor dem Empfehlen von apply anhalten und eine strukturierte Zusammenfassung präsentieren:
    • Plan-Summen: Die genauen Ressourcenanzahlen angeben (X to add, Y to change, Z to destroy) und alle moved-State-Übergänge.
    • Hochrisiko-Änderungen zuerst (-/+ ersetzen, - löschen): Jede für den Austausch oder die Löschung markierte Ressource hervorheben, das verursachende ForceNew-Attribut benennen (z.B. name, image, boot_disk) und erklären, wie es zu beheben ist (z.B. durch Hinzufügen eines fehlenden moved {}-Blocks oder Wiederherstellen des Originalwerts in tfvars).
    • In-Place-Updates (~) & Erstellungen (+): Jede betroffene Ressource und die genauen Attribute auflisten, die sich ändern (z.B. labels, tags, scopes, public_access_prevention).
    • Beabsichtigte vs. unbeabsichtigte Abweichung: Klar markieren, welche Abweichungen beabsichtigt sind (Opt-In-Sicherheitshärtung oder Tag-Standardisierung) versus unbeabsichtigtes Brownfield-Drift, damit der Benutzer sie genehmigen oder rückgängig machen kann.

Detaillierte Richtlinien & Referenzen

Verwandte Skills