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
Unternehmenshandbuch 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 lesenDie 9 Stellen, an denen dein KI-System Daten verliert (und wie du jede einzelne abdichtest)
Eine systematische Übersicht aller Stellen, an denen KI-Systeme Daten preisgeben. Prompts, Embeddings, Logs, Tool Calls, Agent Memory, Fehlermeldungen, Cache, Fine-Tuning-Daten und Agent Handoffs.
Guide lesenBereit, produktionsreife KI-Systeme zu bauen?
Unser Team ist spezialisiert auf produktionsreife KI-Systeme. Lass uns besprechen, wie wir deinem Unternehmen helfen können.
Gespräch starten