Shell & Automatisierung

Bash-Skript oder Ansible: wo die Grenze verläuft

Für einmalige Abläufe auf einem Host reicht Bash, für wiederkehrende Zustände auf vielen Hosts lohnt Ansible. Die Grenze verläuft bei der Frage, ob dein Skript ein zweites Mal ohne Schaden laufen darf.

5 Kapitel mit allen Befehlen
Hände tippen auf einer mechanischen Tastatur, Terminal-Fenster auf dem 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 Frage in fast jedem Team irgendwann auftaucht

Am Anfang steht meistens ein kleines Skript. Es legt einen Benutzer an, kopiert eine Konfigurationsdatei nach /etc, startet einen Dienst neu und schreibt eine Zeile ins Log. Das funktioniert, solange es um einen Server geht und solange derjenige, der es geschrieben hat, noch im Haus ist. Sobald zehn oder zwanzig Systeme dazukommen, wächst um dieses Skript herum eine Sammlung aus Schleifen über Hostlisten, ssh-Aufrufen, scp-Kopien und Sonderfällen für Debian und RHEL.

Der eigentliche Bruch kommt nicht durch die Anzahl der Server, sondern durch die Wiederholung. Ein typisches Bash-Skript beschreibt Schritte, kein Zielbild. Ein zweiter Durchlauf hängt deshalb eine Zeile erneut an eine Konfigurationsdatei an, ein abgebrochener Lauf hinterlässt einen halb konfigurierten Host, und niemand kann verlässlich sagen, welcher Server gerade in welchem Zustand ist. Fehlerbehandlung mit set -euo pipefail, trap und sauberen Rückgabewerten ist möglich, aber sie muss in jedem Skript neu gebaut werden.

Typische Fehler gehen in beide Richtungen. Manche Teams schleppen jahrelang gewachsene Skriptsammlungen mit, in denen sed-Aufrufe auf Konfigurationsdateien die eigentliche Logik sind. Andere führen Ansible ein und schreiben dann Playbooks, die im Wesentlichen aus command- und shell-Tasks bestehen. Beides kostet mehr, als es bringt. Die Entscheidung sollte an der Aufgabe hängen, nicht an der Vorliebe.

Miniatur-Szene: Förderband aus Terminal-Fenstern, Zahnräder treiben es an, ein Roboterarm setzt einen Haken
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Entscheidungskriterien im Überblick

  1. 01 Einmalig oder wiederkehrend
  2. 02 Ein Host oder viele Hosts
  3. 03 Ablauf oder Sollzustand
  4. 04 Eine Distribution oder gemischt
  5. 05 Wer pflegt es in zwei Jahren
Was du mitnimmst

Die Kriterien, an denen du die Entscheidung festmachst

Es gibt keinen festen Schwellenwert an Servern, ab dem Ansible zwingend wird. Es gibt aber ein paar Eigenschaften einer Aufgabe, die ziemlich zuverlässig zeigen, in welche Richtung du gehen solltest.

Einmaliger Ablauf oder dauerhafter Sollzustand

Ein Backup-Job, eine Auswertung von Logdateien oder eine Migration, die genau einmal läuft, ist in Bash gut aufgehoben. Sobald du einen Zustand beschreibst, der über Monate gleich bleiben soll, etwa installierte Pakete, Benutzer, sudo-Regeln und Dienste, gewinnt ein deklaratives Werkzeug.

Idempotenz, konkret gedacht

Idempotent heißt: Der zweite Lauf ändert nichts mehr. Ansible-Module wie lineinfile, template, package oder service prüfen den Ist-Zustand und melden changed oder ok zurück. In Bash musst du das mit grep -q, Prüfungen auf Dateiexistenz und Vergleichen selbst nachbauen, und genau diese Prüfungen fehlen erfahrungsgemäß zuerst.

Rückmeldung und Nachvollziehbarkeit

Ansible liefert dir pro Host und Task ein Ergebnis, kennt --check und --diff für einen Trockenlauf und bricht bei Fehlern kontrolliert ab. Ein Skript sagt dir im Zweifel nur, dass es fertig ist. Wenn du gegenüber Audit oder Betrieb belegen musst, was auf welchem System passiert ist, ist das ein starkes Argument.

Heterogene Systeme und Wiederverwendung

Sobald du Debian, RHEL und SLES gleichzeitig bedienst, sammeln sich in Bash Fallunterscheidungen für apt, dnf und zypper. Ansible abstrahiert das über Module und Facts, und Rollen lassen sich mit Variablen und Templates für mehrere Umgebungen wiederverwenden, statt für jede Umgebung eine Skriptkopie zu pflegen.

Der Weg dorthin ist kein Bruch

Du musst nichts wegwerfen. Ein sinnvoller erster Schritt ist ein Inventory mit Gruppen, dann eine Rolle für den Standard-Grundzustand eines Servers, danach ziehst du Skript für Skript nach. Bestehende Skripte kannst du übergangsweise über das script-Modul einbinden und später durch echte Module ersetzen.

Bash bleibt trotzdem Pflicht

Auch mit Ansible schreibst du weiter Shell-Code, in Jinja2-Templates, in kleinen Helfern, in systemd-Units und in CI-Pipelines. Wer Umleitungen, Quoting, Exit-Codes und Prozessverwaltung nicht sicher beherrscht, produziert auch in Playbooks unsaubere Tasks.

Tutorial

Vom gewachsenen Skript zum überprüfbaren Sollzustand

Der Weg führt nicht über einen Bruch, sondern über vier Schritte: das vorhandene Skript härten, seine Wiederholbarkeit ehrlich testen, die Bausteine einzeln übersetzen und den Idempotenz-Beweis führen. Zum Schluss steht, wo Bash auch im Playbook seinen Platz behält, denn ganz verschwindet es nie.

01

Erst das Bash-Skript reparieren

Bevor du migrierst, bring das Skript auf einen Stand, an dem sich sein Verhalten überhaupt beurteilen lässt. Oft stellt sich dabei heraus, dass die Aufgabe für Bash völlig in Ordnung war und nur die Umsetzung nicht.

Der Kopf, den jedes ernsthafte Skript braucht
#!/usr/bin/env bash
set -euo pipefail

log() { printf '%s %s\n' "$(date -Is)" "$*" >&2; }

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT      # räumt auch bei Abbruch auf

# Nur ein Lauf gleichzeitig, sonst zerlegen sich zwei Cron-Jobs gegenseitig
exec 9>/var/lock/deploy.lock
if ! flock -n 9; then
  log "läuft bereits, breche ab"
  exit 0
fi

set -e greift nicht in if-Bedingungen, nicht in &&-Ketten und nicht bei Funktionen, deren Rückgabewert geprüft wird. Verlass dich also nicht blind darauf. Die Sperre über flock ist der billigste Schutz gegen den Klassiker, dass ein langer Lauf noch aktiv ist, wenn der nächste startet.

Prüfen, bevor es auf einen Server kommt
bash -n deploy.sh              # reine Syntaxprüfung ohne Ausführung
shellcheck -S style deploy.sh  # findet Quoting-Fehler und set -e-Fallen
shfmt -d -i 2 deploy.sh        # zeigt Formatabweichungen als Diff

shellcheck gehört in die Pipeline, nicht in den guten Vorsatz. Es findet genau die Stellen, an denen set -e nicht greift, und es findet unquotierte Variablen, die bei Leerzeichen im Pfad zuschlagen.

02

Wiederholbarkeit testen, bevor du migrierst

Ob ein Skript wiederholbar ist, erkennst du nicht beim Lesen, sondern erst beim zweiten Ausführen. Der Test kostet eine Wegwerf-VM und zeigt dir, ob eine Migration überhaupt nötig ist.

Der einzige Test, der zählt
# Auf einem Wegwerf-System, nicht auf der Produktion
sudo ./deploy.sh                                 # erster Lauf
sudo rpm -Va > /tmp/nach-lauf1.txt || true       # RHEL, Rocky, Alma
# Debian, Ubuntu: sudo debsums -ac > /tmp/nach-lauf1.txt || true

sudo ./deploy.sh                                 # zweiter Lauf, direkt danach
sudo rpm -Va > /tmp/nach-lauf2.txt || true

# Leerer Diff: der zweite Lauf hat an paketierten Dateien nichts verändert
diff -u /tmp/nach-lauf1.txt /tmp/nach-lauf2.txt

rpm -Va und debsums -ac listen Dateien, die von ihrem Paketzustand abweichen, und enden mit einem Exit-Code ungleich null, sobald sie etwas finden, daher das || true. Der Vergleich nach dem ersten und dem zweiten Lauf zeigt, ob die Wiederholung noch etwas verändert, etwa eine doppelt angehängte Zeile in einer Konfigurationsdatei. Neu angelegte Dateien außerhalb von Paketen erfasst der Test nicht, die findest du zusätzlich mit find /etc -newer und einer vorher angelegten Referenzdatei.

Woran du erkennst, dass Bash die Aufgabe nicht mehr trägt

  • Das Skript hängt irgendwo mit >> etwas an eine Konfigurationsdatei anBeim zweiten Lauf steht der Eintrag doppelt drin. Genau für diesen Fall gibt es lineinfile und template.
  • Es gibt eine Kopie je Distribution, je Kunde oder je UmgebungDie Varianten driften auseinander, und eine Fehlerbehebung landet nur in einer davon. Variablen und Rollen lösen das, Copy-and-paste nicht.
  • Vor jedem Lauf prüft jemand von Hand, ob das Skript schon gelaufen istEin Ablauf, der nicht wiederholbar ist, kann auch nicht automatisch nachgezogen werden. Damit fällt jede Form von Selbstheilung weg.
  • Das Skript läuft in einer SSH-Schleife über eine fest eingetragene HostlisteEs gibt keinen Bericht, welcher Host durchgelaufen ist. Ein Abbruch in der Mitte hinterlässt einen halb konfigurierten Bestand, den niemand mehr sauber identifiziert.
  • Ein Rollback existiert nur als Wissen im Kopf einer PersonSteht der Sollzustand in Git, ist der letzte funktionierende Stand ein git revert und ein Lauf entfernt. Bei Skripten ist er eine Rekonstruktion aus dem Gedächtnis.
03

Baustein für Baustein übersetzen

Migriere nicht das ganze Skript auf einmal. Nimm dir die Blöcke einzeln vor, jeder wird zu einer Task, und die Übersetzung ist fast immer mechanisch. Wie du Inventory, ansible.cfg und Rollen von Grund auf anlegst, zeigt der Ansible-Einstieg für Admins.

Die mechanische Übersetzung, Zeile für Zeile

Bash-KonstruktAnsible-ModulWas du dabei gewinnst
dnf install -y nginxansible.builtin.packageLäuft auf apt und dnf gleichermaßen, meldet nur beim ersten Mal changed
echo '...' >> /etc/sysctl.confansible.builtin.lineinfileKein doppelter Eintrag beim zweiten Lauf
cp app.conf /etc/app/ansible.builtin.copy mit validateEine ungültige Konfiguration wird gar nicht erst geschrieben
sed -i 's/alt/neu/' dateiansible.builtin.replaceZeigt im --diff genau, was sich ändert, vor dem Schreiben
useradd -m -s /bin/bash appansible.builtin.userBeim zweiten Lauf kein Fehler, weil der Benutzer schon existiert
mkdir -p /srv/app && chown app:ansible.builtin.fileEine Task statt mehrerer Befehle, inklusive Rechten und SELinux-Kontext
systemctl restart nginxHandler über notifyNeustart nur dann, wenn sich wirklich etwas geändert hat
roles/webserver/tasks/main.yml
- name: Paket installieren
  ansible.builtin.package:
    name: nginx
    state: present

- name: Konfiguration ausrollen
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: "0644"
    validate: "nginx -t -c %s"
  notify: nginx neu laden

- name: Dienst aktivieren und starten
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

validate ist der Unterschied zwischen einem Ausfall und einem sauber abgebrochenen Task: Ansible schreibt zuerst eine temporäre Kopie, ruft den Prüfbefehl mit %s darauf auf und verschiebt sie erst nach Erfolg. Es gibt das für fast jeden Dienst, etwa visudo -cf %s, sshd -t -f %s oder named-checkconf %s.

roles/webserver/handlers/main.yml
- name: nginx neu laden
  ansible.builtin.service:
    name: nginx
    state: reloaded

Handler laufen am Ende des Plays und nur einmal, egal wie viele Tasks sie benachrichtigt haben. Brauchst du den Neustart früher, weil eine spätere Task darauf aufbaut, erzwingst du ihn mit ansible.builtin.meta: flush_handlers.

04

Den Beweis führen: Trockenlauf, Diff und der zweite Lauf

Ein Playbook gilt erst dann als fertig, wenn der zweite Lauf null Änderungen meldet. Alles andere ist ein Skript in YAML-Schreibweise.

Der Ablauf für jede neue Rolle
cd ansible
ansible-lint roles/ site.yml

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

# Echter Lauf
ansible-playbook -i inventories/stage site.yml --diff --limit web01

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

Erst wenn der letzte Lauf changed=0 liefert, rollst du auf weitere Hosts aus. Bei größeren Beständen dann in Wellen über serial: 25% im Play, damit ein Fehler nicht sofort alle Systeme erwischt.

05

Wo Bash im Playbook seinen Platz behält

Für manche Aufgaben gibt es kein Modul, und dann ist ein Aufruf über command oder shell die richtige Wahl. Du musst ihm nur die Idempotenz beibringen, die das Modul sonst mitgebracht hätte.

Fremdbefehle idempotent einbinden
- name: Einmalige Initialisierung
  ansible.builtin.command:
    cmd: /opt/app/bin/init-db
    creates: /var/lib/app/.initialized     # macht die Task idempotent

- name: Version auslesen, ändert nichts
  ansible.builtin.command:
    cmd: /opt/app/bin/app --version
  register: app_version
  changed_when: false
  check_mode: false

- name: Export, braucht ausdrücklich bash
  ansible.builtin.shell:
    cmd: |
      set -euo pipefail
      /opt/app/bin/export > /var/backups/app.dump
    executable: /bin/bash
  changed_when: true

creates und removes sind die einfachste Form von Idempotenz: Ansible prüft die Datei und überspringt den Befehl, wenn der Zustand schon erreicht ist. Für alles andere entscheidest du über changed_when anhand von rc oder stdout, wann ein Lauf als Änderung zählt.

ansible/ansible.cfg
[defaults]
inventory      = inventories/prod
forks          = 20
host_key_checking = true

[ssh_connection]
pipelining = true
ssh_args   = -o ControlMaster=auto -o ControlPersist=60s

Die Voreinstellung von forks ist 5, das bremst bereits ab einer Handvoll Hosts spürbar. pipelining spart je Task eine SSH-Runde und ist der größte Einzelgewinn bei langen Playbooks. Voraussetzung ist, dass in der sudoers-Konfiguration der Zielsysteme requiretty nicht gesetzt ist.

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.

Passender Lernpfad

Karrierepfad

Dein Weg zum Linux Junior Administrator (LPIC-1)

Du willst Linux im Arbeitsalltag sicher bedienen, Systeme zuverlässig verwalten und dich gezielt auf die LPIC-1 Prüfung vorbereiten? In diesem Lernpfad baust du Schritt für Schritt das Wissen auf, das du als Linux Junior Administrator brauchst: von der Kommandozeile über Benutzer- und Rechteverwaltung bis zu Netzwerkgrundlagen und Systemdiensten.

Aus unseren Kursen

Was Teilnehmende über unsere IT-Kurse sagen

Qualitativ sehr guter Kurs. Ruhiger und wertschätzender Umgang. Keine Informationsüberlastung.
Rückmeldung aus dem Kurs „Ansible Kompaktkurs“
Gut zu wissen

Häufige Fragen zu Automatisierung mit Bash und Ansible

Ist deine Frage nicht dabei? Ruf an oder schreib uns, du sprichst direkt mit jemandem aus der Kursberatung.

Frag uns direkt
Ab wie vielen Servern lohnt sich Ansible?
Eine harte Grenze gibt es nicht, als Faustregel wird es ab etwa fünf bis zehn dauerhaft gepflegten Systemen deutlich günstiger, weil sich Inventory und Rollen dann über mehrere Hosts amortisieren. Wichtiger als die Zahl ist die Frage, wie oft dieselbe Konfiguration erneut hergestellt werden muss. Wer einen einzigen Server hat, ihn aber regelmäßig neu aufsetzt, profitiert ebenfalls sofort.
Was bedeutet Idempotenz bei Ansible praktisch?
Idempotenz bedeutet, dass ein Playbook beliebig oft laufen kann und nach dem ersten erfolgreichen Durchlauf keine Änderungen mehr vornimmt. Ansible erreicht das, indem die Module den aktuellen Zustand prüfen, bevor sie schreiben, und im Ergebnis ok statt changed melden. Sobald du command- oder shell-Tasks einsetzt, verlierst du diese Eigenschaft, außer du sicherst sie mit creates, removes oder changed_when selbst ab.
Braucht Ansible einen Agenten auf den Zielsystemen?
Nein, Ansible arbeitet agentenlos über SSH und benötigt auf dem Ziel im Normalfall nur einen Python-Interpreter. Das macht die Einführung in bestehenden Umgebungen einfach, weil du keine zusätzliche Software ausrollen und keinen weiteren Dienst betreiben musst. Für Windows-Ziele läuft die Verbindung stattdessen über WinRM oder SSH.
Ist Ansible langsamer als ein Shell-Skript?
Bei kleinen Aufgaben auf wenigen Hosts ist ein direktes Skript in der Regel schneller, weil Ansible pro Task Verbindungen aufbaut und Module überträgt. Mit steigender Hostzahl dreht sich das Bild, weil Ansible parallel arbeitet und die Parallelität über forks steuerbar ist. Zusätzlich lassen sich SSH-Pipelining und persistente Verbindungen aktivieren, was den Overhead spürbar senkt.
Welcher cmt-Kurs passt zu welcher Seite der Entscheidung?
Für die Shell-Seite bilden der Kurs Linux / UNIX Shellprogrammierung Grundkurs und der Linux Aufbaukurs: Shell Programmierung (LPI03) die Grundlage. Für den Einstieg ins Konfigurationsmanagement passt der Ansible Kompaktkurs, für gewachsene Setups Ansible für Fortgeschrittene: Kein Playbook-Chaos. Wer im Red-Hat-Umfeld arbeitet, findet mit AU294 Red Hat Enterprise Linux Automation with Ansible und mit Configuration Management und Service Operations mit Ansible (DO1) den passenden Rahmen.

Zuletzt geprüft am 05. Oktober 2026.

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

Du willst die Entscheidung nicht allein treffen?

Sag uns, wie viele Systeme du betreust und was heute schon skriptgesteuert läuft. Wir sagen dir ehrlich, ob ein Shell-Kurs oder der Einstieg in Ansible der sinnvollere nächste Schritt ist. Alle Kurse finden in Präsenz oder Live-Online statt, auf Wunsch auch als Firmenschulung mit deinen eigenen Playbooks und Skripten.