Terraform State Management mit S3 und DynamoDB: Der produktionsreife Ansatz
Ursprünglich veröffentlicht auf LinkedIn.
Das Problem mit lokalem State
Wer Terraform zum ersten Mal nutzt, speichert den State lokal in terraform.tfstate. Das funktioniert für Einzelpersonen und Experimente — aber sobald ein zweites Teammitglied hinzukommt oder eine CI/CD-Pipeline Deployments übernimmt, entstehen Konflikte, Datenverluste und inkonsistente Infrastruktur.
Die Lösung: ein Remote-Backend. Auf AWS ist die Kombination aus S3 (State-Speicherung) und DynamoDB (State-Locking) der de-facto-Standard.
S3-Bucket für den State
Der S3-Bucket braucht einige wichtige Konfigurationen:
resource "aws_s3_bucket" "terraform_state" { bucket = "my-project-tfstate" # Verhindert versehentliches Löschen lifecycle { prevent_destroy = true }}resource "aws_s3_bucket_versioning" "terraform_state" { bucket = aws_s3_bucket.terraform_state.id versioning_configuration { status = "Enabled" }}resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" { bucket = aws_s3_bucket.terraform_state.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" } }}resource "aws_s3_bucket_public_access_block" "terraform_state" { bucket = aws_s3_bucket.terraform_state.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true}Versioning ist entscheidend: Es ermöglicht das Zurückrollen auf einen früheren State, wenn ein apply schiefläuft.
DynamoDB-Tabelle für State Locking
State Locking verhindert, dass zwei terraform apply-Läufe gleichzeitig denselben State modifizieren:
resource "aws_dynamodb_table" "terraform_lock" { name = "my-project-tflock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" attribute { name = "LockID" type = "S" }}PAY_PER_REQUEST ist hier die richtige Wahl — die Tabelle wird selten geschrieben, und On-Demand-Billing vermeidet unnötige Kosten.
Backend-Konfiguration
terraform { backend "s3" { bucket = "my-project-tfstate" key = "prod/terraform.tfstate" region = "eu-central-1" encrypt = true dynamodb_table = "my-project-tflock" }}Der key-Pfad ermöglicht mehrere State-Dateien im selben Bucket — nützlich für Workspaces oder mehrere Umgebungen.
Bootstrapping-Problem
Hier liegt die klassische Henne-Ei-Frage: Der S3-Bucket und die DynamoDB-Tabelle müssen existieren, bevor Terraform das Backend nutzen kann. Lösungsansätze:
- Manuell erstellen — einmalig per AWS CLI oder Konsole, dann in Terraform importieren
- Separates Bootstrap-Modul — ein kleines Terraform-Projekt mit lokalem State, das nur das Backend provisioniert
- Terraform Cloud — umgeht das Problem vollständig
Ich bevorzuge Option 2: Das Bootstrap-Modul ist klein, selten geändert, und der lokale State für dieses eine Modul ist akzeptabel.
IAM-Berechtigungen für CI/CD
Die CI/CD-Pipeline braucht minimale Berechtigungen:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-project-tfstate/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-project-tfstate" }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem" ], "Resource": "arn:aws:dynamodb:eu-central-1:*:table/my-project-tflock" } ]}Least Privilege gilt auch hier: Die Pipeline braucht keine dynamodb:Scan- oder dynamodb:Query-Berechtigungen.
Fazit
S3 + DynamoDB als Terraform-Backend ist battle-tested, kostengünstig und einfach zu betreiben. Die Einrichtung dauert 30 Minuten — und spart Stunden an Debugging bei State-Konflikten.
Dieser Artikel wurde ursprünglich auf LinkedIn veröffentlicht und für die Website-Version erweitert.