Infrastructure as Code mit 200 Ressourcen: Was Terraform-Tutorials dir nicht sagen
Produktions-IaC-Muster für echte Systeme. State Management im großen Maßstab, Modul-Design, CDK + Terraform Hybrid, Drift-Erkennung, GitOps mit Flux und 30+ AWS-Services verwalten.
IaC ist nicht "terraform init"
Jedes Terraform-Tutorial fängt gleich an: schreib eine .tf-Datei, führe terraform init aus, dann terraform apply, und schau zu, wie deine EC2-Instanz erscheint. Damit kommst du von null auf eine Ressource. Es bereitet dich nicht darauf vor, 200+ Ressourcen über 30 AWS-Services hinweg zu verwalten, mit einem Team von Ingenieuren, die alle sicher Infrastrukturänderungen machen müssen.
Wir verwalten Infrastruktur für mehrere Produktionssysteme, von Kubernetes-Clustern mit Pimcore und OpenSearch bis zu Serverless-Architekturen mit Lambda, DynamoDB und API Gateway. Die Muster in diesem Artikel haben die Produktion überlebt. Wie wir Anwendungen auf dieser Infrastruktur deployen, findest du auf unserer Cloud-Services Seite.
State Management im großen Maßstab
Terraform State ist die kritischste Datei deiner Infrastruktur. Er bildet deine .tf-Dateien auf echte Ressourcen ab. Verlier ihn, und Terraform weiß nicht, was existiert. Beschädige ihn, und Terraform zerstört möglicherweise Produktionsressourcen.
Remote State (Nicht verhandelbar)
# backend.tf
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "prod/platform/terraform.tfstate"
region = "eu-central-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
| Regel | Warum |
|---|---|
| Remote State in S3 (oder Äquivalent) | Lokale State-Dateien gehen verloren, können nicht geteilt werden |
| Verschlüsselung im Ruhezustand | State enthält Secrets (Datenbankpasswörter, API-Keys) |
| DynamoDB Locking | Verhindert, dass zwei Ingenieure gleichzeitig apply ausführen |
| Versionierung auf dem S3-Bucket | Wiederherstellung nach State-Korruption durch Rollback |
State-Organisation
Eine riesige State-Datei für alles ist eine Wartungskatastrophe. Aufteilen nach Umgebung und Domäne:
terraform/
├── environments/
│ ├── prod/
│ │ ├── platform/ # EKS, VPC, Netzwerk
│ │ ├── databases/ # RDS, ElastiCache, OpenSearch
│ │ ├── compute/ # Lambda, ECS, Fargate
│ │ ├── storage/ # S3-Buckets, CloudFront
│ │ └── monitoring/ # CloudWatch, Alarme
│ ├── staging/
│ │ └── (gleiche Struktur)
│ └── dev/
│ └── (gleiche Struktur)
├── modules/
│ ├── vpc/
│ ├── eks-cluster/
│ ├── rds-postgres/
│ ├── opensearch/
│ ├── redis/
│ └── lambda-function/
└── global/
├── iam/ # IAM-Rollen, Policies
├── route53/ # DNS-Zonen
└── ecr/ # Container-Registries
Jedes Verzeichnis ist ein separater Terraform-Workspace mit eigener State-Datei. Änderungen am Netzwerk gefährden nicht die Datenbank. Änderungen am Monitoring erfordern keinen Plan, der jede Ressource anfasst.
Cross-State-Referenzen
Workspaces müssen sich gegenseitig referenzieren. Der VPC-Workspace gibt die VPC-ID aus. Der Datenbank-Workspace liest sie:
# In databases/main.tf
data "terraform_remote_state" "platform" {
backend = "s3"
config = {
bucket = "company-terraform-state"
key = "prod/platform/terraform.tfstate"
region = "eu-central-1"
}
}
resource "aws_db_instance" "main" {
vpc_security_group_ids = [data.terraform_remote_state.platform.outputs.db_security_group_id]
db_subnet_group_name = data.terraform_remote_state.platform.outputs.db_subnet_group_name
}
Modul-Design
Wann du ein Modul extrahieren solltest
Nicht jede Ressource braucht ein Modul. Extrahiere, wenn:
- Das gleiche Muster an 3+ Stellen verwendet wird (DRY)
- Die Ressourcengruppe eine klare Grenze hat (VPC, Datenbank-Cluster)
- Die Konfiguration sinnvolle Defaults hat, die Duplikation reduzieren
Extrahiere nicht, wenn:
- Es nur einmal verwendet wird (vorzeitige Abstraktion)
- Das Modul 20+ Variablen hätte (zu viele Stellschrauben)
- Die Abstraktion wichtige Details versteckt (Netzwerk, Sicherheit)
Modul-Interface-Design
Ein gutes Modul hat wenige erforderliche Variablen, sinnvolle Defaults und klare Outputs:
# modules/rds-postgres/variables.tf
variable "name" {
description = "Name der Datenbankinstanz"
type = string
}
variable "vpc_id" {
description = "VPC, in die deployt wird"
type = string
}
variable "subnet_ids" {
description = "Subnetze für die DB-Subnetzgruppe"
type = list(string)
}
variable "instance_class" {
description = "RDS-Instanztyp"
type = string
default = "db.t3.medium"
}
variable "engine_version" {
description = "PostgreSQL-Version"
type = string
default = "15.4"
}
variable "allocated_storage" {
description = "Speicher in GB"
type = number
default = 50
}
variable "multi_az" {
description = "Multi-AZ-Deployment aktivieren"
type = bool
default = false # true für Prod, false für Staging/Dev
}
Der Modul-Konsument schreibt:
module "database" {
source = "../../modules/rds-postgres"
name = "pimcore-prod"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
multi_az = true
}
Fünf Zeilen statt fünfzig. Das Modul kümmert sich um Security Groups, Parameter Groups, Subnetzgruppen, Verschlüsselung, Backup-Retention und Monitoring.
CDK + Terraform: Der pragmatische Hybrid
Manche Teams setzen voll auf CDK. Andere voll auf Terraform. Wir nutzen beides, und es funktioniert.
| Anwendungsfall | Tool | Warum |
|---|---|---|
| Netzwerk, Datenbanken, Cluster | Terraform | Deklarativ, Plan-vor-Apply, State Management |
| Lambda-Funktionen + API Gateway | CDK | Besseres Lambda-Bundling, API-Gateway-Constructs |
| Komplexe IAM-Policies | CDK | TypeScript-Logik für bedingte Policies |
| Kubernetes-Ressourcen | Kustomize + Flux | GitOps, Reconciliation-Loops |
| Statische Infrastruktur | Terraform | Einfach, lesbar, gut verstanden |
Die Grenze ist klar: Terraform verwaltet Infrastruktur, die sich selten ändert (VPC, RDS, EKS-Cluster). CDK verwaltet Infrastruktur, die sich mit Application-Deployments ändert (Lambda-Funktionen, API-Routen). Kustomize + Flux verwaltet Kubernetes-Workloads.
Die Koexistenz funktioniert über Outputs. Terraform gibt die VPC-ID, den Cluster-Endpoint und den Datenbank-Connection-String aus. CDK liest sie aus dem SSM Parameter Store oder dem Terraform Remote State.
Das Drift-Problem
Drift passiert, wenn jemand Infrastruktur über die Konsole ändert (ClickOps), über einen CLI-Befehl oder über ein anderes Tool. Der reale Zustand weicht vom Terraform State ab.
Drift erkennen
# Plan regelmäßig ausführen (CI, geplanter Job)
terraform plan -detailed-exitcode
# Exit-Codes:
# 0 = keine Änderungen (State stimmt mit Realität überein)
# 1 = Fehler
# 2 = Änderungen erkannt (Drift!)
Führe Drift-Erkennung in CI nach Zeitplan aus (täglich für Produktion, wöchentlich für Staging). Alarmiere bei erkanntem Drift. Nicht automatisch beheben. Zuerst untersuchen.
Häufige Drift-Ursachen
| Ursache | Prävention |
|---|---|
| Konsolenänderungen (ClickOps) | "Keine Konsolenänderungen"-Policy durchsetzen. SCPs zur Einschränkung verwenden. |
| Auto-Scaling-Änderungen | Auto-Scaling-Attribute in Terraform ignorieren (lifecycle { ignore_changes }) |
| AWS-Service-Updates | Provider-Versionen pinnen. Bewusst aktualisieren. |
| Terraform eines anderen Teams | Separate State-Dateien pro Team/Domäne. |
| Manueller Hotfix während eines Incidents | Änderung dokumentieren. Nach dem Incident in Terraform umsetzen. |
# Auto-Scaling-Änderungen ignorieren (erwarteter Drift)
resource "aws_ecs_service" "app" {
desired_count = 2
lifecycle {
ignore_changes = [desired_count] # Auto-Scaling ändert diesen Wert
}
}
GitOps mit Flux
Für Kubernetes-Workloads nutzen wir Flux für GitOps. Der Reconciliation-Loop ersetzt kubectl apply durch ein Pull-basiertes Modell.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Git Repo │────▶│ Flux │────▶│ Kubernetes │
│ (Manifests) │ │ Controller │ │ Cluster │
└─────────────┘ └─────────────┘ └─────────────┘
│
│ Reconciliation alle 1 Min.
│ Erkennt Drift
│ Wendet automatisch an
# flux-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: platform
namespace: flux-system
spec:
interval: 1m
sourceRef:
kind: GitRepository
name: infrastructure
path: ./kubernetes/resources/overlay/prod
prune: true
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: pimcore
namespace: production
Flux pollt das Git-Repo jede Minute. Wenn sich Manifests geändert haben, wendet es sie an. Wenn jemand eine Ressource manuell geändert hat (Drift), setzt Flux sie auf den Git-Stand zurück. Das ist echte Reconciliation, nicht nur Deployment-Automatisierung.
Sealed Secrets
Secrets dürfen nicht als Klartext in Git. Verwende Bitnami Sealed Secrets:
# Secret für den Cluster verschlüsseln
kubeseal --cert sealed-secrets.pem \
-f secrets/database-secrets.yaml \
-o yaml > secrets/database-secrets-sealed.yaml
# Die versiegelte Version committen (sicher in Git)
# Flux wendet sie an, der Controller entschlüsselt sie im Cluster
Wie wir Secrets in Pimcore-Kubernetes-Deployments konkret handhaben, beschreibt unser Pimcore-Upgrade-Guide, der die vollständige Deployment-Reihenfolge behandelt.
30+ AWS-Services verwalten
Im Enterprise-Maßstab verwaltest du viele Services. Organisation ist entscheidend.
Service-Katalog
| Kategorie | Services | Terraform-Modul? |
|---|---|---|
| Netzwerk | VPC, Subnetze, NAT, ALB, Route53 | Ja (vpc-Modul) |
| Compute | EKS, ECS Fargate, Lambda | Ja (pro Service-Typ) |
| Datenbank | RDS PostgreSQL, DynamoDB | Ja (rds-postgres-Modul) |
| Cache | ElastiCache Redis | Ja (redis-Modul) |
| Suche | OpenSearch | Ja (opensearch-Modul) |
| Speicher | S3, EFS | Inline (einfach genug) |
| CDN | CloudFront | Inline |
| Messaging | SQS, MSK (Kafka), RabbitMQ | Inline |
| Auth | Cognito | CDK (komplexe Konfiguration) |
| Monitoring | CloudWatch, X-Ray | Inline |
| CI/CD | ECR, CodeBuild | Inline |
| Sicherheit | IAM, KMS, Secrets Manager | Globaler Workspace |
Tagging-Strategie
Jede Ressource muss getaggt sein für Kostenverteilung, Ownership und Lifecycle-Management:
locals {
common_tags = {
Environment = var.environment # prod, staging, dev
Project = var.project_name # pimcore, commerce, ai
ManagedBy = "terraform"
Team = var.team # platform, backend, data
CostCenter = var.cost_center
}
}
resource "aws_instance" "example" {
tags = merge(local.common_tags, {
Name = "pimcore-web-1"
Role = "web"
})
}
Filtere den AWS Cost Explorer nach dem Project-Tag, um genau zu sehen, was jedes System kostet. Filtere nach ManagedBy, um manuell erstellte Ressourcen zu finden (nicht als "terraform" getaggt).
Häufige Fehler
-
Eine State-Datei für alles. Ein
terraform plan, der 200 Ressourcen anfasst, dauert Minuten, und ein Fehler betrifft alles. Aufteilen nach Domäne und Umgebung. -
Kein State Locking. Zwei Ingenieure führen gleichzeitig
terraform applyaus. Die Änderungen von einem gehen verloren oder der State ist beschädigt. DynamoDB Locking verwenden. -
Module mit 20+ Variablen. Wenn dein Modul-Interface genauso komplex ist wie die rohen Ressourcen, bringt die Abstraktion keinen Mehrwert. Halte Modul-Interfaces klein.
-
Drift automatisch beheben. Drift zu erkennen ist gut. Ihn automatisch zu fixen ist gefährlich. Der "Drift" könnte ein valider Hotfix während eines Incidents sein. Erst untersuchen, dann revertieren.
-
Secrets in State-Dateien. Terraform State enthält jedes Attribut jeder Ressource, einschließlich Datenbankpasswörter. State im Ruhezustand verschlüsseln und Zugriff einschränken.
-
Kein Provider-Version-Pinning. Ein Provider-Update ändert das Ressourcenverhalten. Versionen in
required_providerspinnen und bewusst aktualisieren. -
ClickOps für "nur dieses eine Mal". Konsolenänderungen erzeugen Drift, der bis zum nächsten
terraform planunsichtbar ist. Infrastructure-as-Code für alles durchsetzen. -
Kein Tagging. Ohne Tags kannst du keine Kosten zuordnen, keine Ownership identifizieren und keine manuell erstellten Ressourcen finden.
Wichtigste Erkenntnisse
-
State nach Domäne und Umgebung aufteilen. Netzwerk, Datenbanken, Compute und Monitoring sollten separate Workspaces sein. Änderungen an einem sollten nicht einen anderen gefährden.
-
Module sind für Muster, nicht für Abstraktion. Extrahiere, wenn die gleiche Ressourcengruppe 3+ Mal vorkommt. Erstelle keine Module für einmalige Ressourcen.
-
CDK + Terraform ist pragmatisch. Terraform für statische Infrastruktur, CDK für Lambda/API Gateway, Kustomize + Flux für Kubernetes. Jedes Tool dort, wo es am stärksten ist.
-
Drift-Erkennung ist ein geplanter Job. Führe
terraform plantäglich in CI aus. Alarmiere bei Drift. Untersuche vor dem Beheben. -
GitOps mit Flux bietet echte Reconciliation. Nicht nur Deployment-Automatisierung. Flux erkennt und revertiert manuelle Änderungen. Sealed Secrets halten Credentials sicher in Git.
-
Alles taggen. Environment, Projekt, Team, Cost Center, Managed-by. Ohne Tags sind Kostenzuordnung und Ressourcen-Auditing unmöglich.
Wir verwalten Infrastruktur für Cloud-Deployments, Custom-Software-Plattformen und Data-Engineering-Systeme. Wenn du Hilfe mit IaC im großen Maßstab brauchst, sprich mit unserem Team oder fordere ein Angebot an.
Behandelte Themen
Verwandte Guides
Agent Memory, das sich wirklich erinnert: Knowledge Graphs und Context Assembly
Ein tiefgehender technischer Guide zu Agent Memory in Produktion: Recent Windows, Semantic Recall, Working-Memory-Templates, mandantenspezifische Knowledge Graphs und mehrschichtige Context Assembly.
Guide lesenUnternehmenshandbuch zu Agentischen KI-Systemen
Technischer Leitfaden zu agentischen KI-Systemen in Unternehmen. Erfahre mehr ueber Architektur, Faehigkeiten und Anwendungen autonomer KI-Agenten.
Guide lesenAgentic Commerce: Wie du KI-Agenten sicher einkaufen lässt
Wie du gesteuerten, KI-initiierten Handel designst. Policy Engines, HITL-Freigabe-Gates, HMAC-Quittungen, Idempotenz, Tenant-Scoping und das vollständige Agentic Checkout Protocol.
Guide lesenBauen Sie gerade so etwas?
Wir entwerfen und betreiben Produktionssysteme wie das in diesem Leitfaden. Sprechen Sie mit den Ingenieuren, die ihn geschrieben haben, ohne Verkaufsgespräch.
Gespräch starten