Shell & Automatisierung

Bash-Skript oder Ansible: So entscheidest du richtig

Einmalige Aufgaben auf einem Host: Bash. Wiederkehrende Zustände auf vielen Hosts: Ansible. Die Grenze verläuft nicht bei der Zeilenzahl, sondern 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

Die Entscheidung fällt nicht an der Zeilenzahl. Sie fällt an der Frage, ob dein Skript ein zweites Mal ohne Schaden laufen darf, und die beantwortest du durch Ausführen, nicht durch Lesen.

Der einzige Test, der zählt
# Auf einem Wegwerf-System, nicht auf der Produktion
sudo ./deploy.sh
sudo ./deploy.sh                       # zweiter Lauf, direkt danach

# Und danach: hat sich am System noch etwas verändert?
sudo rpm -Va > /tmp/nachher.txt        # RHEL, Rocky, Alma
sudo debsums -c                        # Debian, Ubuntu
diff -u /tmp/vorher.txt /tmp/nachher.txt

rpm -Va und debsums -c zeigen dir Dateien, die von ihrem Paketzustand abweichen. Das ist eine grobe, aber ehrliche Momentaufnahme davon, was dein Skript tatsächlich am System verändert hat, und deckt regelmäßig Nebenwirkungen auf, die niemand beabsichtigt hatte.

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.

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.fileEin Task statt drei, 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 dritte Befehl 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.

Gut zu wissen

Häufige Fragen zu Automatisierung mit Bash und Ansible

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

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 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“
Trainer gut und verständlich. Unterlagen nur zum lesen (keine Kopierfunktion). Wissenstransfer erfolgreich.
Rückmeldung aus dem Kurs „SUSE Linux Enterprise 15 High Availability Deployment – HAE311v15“
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.