Terraform
auf einem Blatt.
Dichte Referenz für Senior Platform Engineers und SREs zu HashiCorps Infrastructure-as-Code-Werkzeug. HCL-Kern, State und Locking, Module, Meta-Argumente, Import und Refactoring, Tests, CI/CD-Patterns und Anti-Patterns. Keine Einsteiger-Folien.
Vorschau (2 Seiten A4 quer + Brand-Rückseite)


PDF herunterladen
Direkter Download, keine Mail-Adresse nötig. Unter CC BY-SA 4.0 ist Kopieren, Drucken und Weiterverteilen ausdrücklich erlaubt, solange die Quellenangabe sichtbar bleibt.
Was drin steht
HCL-Kern & Provider
resource/data/locals/variables/outputs, Typen + Validation, sensitive. Version-Pinning mit ~>, .terraform.lock.hcl, Provider-Alias für Multi-Account, region je Ressource (AWS 6.0+).
State & Drift
Remote Backends, natives S3-Locking (use_lockfile), state mv/rm, plan -refresh-only. Ephemeral Values und write-only Attribute gegen Klartext-Secrets.
Module & Meta-Argumente
Struktur, Registry-Versionierung, Git-ref-Pinning, dynamische Quellen mit const-Variablen (1.15+). for_each vs count, depends_on, lifecycle inkl. destroy = false (1.16+), dynamic blocks.
Plan/Apply & Import
plan -out als Artefakt, -detailed-exitcode, -replace, -target-Vorsicht. import-Block (auch in Modulen, 1.16+), terraform query, -generate-config-out, moved/removed statt State-Chirurgie.
Workspaces & Tests
Workspaces und ihre Grenzen bei Prod-Trennung. terraform test mit .tftest.hcl, run/assert, Mocking seit 1.7.
CI/CD & Anti-Patterns
Plan-Artefakt + Approval-Gate, Pipeline-Hygiene, BUSL-Lizenzstand. Anti-Patterns aus der Praxis.
Cheatsheet im Volltext
Derselbe Inhalt wie im PDF, zum Mitlesen, Durchsuchen und direkten Kopieren der HCL-Snippets. Stand: Terraform v1.16 (Edition 2026.10).
HCL-Kern
Bausteine
resource: verwaltetes Objekt (Create/Update/Delete durch Terraform). data: Read-only-Lookup bestehender Objekte. variable: Eingabe pro Root-/Child-Modul. locals: benannte Zwischenwerte, DRY für Ausdrücke. output: Rückgabewerte, Schnittstelle zwischen Modulen und nach außen.
Typen, Validation, sensitive
Typen explizit setzen: string, number,
bool, list(...), map(...),
object({...}), fängt Fehler beim Plan
statt beim Apply.
validation-Block prüft Eingaben mit
condition + error_message.
sensitive = true maskiert Werte im CLI-Output,
entfernt sie aber nicht aus State oder Plan-Datei.
variable "env" {
type = string
validation {
condition = contains(["dev","prod"], var.env)
error_message = "env muss dev oder prod sein."
}
}
locals {
name_prefix = "app-${var.env}"
}
output "lb_dns" {
value = aws_lb.this.dns_name
description = "Public LB-Endpoint"
}
Provider & Versionen
Version-Pinning
required_version pinnt die CLI,
required_providers pinnt Provider
(source + version).
Pessimistic-Operator ~> lässt nur die
letzte angegebene Stelle steigen: ~> 6.0 = 6.x,
~> 6.0.1 = 6.0.x.
.terraform.lock.hcl einchecken,
exakte Provider-Versionen + Hashes für alle
Umgebungen. terraform init -upgrade ist eine
bewusste Aktion, kein Default.
Provider-Alias
Mehrere Accounts/Rollen: gleicher Provider mit
alias, Zuordnung per provider = am
Resource- bzw. providers =-Map am Modul-Block.
Nur andere Region im selben Account: AWS-Provider 6.0+ nimmt
region an den meisten Ressourcen direkt, ohne Alias.
terraform {
required_version = ">= 1.10, < 2.0"
required_providers {
aws = { source = "hashicorp/aws",
version = "~> 6.0" }
}
}
provider "aws" { region = "eu-central-1" }
provider "aws" {
alias = "dr"
region = "eu-west-1"
assume_role { role_arn = var.dr_role_arn }
}
resource "aws_s3_bucket" "replica" {
provider = aws.dr
bucket = "${local.name_prefix}-replica"
}
State
Remote Backend, Locking
State gehört in ein Remote Backend (S3, azurerm, gcs, …): gemeinsamer Stand + Locking gegen parallele Applies.
S3: natives Locking via use_lockfile = true
(seit 1.10, GA in 1.11); die DynamoDB-Argumente sind seit 1.11
deprecated.
terraform {
backend "s3" {
bucket = "tf-state-prod"
key = "network/terraform.tfstate"
region = "eu-central-1"
use_lockfile = true # natives S3-Locking
encrypt = true
}
}
State ist sensibel
Der State enthält Resource-Attribute im Klartext, auch Passwörter und Keys. Zugriff wie auf ein Secret beschränken, Verschlüsselung at rest aktivieren.
Werte aus dem State heraushalten: ephemeral Values/Resources (1.10+) und write-only Attribute (1.11+) werden nicht persistiert.
State-Kommandos, Drift
state list / state show: Inventar
inspizieren. state mv / state rm:
imperative Chirurgie, für geplantes Refactoring
besser moved/removed-Blöcke.
Drift: plan -refresh-only zeigt
Abweichungen der Realität vom State,
apply -refresh-only übernimmt sie in den State
(ohne Infrastruktur-Änderung).
terraform state list terraform state show aws_s3_bucket.logs terraform state mv aws_iam_role.a aws_iam_role.b terraform state rm aws_iam_role.orphan terraform plan -refresh-only # Drift-Report
Module
Struktur
Standard-Layout: main.tf,
variables.tf, outputs.tf
(+ README). Kleine, fachlich geschnittene Module;
Komposition flach im Root-Modul. Tiefe
Modul-in-Modul-Ketten machen Plans träge und Schnittstellen
unklar.
Quellen, Versionierung
Registry-Quelle <ns>/<name>/<provider>
+ version (nur Registry-Quellen unterstützen
version).
Git-Quelle über ?ref= auf Tag/Commit
pinnen, nie auf einen beweglichen Branch.
Seit 1.15 dürfen source/version
Variablen und Locals enthalten; die Variable braucht
const = true (zur init-Zeit bekannt,
nicht sensitive/ephemeral).
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "6.7.3"
name = local.name_prefix
cidr = "10.20.0.0/16"
}
module "baseline" {
source = "git::https://git.example.com/baseline.git?ref=v2.3.1"
}
Meta-Argumente
for_each, count, depends_on
for_each über Map/Set: stabile
Keys als Adresse, Einträge löschen
verschiebt keine Nachbarn.
count: nur für identische Kopien oder das
An/Aus-Muster (count = var.enabled ? 1 : 0). Bei
benannten Objekten drohen Index-Shifts.
depends_on: nur wenn die implizite
Abhängigkeit über Referenzen nicht ausreicht (z.B.
IAM-Policy vor Nutzung).
lifecycle
create_before_destroy: Ersatz zuerst hochziehen.
prevent_destroy: Plan schlägt fehl, statt z.B.
eine Datenbank zu löschen.
ignore_changes: einzelne Attribute extern
verwalten lassen, chirurgisch einsetzen.
replace_triggered_by: Ersatz an fremde
Änderung koppeln.
destroy = false (1.16+): Delete wird zu Forget
(bei Replace, entfallenden count/for_each-Instanzen,
destroy); das Objekt bleibt unverwaltet stehen. Wer
den ganzen Block löscht, braucht weiter
removed.
resource "aws_iam_user" "team" {
for_each = toset(["ci", "audit", "ops"])
name = "svc-${each.key}"
}
resource "aws_db_instance" "main" {
# ...
lifecycle {
prevent_destroy = true
ignore_changes = [password]
}
}
dynamic blocks
Wiederholte Nested Blocks
dynamic "<block>" + for_each +
content erzeugt wiederholte verschachtelte
Blöcke; Iterator heißt wie das Block-Label.
Sparsam einsetzen: verschachtelte dynamics sind
schwer lesbar, oft ist die ausgeschriebene Liste klarer.
resource "aws_security_group" "web" {
name = "${local.name_prefix}-web"
dynamic "ingress" {
for_each = var.service_ports # z.B. {http=80, https=443}
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}
}
Plan / Apply / Destroy
Kern-Workflow
fmt -check + validate als schnelles
Gate, dann plan -out=tfplan und
apply tfplan, es wird exakt der
reviewte Plan angewandt.
plan -detailed-exitcode: 0 = keine
Änderung, 1 = Fehler, 2 = Änderungen, sauber für
Pipelines. -replace=<addr> erzwingt gezielte
Neuerstellung. destroy nur für ganze
(Wegwerf-)Umgebungen.
-target ist ein Notfall-Werkzeug
-target schneidet den
Abhängigkeitsgraphen ab, das Ergebnis entspricht
nicht mehr der Konfiguration. Nur zur Incident-Reparatur, danach
sofort ein voller Plan ohne -target, bis er leer
ist.
terraform fmt -check -recursive terraform validate terraform plan -out=tfplan -detailed-exitcode terraform apply tfplan terraform apply -replace='aws_instance.worker[2]'
Import & Refactoring
import-Block (1.5+)
Bestehende Infrastruktur deklarativ
adoptieren: plannbar, reviewbar, im normalen Plan/Apply-Zyklus.
-generate-config-out=<datei> erzeugt HCL
für importierte Objekte ohne Config. for_each
am import-Block seit 1.7, import-Blöcke in Child-Modulen
seit 1.16.
Statt id geht seit 1.12 identity = {...}
(strukturierte Resource Identity, sofern der Provider sie liefert;
schließt id aus).
terraform query (1.14+): list-Blöcke
in *.tfquery.hcl finden bestehende Objekte,
-generate-config-out liefert Config +
import-Blöcke.
moved / removed
moved (1.1+): Adresse geändert (Rename,
Modul-Umzug), Terraform migriert den State beim Plan,
kein state mv.
removed (1.7+): Objekt aus Config + State
entfernen, ohne es zu zerstören
(destroy = false).
import {
to = aws_s3_bucket.logs
id = "legacy-logs-bucket"
}
moved {
from = aws_instance.app
to = module.compute.aws_instance.app
}
removed {
from = aws_s3_bucket.archiv
lifecycle { destroy = false }
}
Workspaces & Tests
Workspaces und ihre Grenzen
Ein Workspace = eigener State bei gleicher Config und gleichem Backend. Gut für kurzlebige Varianten (Feature-Test, Review-Umgebung).
Grenze: Prod/Non-Prod-Trennung, gleiche Credentials, gleicher State-Bucket,
terraform.workspace-Verzweigungen im Code.
Dafür getrennte Root-Module + Backends verwenden.
terraform workspace new review-142
terraform workspace select review-142
terraform workspace list
# im Code: name = "app-${terraform.workspace}"
terraform test (1.6+)
Tests in *.tftest.hcl: run-Blöcke
führen command = plan (schnell, ohne echte
Ressourcen) oder apply aus, Default ist
apply, und prüfen assert-Bedingungen. Seit 1.7:
mock_provider + Overrides, Provider-Antworten simulieren statt echte Infrastruktur
anzulegen.
# tests/naming.tftest.hcl
run "prod_prefix" {
command = plan
variables { env = "prod" }
assert {
condition = local.name_prefix == "app-prod"
error_message = "Prefix-Schema verletzt"
}
}
CI/CD-Patterns
Plan-Artefakt, Approval
Build-Job: plan -out=tfplan, Plan-Artefakt +
lesbaren show-Output an den Review hängen.
Nach Approval wendet der Apply-Job dasselbe
Artefakt an, kein zweiter Plan zwischen Review
und Apply, keine unbemerkte Differenz.
Pipeline-Hygiene
init -input=false (keine interaktiven Prompts),
fmt -check + validate als frühes
Gate, Lock-Datei committed; init -lockfile=readonly
schreibt sie nie um, neue oder entfernte Provider brechen ab.
State-Zugriff nur für die Pipeline-Identität; kurzlebige Cloud-Credentials statt statischer Keys.
terraform init -input=false -lockfile=readonly terraform plan -input=false -out=tfplan terraform show -no-color tfplan > plan.txt # Review # nach Approval: terraform apply -input=false tfplan
Version & Lizenz
1.x-Release-Linie, BUSL
Aktuelle stabile Linie: 1.16 (1.16.0 vom 26.08.2026, Stand Oktober 2026), 1.17 in der Beta.
Für destroy = false mit
create_before_destroy und für mehrere
import-Blöcke auf count/for_each-Instanzen mindestens 1.16.3
nehmen.
Seit 1.6 steht Terraform unter der Business Source License 1.1 (Change License MPL 2.0, je Version vier Jahre nach Veröffentlichung); die Einschränkung zielt auf konkurrierende Angebote. Versionen bis 1.5.x bleiben MPL 2.0. OpenTofu ist ein MPL-2.0-lizenzierter Community-Fork auf Basis von Terraform 1.5.
Anti-Patterns
Was du nicht tun solltest
-target als Routine: zerschnittener Graph, Code und Realität divergieren, nur im Notfall.
State von Hand editieren: JSON-Chirurgie
korrumpiert Serial/Lineage, state-Kommandos
oder moved/removed nutzen.
count für benannte Objekte:
Löschen in der Mitte verschiebt Indizes, Nachbarn
werden zerstört/neu erzeugt. for_each
nehmen.
Provider/Module unpinned: nächstes
init zieht ein Major-Update in den Apply. Provider
pinnen + Lock-Datei committen; die Lock-Datei deckt
nur Provider ab, Modulversionen löst jedes frische
init neu auf, also exakt pinnen (version
bzw. ?ref=-Tag).
Secrets als Klartext-Variable: landen in State und Plan-Datei, ephemeral/write-only nutzen, State-Zugriff beschränken.
Workspaces als Prod-Trennung: ein Backend, eine Credential-Grenze für alles, getrennte Root-Module + Backends.
Apply ohne Plan-Artefakt in CI: zwischen
Review und Apply entsteht ein anderer Plan, immer
apply tfplan.
Monolith-Root-Modul: ein State für alles = maximaler Blast-Radius und träge Plans, nach Lebenszyklus/Team schneiden.
ignore_changes als Dauerpflaster: verdeckt Drift und echte Ownership-Fragen, klären, wer das Attribut verwaltet.
Verwandte Cheatsheets
Ebenfalls von OMNI52:
vault-cheatsheet.de, Secrets-Management
flux-cheatsheet.de, GitOps mit Flux
argocd-cheatsheet.de, GitOps mit Argo CD
rancher-cheatsheet.de, Cluster-Management
Lizenz & Weiterverteilung
Nicht erlaubt: Logo, Marken oder den Eindruck zu vermitteln, dass der Inhalt von dir/euch stammt oder dass OMNI52 GmbH die Weiterverwendung sponsort.
Volltext der Lizenz: creativecommons.org/licenses/by-sa/4.0/deed.de.
Terraform and the Terraform logo are trademarks of HashiCorp, Inc. (HashiCorp is an IBM company). OMNI52™ is a trademark of OMNI52 GmbH (filed, not yet registered). This website is operated by OMNI52 GmbH and is not affiliated with, endorsed by, or sponsored by HashiCorp, Inc. or IBM. “Terraform” is used in a nominative / descriptive sense to indicate the technology this cheatsheet documents.