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.tfmit 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:
- CLI-Verfügbarkeit: Führe
terraform version(odertofu versionfür OpenTofu) aus, um zu ermitteln, ob CLI-Formatierung, Validierung und Plan-Prüfungen in Phase 5 ausgeführt werden können. - Fehlende Standarddateien: Prüfe, ob
versions.tf,provider.tf,variables.tf,locals.tfundoutputs.tfexistieren. - 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/...), statischeproject-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
- Cloud-Identifikatoren:
- Formatierungs-Baseline: Wenn Terraform installiert ist, prüfen, ob die Dateien
terraform fmt -check -recursiveentsprechen.
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) odermerge(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
typedeklarieren: (string,number,bool,list(string),map(string)). - Immer
descriptionhinzufügen: Zweck und erwartetes Format erklären. defaultnur angeben, wenn sinnvoll: Konfigurationsoptionen wieenvironmentoderaws_regionkönnen Standardwerte haben, aber sensible Werte dürfen keine hartcodierten Standardwerte haben.- Geheimnisse als
sensitive = truemarkieren: 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 einemmoved {}-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.nameoderaws_s3_bucket.bucket) istForceNewund zerstört und erstellt die Live-Infrastruktur neu. Vorhandene Cloud-Namen invariables.tf/terraform.tfvarsparametrisieren, anstatt sie in"${local.name_prefix}-..."umzuschreiben. - Importierte Images/AMIs bei Brownfield-Importen fixieren: Das Wechseln eines hartcodierten
ami-*oderdebian-12-bookworm-v20240515zu einemmost_recent = true/ Familien-data-Lookup ersetzt vorhandene VMs, wenn ein neueres Image existiert. Das genaue importierte Image/AMI Name oder ID über eine Variable/einendata-Filter fixieren, wenn Zero Drift erforderlich ist, undmost_recent = truenur 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_allbei AWS;effective_labelsundterraform_labelsbei GCP) sowie leere/Standardattribute ("",[],{},false,0). - Konfliktträchtige, aus dem Zustand ausgegebene Unterblöcke entfernen (z.B. primäre
network_interfacebei EC2, wennsubnet_idgesetzt ist). - Legacy
${data.terraform_remote_state.*.outputs.*}-Interpolationen und vollständige Self-Link-URLs durch direkte Ressourcenverweise (z.B.google_compute_network.main.id) oderdata-Lookups ersetzen. - Nummerierte doppelte Ressourcen (
vm-01,vm-02) in eine einzelnefor_each-Ressource zusammenführen, gepaart mitmoved-Blöcken (to = google_compute_instance.api["01"]). - Inline-
\n-escapedeuser_data/startup-script-Strings inscripts/-Dateien extrahieren, die überfile()odertemplatefile()geladen werden. - Bei Mutation von GCP-VM-
scopesodermachine_typesicherstellen, dassallow_stopping_for_update = truegesetzt 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
-
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/labelsund Sicherheits-/Netzwerkeinstellungen mit der importierten Quelle. - Stelle sicher, dass keine
ForceNew-Felder versehentlich geändert wurden und dass jedetfer--*-Ressource einen passendenmoved {}-Block hat. - Trenne und melde alle beabsichtigten Abweichungen (z.B. Sicherheitshärtung) von der Zero-Drift-Refaktorisierung.
- Vergleiche bei jeder Ressource die Cloud-
-
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
terraform plan-Änderungen zusammenfassen (wenn eine Abweichung erkannt wird): Wennterraform planetwas anderes alsNo changesmeldet, vor dem Empfehlen vonapplyanhalten und eine strukturierte Zusammenfassung präsentieren:- Plan-Summen: Die genauen Ressourcenanzahlen angeben (
X to add, Y to change, Z to destroy) und allemoved-State-Übergänge. - Hochrisiko-Änderungen zuerst (
-/+ersetzen,-löschen): Jede für den Austausch oder die Löschung markierte Ressource hervorheben, das verursachendeForceNew-Attribut benennen (z.B.name,image,boot_disk) und erklären, wie es zu beheben ist (z.B. durch Hinzufügen eines fehlendenmoved {}-Blocks oder Wiederherstellen des Originalwerts intfvars). - 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.
- Plan-Summen: Die genauen Ressourcenanzahlen angeben (
Detaillierte Richtlinien & Referenzen
- Siehe Standard-Dateilayout für Domänengruppierungsmuster.
- Siehe Refaktorisierungsregeln für Namensgebung,
ForceNew-Schutz, Tags/Labels und Anti-Pattern-Checklisten.