Worum es in diesem Teil wirklich geht
Der erste Ansible-Beitrag hat Installation, Inventory und ein erstes Playbook eingeführt. Dieser Teil macht daraus eine belastbare Betriebsstruktur: ein Repository, das Hosts, Gruppen, Variablen, Rollen, Secrets, Tests und Ausführung so trennt, dass Änderungen reviewbar und wiederholbar bleiben.
Das Ziel ist nicht ein besonders großes Beispiel, sondern ein Muster, das in einem Lab beginnt und später in Test, Produktion, AWX oder Red Hat Ansible Automation Platform weiterleben kann. Ein Techniker soll nach diesem Beitrag wissen, wo welche Datei liegt, warum sie dort liegt und wie man vor produktiven Änderungen prüft, was passieren würde.
Zielbild: Repository als Betriebsvertrag
Ein brauchbares Ansible-Repository ist mehr als eine Sammlung von Playbooks. Es ist der Vertrag zwischen Architektur, Betrieb und Review: Welche Systeme gibt es? Welche Gruppenlogik gilt? Welche Werte sind global, welche umgebungsspezifisch, welche hostbezogen? Welche Rolle setzt welchen Zustand? Welche Secrets sind verschlüsselt? Und welche Befehle gelten als technische Abnahme?
Control Node, CI oder AWX|| liest ansible.cfgvInventory pro Umgebung -> group_vars / host_vars / vault|| ruft Playbooks aufvRollen mit Defaults, Tasks, Handlern und Templates|| setzen Zustände idempotentvManaged Nodes: SLES, Rocky, Alma, Debian, Ubuntu, Netzwerkdienste
- Inventories beantworten: Auf welche Systeme wirkt ein Lauf?
- Variablen beantworten: Mit welchen Werten wird ein Zustand berechnet?
- Rollen beantworten: Welche wiederverwendbare Betriebslogik wird angewendet?
- Vault beantwortet: Welche sensiblen Werte dürfen im Repository liegen und trotzdem nicht im Klartext stehen?
- Check Mode, Diff Mode und Idempotenzlauf beantworten: Ist die Änderung nachvollziehbar und wiederholbar?
YAML oder YAML: was ist der Unterschied?
Technisch beschreiben .yaml und .yaml dasselbe Dateiformat. .yaml ist historisch aus Umgebungen entstanden, in denen Dateiendungen kurz gehalten wurden. Heute ist .yaml meist die klarere Schreibweise, weil sie den Formatnamen vollständig ausdrückt. Ansible akzeptiert beide Varianten.
Für Teams ist nicht entscheidend, welche Endung „richtiger“ ist, sondern dass sie im Repository einheitlich verwendet wird. Wir schreiben in Beispielen bewusst .yaml beziehungsweise in Codeblöcken language: yaml, auch wenn manche Ansible-Defaults und viele bestehende Rollen weiterhin main.yaml, site.yaml oder requirements.yaml heißen. In bestehenden Ansible-Konventionen bleibt .yaml völlig legitim; neue ForgeOne-Beispiele bevorzugen .yaml, außer ein etabliertes Tool erwartet konkret .yaml.
Repository-Struktur mit klaren Grenzen
Die Struktur sollte Umgebungen, Playbooks, Rollen, Collections und Secrets trennen. Dadurch können Rollen unverändert gegen Lab und Produktion laufen, während Hostlisten, Gruppenwerte und Vault-Dateien je Umgebung getrennt bleiben.
ansible-platform/├── ansible.cfg├── inventories/│ ├── lab/│ │ ├── hosts.yaml│ │ ├── group_vars/│ │ │ ├── all.yaml│ │ │ ├── linux.yaml│ │ │ └── vault.yaml│ │ └── host_vars/│ │ └── sles16-client01.example.test.yaml│ └── prod/│ ├── hosts.yaml│ ├── group_vars/│ └── host_vars/├── playbooks/│ ├── baseline.yaml│ └── patch-window.yaml├── roles/│ └── linux_baseline/│ ├── defaults/main.yaml│ ├── tasks/main.yaml│ ├── handlers/main.yaml│ └── templates/issue.j2└── collections/└── requirements.yaml
Wichtig ist die Richtung der Abhängigkeit: Playbooks rufen Rollen auf. Rollen kennen Defaults und Templates. Inventories liefern Zielsysteme und Werte. Secrets liegen verschlüsselt. Rollen sollten möglichst nicht wissen, ob sie gerade in Lab oder Produktion laufen; diese Entscheidung kommt aus Inventory und Variablen.
ansible.cfg: Verhalten projektweit fixieren
Ohne projektlokale ansible.cfg hängt ein Lauf zu stark von Benutzerprofil, Systemkonfiguration und Shell-Umgebung ab. Für Teamarbeit und CI sollte die relevante Konfiguration im Repository liegen.
[defaults]inventory = inventories/lab/hosts.yamlroles_path = rolescollections_path = .ansible/collectionsinterpreter_python = auto_silenthost_key_checking = Trueretry_files_enabled = Falsestdout_callback = yaml[ssh_connection]ssh_args = -o ControlMaster=auto -o ControlPersist=60spipelining = Truetimeout = 30[privilege_escalation]become = Truebecome_method = sudo
ansible-config viewansible-config dump --only-changed
ansible-config view zeigt, welche Konfiguration Ansible tatsächlich lädt. ansible-config dump --only-changed macht sichtbar, welche Werte vom Standard abweichen. Das ist nützlich, wenn ein Lauf lokal anders reagiert als im CI-Runner oder in AWX.
Inventory nach Umgebung, Distribution und Funktion
Ein Inventory sollte nicht nur Servernamen sammeln. Es beschreibt, welche Systeme zu welcher Umgebung, Distribution und Betriebsfunktion gehören. Ein Host kann in mehreren Gruppen liegen: zum Beispiel linux, suse und monitoring_targets.
all:children:linux:children:suse:hosts:sles16-client01.example.test:ansible_host: 172.31.52.21rhel_family:hosts:rocky10-client01.example.test:ansible_host: 172.31.52.31monitoring_targets:hosts:sles16-client01.example.test:rocky10-client01.example.test:vars:ansible_user: labadminansible_python_interpreter: /usr/bin/python3
Die Gruppen suse und rhel_family beschreiben technische Unterschiede. monitoring_targets beschreibt eine Betriebsfunktion. Dadurch kann dieselbe Rolle später gezielt nur gegen SLES-Systeme, gegen alle Linux-Systeme oder gegen alle Monitoring-Ziele laufen.
Variablenmodell: wenige Orte, klare Regeln
Ansible kennt sehr viele Variablenquellen. In der Praxis sollte man bewusst wenige verwenden: role defaults für überschreibbare Standardwerte, group_vars/all für globale Konventionen, group_vars/<gruppe> für Gruppenlogik, host_vars für echte Ausnahmen und Vault für Secrets.
# inventories/lab/group_vars/all.yamltimezone: Europe/Viennabaseline_owner: platform-team# inventories/lab/group_vars/linux.yamllinux_baseline_packages:- chrony- vim- curllinux_baseline_services:- chronyd# inventories/lab/host_vars/sles16-client01.example.test.yamllinux_baseline_packages:- chrony- vim- curl- supportutils
Host-Variablen sollten Ausnahmen bleiben. Wenn viele Hosts dieselbe Ausnahme brauchen, ist meistens eine neue Gruppe sinnvoller. Das hält Reviews lesbar und verhindert, dass Konfiguration in vielen host_vars-Dateien versteckt wird.
Rollen statt wachsender Einzelplaybooks
Ein Playbook sollte orchestrieren, nicht alle Details enthalten. Rollen kapseln wiederverwendbare Betriebslogik. Das Playbook entscheidet dann nur, welche Rolle auf welche Zielgruppe angewendet wird.
# playbooks/baseline.yaml---- name: Linux baseline ausrollenhosts: linuxbecome: truegather_facts: trueroles:- role: linux_baseline
# roles/linux_baseline/tasks/main.yaml---- name: Basispakete installierenansible.builtin.package:name: "{{ linux_baseline_packages }}"state: present- name: Baseline-Dienste aktivierenansible.builtin.service:name: "{{ item }}"enabled: truestate: startedloop: "{{ linux_baseline_services }}"- name: Betriebsnotiz schreibenansible.builtin.template:src: issue.j2dest: /etc/issue.d/forgeone-baseline.issueowner: rootgroup: rootmode: '0644'notify: Baseline-Hinweis geändert
# roles/linux_baseline/handlers/main.yaml---- name: Baseline-Hinweis geändertansible.builtin.debug:msg: "Baseline-Hinweis wurde auf {{ inventory_hostname }} aktualisiert."
Das wirkt klein, ist aber der entscheidende Schritt: Ab jetzt kann dieselbe Rolle in mehreren Umgebungen laufen, mit unterschiedlichen Variablen versorgt werden und später durch Molecule, ansible-lint oder CI geprüft werden.
Vault: Secrets nicht verstecken, sondern kontrolliert verschlüsseln
Ansible Vault schützt sensible Werte im Repository vor Klartextablage. Es ersetzt keine Rechtevergabe und keine zentrale Secrets-Plattform, verhindert aber, dass Passwörter, Tokens oder private Parameter direkt im Git-Verlauf landen.
ansible-vault create inventories/lab/group_vars/vault.yamlansible-vault encrypt_string \--vault-id lab@prompt \'BeispielPasswort' \--name 'ansible_become_password'
# inventories/lab/group_vars/linux.yamlansible_become_password: "{{ vault_ansible_become_password }}"# inventories/lab/group_vars/vault.yamlvault_ansible_become_password: !vault |$ANSIBLE_VAULT;1.2;AES256;lab396661303834...
Im Team sollte klar sein, welche Vault-IDs es gibt, wer die Passphrase besitzt, ob im CI ein Secret-Store verwendet wird und wie man Vault-Dateien rotiert. Unklarer Vault-Betrieb ist später schwerer zu debuggen als ein normales Playbook.
Check Mode, Diff Mode und technische Abnahme
Vor produktiven Änderungen sollte ein Lauf immer mindestens syntaktisch geprüft, simuliert, mit Diff betrachtet und danach auf Idempotenz getestet werden. Der zweite Lauf ist wichtig: Er zeigt, ob die Rolle wirklich Zustände beschreibt oder jedes Mal erneut Änderungen erzeugt.
ansible-inventory -i inventories/lab/hosts.yaml --graphansible-inventory -i inventories/lab/hosts.yaml --listansible-playbook -i inventories/lab/hosts.yaml playbooks/baseline.yaml --syntax-checkansible-playbook -i inventories/lab/hosts.yaml playbooks/baseline.yaml --check --diffansible-playbook -i inventories/lab/hosts.yaml playbooks/baseline.yaml --limit sles16-client01.example.test --diffansible-playbook -i inventories/lab/hosts.yaml playbooks/baseline.yaml --check --diff
Prüfschritt Erwartung--------------------------- ---------------------------------------------Inventory --graph Gruppen und Hosts sind wie geplant sichtbarInventory --list Variablen werden am Host korrekt aufgelöstSyntax-Check Playbook ist syntaktisch gültigCheck Mode + Diff geplante Änderungen sind nachvollziehbarLimit auf Einzelhost Änderung wird zuerst klein ausgerolltZweiter Check Mode changed=0 oder bewusst erklärte Abweichungen
Wenn Check Mode keine sinnvolle Aussage liefert, muss das dokumentiert werden. Nicht jedes Modul kann Check Mode vollständig simulieren. Gerade deshalb ist der limitierte erste Echtlauf auf einem einzelnen Host so wichtig.
Typische Fehler in dieser Phase
- Variablen werden in Playbooks hart codiert, obwohl sie in group_vars oder defaults gehören.
- host_vars werden als Müllhalde für Gruppenlogik verwendet.
- Secrets liegen in normalen YAML-Dateien oder landen versehentlich im Git-Verlauf.
- Rollen haben keine Defaults und funktionieren nur mit exakt einem Inventory.
- Playbooks werden ohne --check, --diff und zweiten Idempotenzlauf produktiv ausgeführt.
- Inventory-Gruppen mischen Umgebung, Distribution und Funktion unklar in einem Namen.
Wie es weitergeht
Der nächste sinnvolle Teil ist eine echte Linux-Baseline: Pakete, Dienste, SSH, Zeitbasis, Firewall und einfache Compliance-Prüfungen. Danach kann dieselbe Struktur für Updates, Reboots, Rollenqualität, CI und AWX weiterverwendet werden.



