DevOps & Automatisierung

Terraform oder Ansible: Welches Werkzeug du wofür einsetzt

Terraform erzeugt Infrastruktur, Ansible konfiguriert sie. Die beiden konkurrieren nicht, sie greifen ineinander. Wer Server mit Ansible provisioniert oder Software mit Terraform ausrollt, kämpft dauerhaft gegen das jeweilige Werkzeug.

5 Kapitel mit allen Befehlen
Team bespricht eine Deployment-Pipeline an einem großen Monitor
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Präsenz & Live-Online 4,9 aus 503 Google-Bewertungen Auch Inhouse für dein Team
Worum es geht

Warum die Abgrenzung in der Praxis so oft verschwimmt

Der typische Ausgangspunkt sieht so aus: Es gibt gewachsene Shell-Skripte, ein paar Ansible-Playbooks für die Paketinstallation und irgendwo eine Handvoll VMs, die jemand vor drei Jahren im Portal angeklickt hat. Sobald ein zweites Rechenzentrum, ein Cloud-Konto oder eine Staging-Umgebung dazukommt, reicht das nicht mehr. Reproduzierbarkeit heißt dann, dass Netzwerke, Subnetze, Security Groups, Load Balancer und Instanzen aus einer versionierten Beschreibung entstehen und nicht aus einem Ticketverlauf.

Genau hier liegt die Trennlinie. Terraform beziehungsweise der Fork OpenTofu beschreibt den Zielzustand von Ressourcen deklarativ, gleicht ihn über Provider-APIs ab und führt Buch darüber in einer State-Datei. Ansible ist prozedural aufgebaut, arbeitet agentenlos über SSH oder WinRM und bringt einen bereits existierenden Host in den gewünschten Konfigurationszustand: Pakete, systemd-Units, Dateien, Benutzer, Firewall-Regeln über nftables. Beide Werkzeuge können theoretisch das andere Feld mitbedienen, und genau daraus entstehen die Probleme.

Die typischen Fehler sind gut dokumentiert. VMs werden mit dem Ansible-Cloud-Modul angelegt, wodurch niemand mehr sauber erkennt, welche Ressourcen zum Stack gehören und ein Rückbau zur Handarbeit wird. Oder Terraform bekommt lange provisioner-Blöcke mit remote-exec, die bei jedem Fehlschlag den State inkonsistent hinterlassen. Dazu kommt die State-Datei im lokalen Verzeichnis oder im Git-Repository, ohne Remote-Backend und ohne State Locking, was bei zwei parallel laufenden Applies zu zerstörten Ressourcen führen kann.

Miniatur-Szene: Endlosschleife als Bahn mit Stationen für Build, Test und Deployment, am Ende eine startende Rakete
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Von der API bis zum Workload: Wer macht was

  1. 01 Terraform oder OpenTofu: Ressourcen provisionieren
  2. 02 Remote State mit Locking und Umgebungstrennung
  3. 03 Dynamisches Inventory aus Tags und Outputs
  4. 04 Ansible: Betriebssystem und Dienste konfigurieren
  5. 05 CI/CD: Plan, Review, Apply, Prüfung
  6. 06 GitOps: Workloads im Cluster abgleichen
Was du mitnimmst

So sieht eine saubere Aufgabenteilung aus

Die belastbare Antwort auf die Frage Terraform oder Ansible lautet in den meisten Umgebungen: beides, aber mit klar gezogener Grenze. Terraform oder OpenTofu erzeugt und verwaltet die Ressourcen, Ansible konfiguriert, was darin läuft. Diese Punkte bringen dich zu einem Aufbau, der auch nach zwei Jahren noch wartbar ist.

Grenze am Lebenszyklus ziehen

Alles, was eine Cloud- oder Hypervisor-API anlegt und löscht, gehört zu Terraform: Netze, Storage, Instanzen, DNS-Einträge, Kubernetes-Cluster, IAM-Rollen. Alles, was innerhalb eines laufenden Betriebssystems passiert, gehört zu Ansible. Diese eine Regel löst die meisten Streitfälle im Team.

State ernst nehmen

Der Terraform-State gehört in ein Remote-Backend mit Locking, etwa S3 mit DynamoDB, einen HTTP-Backend-Dienst im GitLab oder Consul. Dazu kommen getrennte States pro Umgebung, ein Umgang mit terraform import für bestehende Ressourcen und die Erkenntnis, dass Secrets im State im Klartext landen können und der Zugriff darauf entsprechend beschränkt werden muss.

Module und Rollen sauber schneiden

In Terraform kapselst du wiederkehrende Bausteine in Module mit klaren Variablen und Outputs statt in kopierte Verzeichnisse. In Ansible entspricht dem die Rolle mit definierten defaults, handlers und Molecule-Tests. In beiden Fällen gilt: kleine, versionierte Einheiten statt einer großen Datei, die niemand mehr anfasst.

Die Übergabe zwischen beiden Werkzeugen definieren

Der saubere Weg führt über ein dynamisches Inventory, das Ansible seine Hosts direkt aus Cloud-Tags oder aus Terraform-Outputs zieht, statt über hartkodierte IP-Listen. Alternativ übernimmt cloud-init nur das Minimum für den SSH-Zugang, und der eigentliche Konfigurationslauf startet danach als eigener Pipeline-Schritt.

Idempotenz prüfen statt annehmen

Ein terraform plan ohne Änderungen und ein zweiter Ansible-Lauf mit changed=0 sind die einzigen belastbaren Belege dafür, dass deine Automatisierung wirklich zustandsorientiert arbeitet. Wo command und shell ohne creates oder changed_when stehen, ist diese Eigenschaft meist schon verloren.

Beides in Pipeline und GitOps einhängen

Plan im Merge Request, Apply nach Review und Freigabe, dazu Policy-Prüfungen und ein Scan der Konfiguration vor dem Rollout. Für Kubernetes-Workloads übernimmt danach ein GitOps-Werkzeug wie Argo CD oder Flux, während Terraform den Cluster selbst und Ansible die Knoten außerhalb des Clusters verantwortet.

Tutorial

Vom leeren Repository bis zum sauberen Übergabepunkt

Hier baust du die Trennlinie einmal richtig auf: Repository-Schnitt, Remote-State mit Sperre, die Übergabe vom Terraform-Output an das Ansible-Inventar, der Idempotenz-Beweis und am Ende die Drift-Erkennung in der Pipeline. Die Reihenfolge entspricht der Reihenfolge, in der du es auch auf der grünen Wiese aufsetzen würdest.

01

Repository schneiden: zwei Ordner, eine Grenze

Die meisten Streitfälle lösen sich, sobald der Ordnerbaum die Grenze abbildet. Alles, was einen Erzeugungs- und einen Löschzeitpunkt hat, gehört nach Terraform. Alles, was einen dauerhaften Sollzustand beschreibt, gehört nach Ansible.

Ordnerstruktur eines gemischten Repositories
infra/
├── terraform/
│   ├── envs/
│   │   ├── prod/          # eigener State je Umgebung, nie ein gemeinsamer
│   │   └── stage/
│   └── modules/
│       └── app-node/      # nur Ressourcen, keine Softwarelogik
└── ansible/
    ├── inventories/
    │   └── prod/
    ├── roles/
    │   └── webserver/
    ├── requirements.yml
    └── site.yml

Getrennte Verzeichnisse je Umgebung sind wichtiger als getrennte Workspaces: Ein eigener State je Umgebung verhindert, dass ein falsch gesetzter Workspace einen Produktiv-Apply auslöst.

Die Grenze am Lebenszyklus, an konkreten Aufgaben durchgespielt

AufgabeWerkzeugWarum dort
Netz, Subnetz, Security GroupTerraformExistiert oder existiert nicht, klarer Lebenszyklus
Maschine erzeugen und wieder löschenTerraformDer State muss wissen, welche Instanz ihm gehört
Pakete, Dienste, KonfigurationsdateienAnsibleÄndert sich viel häufiger als die Maschine darunter
Zertifikate ausrollen und erneuernAnsibleWiederkehrender Sollzustand, kein einmaliger Erzeugungsakt
Benutzer und SSH-Schlüssel auf den HostsAnsibleMuss auch auf Maschinen wirken, die Terraform nie angefasst hat
Datenbankschema migrierenWeder nochGehört in die Pipeline der Anwendung, nicht in die Infrastruktur
02

State zuerst: remote ablegen und sperren

Bevor die erste Ressource entsteht, muss klar sein, wo der State liegt. Wer damit wartet, migriert später einen lokalen State, den inzwischen drei Leute in unterschiedlichen Ständen auf ihren Laptops haben.

terraform/envs/prod/backend.tf
terraform {
  required_version = ">= 1.6"

  backend "s3" {
    bucket         = "tf-state-prod"
    key            = "app-node/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "tf-locks"   # Sperre gegen parallele Läufe
  }
}

Die Sperre ist der eigentliche Punkt, nicht die Ablage. Ohne sie starten zwei Pipelines gleichzeitig ein apply und erzeugen Ressourcen, die anschließend in keinem State mehr auftauchen. Ab Terraform 1.10 kannst du die Sperre mit use_lockfile = true direkt im Bucket halten und dir die DynamoDB-Tabelle sparen.

Bestand aufnehmen, gezielt ersetzen, Fremdes übernehmen
terraform state list                       # was gehört diesem State überhaupt?
terraform state show 'aws_instance.node[0]'

# Eine einzelne Ressource neu bauen lassen, ersetzt das alte "terraform taint"
terraform apply -replace='module.app.aws_instance.node[0]'

# Von Hand angelegte Ressource nachträglich unter Verwaltung stellen
terraform import 'aws_instance.legacy' i-0abc123def456
terraform plan                             # muss danach "no changes" melden

Nach jedem import gilt: Solange terraform plan noch Änderungen zeigt, stimmt dein Code nicht mit der Realität überein. Erst der leere Plan beweist, dass die Übernahme sauber ist.

03

Die Übergabe: Terraform-Output wird zum Ansible-Inventar

Genau an dieser Stelle scheitern die meisten Aufbauten, weil jemand eine hosts-Datei von Hand pflegt. Sobald Terraform eine Maschine ersetzt, zeigt das Inventar auf eine IP-Adresse, die es nicht mehr gibt.

terraform/modules/app-node/main.tf, Tags als Gruppenquelle
resource "aws_instance" "node" {
  count         = var.node_count
  instance_type = var.instance_type
  ami           = var.ami_id

  tags = {
    Name = format("app-node-%02d", count.index + 1)
    env  = var.environment   # wird zur Filterbedingung
    role = "webserver"       # wird zum Gruppennamen im Inventar
  }
}

output "node_ids" {
  value = aws_instance.node[*].id
}

Tags sind hier kein Ordnungsmerkmal für die Rechnung, sondern die Schnittstelle. Lege die Namen einmal fest und dokumentiere sie, sonst erfindet jedes Modul eigene.

ansible/inventories/prod/aws_ec2.yml, dynamisches Inventar
plugin: amazon.aws.aws_ec2
regions:
  - eu-central-1
filters:
  tag:env: prod
  instance-state-name: running
keyed_groups:
  - key: tags.role
    prefix: role
    separator: "_"
hostnames:
  - private-ip-address

Prüfen mit ansible-inventory -i inventories/prod/aws_ec2.yml --graph. Alternativ liest das Inventar-Plugin cloud.terraform.terraform_provider direkt den State und braucht dafür keine Cloud-Berechtigungen, dafür aber Zugriff auf den State.

Werkzeuge sauber installieren statt aus der Distribution
# RHEL, Rocky, Alma liefern ansible-core über dnf mit
sudo dnf install -y ansible-core

# Auf Debian und Ubuntu hinkt das Paket der Collection-Welt oft hinterher,
# deshalb besser in eine eigene Umgebung
python3 -m venv ~/.venv/ansible
~/.venv/ansible/bin/pip install ansible-core ansible-lint
~/.venv/ansible/bin/ansible-galaxy collection install -r ansible/requirements.yml

Halte Collections in requirements.yml mit fester Version fest. Ein Playbook, das auf dem Laptop läuft und in der Pipeline nicht, hat fast immer unterschiedliche Collection-Stände als Ursache.

04

Idempotenz beweisen statt behaupten

Ein Playbook ist genau dann idempotent, wenn der zweite Lauf null Änderungen meldet. Das ist keine Stilfrage: Ohne diesen Nachweis kannst du echten Drift nicht mehr vom Eigenrauschen deiner Tasks unterscheiden.

Der Dreischritt vor jedem Produktivlauf
cd ansible

# 1. Trockenlauf auf einem einzelnen Host
ansible-playbook -i inventories/prod site.yml --check --diff --limit web01

# 2. Echter Lauf auf demselben Host
ansible-playbook -i inventories/prod site.yml --diff --limit web01

# 3. Sofort noch einmal: im Recap muss changed=0 stehen
ansible-playbook -i inventories/prod site.yml --diff --limit web01 | tail -n 5

Bleibt eine Task dauerhaft auf changed, steckt fast immer ein command oder shell ohne creates beziehungsweise ohne changed_when dahinter. Repariere das sofort, sonst gewöhnt sich das Team an gelbe Zeilen und übersieht die eine, die zählt.

Bevor das erste Mal produktiv gelaufen wird

  • Der Plan ist leer, bevor Ansible startetEin offener Terraform-Plan bedeutet, dass Ansible gegen Maschinen arbeitet, die gleich ersetzt werden. Die Konfiguration ist danach spurlos weg und niemand versteht warum.
  • Jede command- und shell-Task hat creates, removes oder changed_whenSonst meldet jeder Lauf Änderungen. Damit verlierst du genau die Information, wegen der du Ansible eingeführt hast.
  • Neustarts laufen ausschließlich über Handler mit notifyEin bedingungsloser Restart-Task wirft bei jedem Lauf bestehende Verbindungen weg, obwohl sich nichts geändert hat.
  • terraform fmt, terraform validate und ansible-lint laufen in der PipelineLokal installierte Versionen driften auseinander. Die Pipeline ist die einzige Instanz, die für alle dieselbe ist.
  • Der State liegt remote und ist gesperrtZwei parallele Applies auf getrennten lokalen States erzeugen verwaiste Ressourcen, die anschließend niemand mehr zuordnen kann und die trotzdem Geld kosten.
05

Drift erkennen und beides in die Pipeline hängen

Der Wert der Trennung zeigt sich erst, wenn du beide Seiten regelmäßig gegen die Realität prüfst. Terraform kann das von Haus aus über einen Exit-Code, Ansible nicht, das musst du selbst bauen.

ci/drift-check.sh
#!/usr/bin/env bash
set -euo pipefail

cd terraform/envs/prod
terraform init -input=false -backend=true

set +e
terraform plan -detailed-exitcode -input=false -lock-timeout=5m
rc=$?
set -e

case "$rc" in
  0) echo "Infrastruktur: kein Drift" ;;
  2) echo "Infrastruktur: Drift erkannt"; exit 1 ;;
  *) echo "Infrastruktur: Plan fehlgeschlagen"; exit "$rc" ;;
esac

-detailed-exitcode liefert 0 für keine Änderung, 2 für vorhandene Änderungen und 1 für einen Fehler. Genau deshalb muss set -e um den Aufruf herum kurz ausgeschaltet werden, sonst bricht das Skript bei erkanntem Drift ab, bevor du ihn melden kannst.

Konfigurations-Drift auf der Ansible-Seite
#!/usr/bin/env bash
set -euo pipefail

cd ansible
ansible-playbook -i inventories/prod site.yml --check --diff | tee /tmp/drift.log

# Ansible liefert im Check-Modus keinen eigenen Exit-Code für Änderungen,
# also wird der Recap ausgewertet
if grep -qE 'changed=[1-9]' /tmp/drift.log; then
  echo "Konfigurations-Drift gefunden"
  exit 1
fi

Als nächtlicher Lauf gegen die Produktion ist das die billigste Frühwarnung, die du bekommen kannst. Sie findet die Handänderung um zwei Uhr nachts, die niemand mehr erwähnt hat.

Wissen prüfen

Teste dich und finde deinen Weg

Bevor du einen Kurs buchst, lohnt sich eine ehrliche Standortbestimmung. Die Tests sind kostenlos und ohne Anmeldung.

Gut zu wissen

Häufige Fragen zu Terraform und Ansible

Noch etwas offen? Wir sind ohne Warteschleife für dich da.

Frag uns direkt
Kann ich Terraform komplett durch Ansible ersetzen?
Technisch ja, denn Ansible bringt Module für die großen Cloud-Provider und für Proxmox oder VMware mit. Praktisch verlierst du dabei den verwalteten Zustand: Ansible weiß nicht, welche Ressourcen es früher einmal angelegt hat, kennt also keinen Plan vor der Ausführung und kein sauberes Löschen einer kompletten Umgebung. Für reine Provisionierung ist ein deklaratives Werkzeug mit State deutlich robuster.
Kann ich Ansible komplett durch Terraform ersetzen?
Nur wenn du konsequent auf unveränderliche Systeme setzt, also fertige Images baust und Server bei jeder Änderung neu ausrollst statt sie anzupassen. Sobald du bestehende Hosts patchen, Konfigurationsdateien pflegen oder ad hoc auf hunderten Systemen etwas prüfen musst, ist ein Konfigurationsmanagement der passendere Weg. Die provisioner-Blöcke in Terraform sind ausdrücklich als letztes Mittel gedacht.
Was bedeutet die Aufspaltung in Terraform und OpenTofu für meine Entscheidung?
Nach dem Lizenzwechsel von HashiCorp entstand OpenTofu als Fork unter der Apache-Lizenz, der von der Linux Foundation getragen wird. Die Sprache HCL und der Aufbau der Konfiguration sind weitgehend kompatibel, sodass sich das erlernte Wissen übertragen lässt. Prüfen solltest du vor allem die Verfügbarkeit der Provider, die Registry-Anbindung und die Lizenzlage in deinem Unternehmen.
Brauche ich Ansible noch, wenn ich alles auf Kubernetes betreibe?
Für die Workloads im Cluster übernehmen Helm und ein GitOps-Werkzeug wie Argo CD oder Flux diese Aufgabe. Die Knoten darunter, Bastion-Hosts, Storage-Systeme, Netzwerkgeräte und alles außerhalb des Clusters bleiben aber klassische Systeme, die konfiguriert und gepatcht werden müssen. In den meisten Umgebungen bleibt Ansible daher im Einsatz, nur mit kleinerem Geltungsbereich.
Womit fange ich an, wenn heute noch alles über Shell-Skripte läuft?
Beginne mit dem Werkzeug, das deinen größten aktuellen Schmerz löst. Wenn du Server von Hand anlegst und Umgebungen auseinanderlaufen, ist Terraform der erste Schritt. Wenn die Server ohnehin stehen, aber Konfigurationen driften und Patchläufe manuell ablaufen, fängst du mit Ansible an. Der zweite Baustein folgt dann meist innerhalb weniger Monate.

Zuletzt geprüft am 26. Juli 2026.

Das sagen Teilnehmer

Echte Stimmen aus unseren IT-Kursen

Qualitativ sehr guter Kurs. Ruhiger und wertschätzender Umgang. Keine Informationsüberlastung.
Rückmeldung aus dem Kurs „Ansible Kompaktkurs“
Die Schulung war genau das richtige, um mein Verständnis zu erweitern. Vielen Dank an den Trainer!
Rückmeldung aus dem Kurs „Linux Grundkurs (LPI01)“
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Linux-Programm den passenden Kurs oder Lernpfad zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die exakt auf eure Systemlandschaft und Distributionen zugeschnitten sind.

Nächster Schritt

Automatisierung im Kurs sauber aufsetzen

Bei cmt kannst du beide Seiten lernen: den Ansible Kompaktkurs für den Einstieg ins Konfigurationsmanagement, den Kurs Ansible für Fortgeschrittene gegen Playbook-Chaos sowie die Terraform-Kurse für AWS und Azure von der Einführung bis zum Aufbaukurs. Wenn du Infrastructure as Code direkt mit GitOps verbinden willst, schau dir den Kurs zu Terraform und GitOps an. Alle Termine gibt es Live-Online oder als Inhouse-Schulung. Sag uns kurz, wie deine Umgebung aussieht, dann sortieren wir gemeinsam die passende Reihenfolge.