Warum Ansible für kleine bis mittlere Infrastrukturen?
In der österreichischen KMU-Landschaft verwalten die meisten IT-Verantwortlichen zwischen 5 und 100 Server – oft ohne dediziertes Ops-Team. Hier liegt Ansible genau richtig: kein Agent auf den Zielsystemen, keine komplizierte Master-Node-Architektur, nur SSH und Python.
Was Ansible von Alternativen wie Puppet oder Chef unterscheidet:
- Agentlos: Verbindet sich per SSH, kein Software-Install auf Zielservern nötig
- YAML-Syntax: Lesbar für Admins ohne Programmier-Hintergrund
- Idempotent: Playbooks können beliebig oft ausgeführt werden – das Ergebnis bleibt gleich
- Schneller Einstieg: Erste Playbooks funktionieren nach 30 Minuten
- Red Hat Backing: Enterprise-Support und langfristige Weiterentwicklung garantiert
Für größere Umgebungen (500+ Server, komplexe Orchestrierung) sind Terraform + Pulumi oder Kubernetes Operators die bessere Wahl. Im KMU-Bereich löst Ansible 90 % der Anforderungen ohne den operativen Overhead.
Ansible-Grundlagen: Inventory, Playbooks, Roles
Installation auf dem Control Node
Ansible läuft auf deinem lokalen Laptop oder einem dedizierten Management-Server (Ubuntu/Debian empfohlen):
# Ubuntu / Debian
sudo apt update && sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible
# Versionscheck
ansible --version
# SSH-Key auf alle Zielserver verteilen (einmalig)
ssh-keygen -t ed25519 -C "ansible-control"
ssh-copy-id -i ~/.ssh/id_ed25519.pub admin@192.168.1.10
Inventory-Struktur
Das Inventory definiert, welche Server Ansible kennt und wie sie gruppiert sind. Für mehrere Umgebungen (Staging, Production) empfehle ich ein INI-basiertes Inventory mit Gruppen-Variablen:
# inventory/hosts
[webserver]
web01.intern.at ansible_host=192.168.1.10
web02.intern.at ansible_host=192.168.1.11
[dbserver]
db01.intern.at ansible_host=192.168.1.20
[vpn]
vpn01.intern.at ansible_host=192.168.1.30
[production:children]
webserver
dbserver
vpn
[production:vars]
ansible_user=admin
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=~/.ssh/id_ed25519
Grundlegende Playbook-Struktur
Ein gut strukturiertes Ansible-Projekt trennt Rollen, Variablen und Inventorys konsequent:
ansible-project/
├── inventory/
│ ├── hosts # Alle Server
│ ├── group_vars/
│ │ ├── all.yml # Globale Variablen
│ │ ├── webserver.yml # Gruppen-spezifisch
│ │ └── production.yml
│ └── host_vars/
│ └── web01.intern.at.yml # Host-spezifisch
├── roles/
│ ├── common/ # Basis-Härtung (alle Server)
│ ├── wireguard/ # VPN-Role
│ ├── veeam-backup/ # Backup-Role
│ └── nginx/ # Webserver-Role
├── site.yml # Master-Playbook
├── vpn.yml # VPN-Playbook
├── backup.yml # Backup-Playbook
└── ansible.cfg # Konfiguration
Die ansible.cfg im Projektverzeichnis überschreibt globale Einstellungen:
# ansible.cfg
[defaults]
inventory = inventory/hosts
remote_user = admin
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
[privilege_escalation]
become = True
become_method = sudo
Die common-Role: Basis-Härtung für alle Server
Jeder Server bekommt zuerst die common-Role, die Grundsicherheit und Monitoring-Voraussetzungen einrichtet:
# roles/common/tasks/main.yml
---
- name: System-Pakete aktualisieren
apt:
update_cache: yes
upgrade: safe
cache_valid_time: 3600
- name: Basis-Pakete installieren
apt:
name:
- ufw
- fail2ban
- unattended-upgrades
- curl
- wget
- git
- htop
- rsync
- logrotate
state: present
- name: SSH-Konfiguration härten
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
state: present
loop:
- { regexp: '^PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^MaxAuthTries', line: 'MaxAuthTries 3' }
notify: restart sshd
- name: UFW – SSH erlauben
ufw:
rule: allow
name: OpenSSH
- name: UFW aktivieren
ufw:
state: enabled
policy: deny
direction: incoming
- name: Unattended Upgrades aktivieren (Sicherheitsupdates)
copy:
dest: /etc/apt/apt.conf.d/50unattended-upgrades
content: |
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Automatic-Reboot "false";
Brauchst du Unterstützung bei der Umsetzung?
30-Min Call — kostenlos, unverbindlich, konkret.
Praxisbeispiel 1: WireGuard VPN Deployment
WireGuard ist das modernste VPN-Protokoll – schnell, sicher, und mit Ansible in Minuten auf mehrere Server ausgerollt. Der folgende Playbook richtet einen WireGuard-Server ein und fügt automatisch Peers (z.B. Mitarbeiter-Laptops) hinzu.
Variablen definieren
# inventory/group_vars/vpn.yml
---
wireguard_interface: wg0
wireguard_port: 51820
wireguard_subnet: "10.10.0"
wireguard_server_ip: "10.10.0.1/24"
wireguard_server_private_key: "{{ vault_wg_server_private_key }}" # aus ansible-vault
wireguard_server_public_key: "{{ vault_wg_server_public_key }}"
wireguard_peers:
- name: "laptop-emre"
public_key: "PEER_PUBLIC_KEY_HIER"
allowed_ips: "10.10.0.2/32"
- name: "laptop-mitarbeiter1"
public_key: "PEER_PUBLIC_KEY_HIER"
allowed_ips: "10.10.0.3/32"
Sicherheitshinweis: Private Keys niemals im Klartext in YAML-Dateien speichern. Verwende ansible-vault: ansible-vault encrypt_string 'DEIN_KEY' --name vault_wg_server_private_key. Der verschlüsselte Wert kann dann sicher in Git eingecheckt werden.
WireGuard Role Tasks
# roles/wireguard/tasks/main.yml
---
- name: WireGuard installieren
apt:
name: wireguard
state: present
- name: WireGuard-Konfigurationsverzeichnis erstellen
file:
path: /etc/wireguard
state: directory
mode: '0700'
owner: root
group: root
- name: WireGuard Server-Konfiguration erstellen
template:
src: wg0.conf.j2
dest: /etc/wireguard/wg0.conf
mode: '0600'
owner: root
group: root
notify: restart wireguard
- name: IP-Forwarding aktivieren
sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: yes
state: present
reload: yes
- name: UFW – WireGuard-Port freigeben
ufw:
rule: allow
port: "{{ wireguard_port }}"
proto: udp
- name: WireGuard-Service aktivieren und starten
systemd:
name: "wg-quick@{{ wireguard_interface }}"
enabled: yes
state: started
Jinja2-Template für die WireGuard-Konfiguration
# roles/wireguard/templates/wg0.conf.j2
[Interface]
Address = {{ wireguard_server_ip }}
ListenPort = {{ wireguard_port }}
PrivateKey = {{ wireguard_server_private_key }}
# NAT für VPN-Traffic
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
{% for peer in wireguard_peers %}
[Peer]
# {{ peer.name }}
PublicKey = {{ peer.public_key }}
AllowedIPs = {{ peer.allowed_ips }}
{% endfor %}
VPN ausrollen
# Playbook erstellen
cat > vpn.yml << 'EOF'
---
- hosts: vpn
roles:
- common
- wireguard
EOF
# Syntax-Check (kein Deploy, nur Validierung)
ansible-playbook vpn.yml --syntax-check
# Dry-run (zeigt, was geändert würde)
ansible-playbook vpn.yml --check --diff
# Produktiv ausrollen (mit vault-Passwort)
ansible-playbook vpn.yml --ask-vault-pass
# VPN-Status prüfen
ansible vpn -m command -a "wg show"
Praxisbeispiel 2: Veeam Agent Backup-Automatisierung
Veeam Agent for Linux ist für österreichische KMUs mit Windows-Backup-Erfahrung eine logische Wahl. Mit Ansible wird die Installation und Konfiguration auf allen Linux-Servern einheitlich und reproduzierbar.
Veeam Agent installieren
# roles/veeam-backup/tasks/main.yml
---
- name: Veeam Repository Key hinzufügen
apt_key:
url: https://www.veeam.com/downloads/v5/deb/veeam-keyring.gpg
state: present
- name: Veeam APT-Repository konfigurieren
apt_repository:
repo: "deb https://repository.veeam.com/backup/linux/agent/dpkg/debian/x86_64 stable veeam"
state: present
filename: veeam
- name: Veeam Agent installieren
apt:
name:
- veeam
- veeamagent
state: present
update_cache: yes
- name: Backup-Verzeichnis auf Zielserver erstellen
file:
path: "{{ veeam_backup_path }}"
state: directory
mode: '0750'
owner: root
- name: Veeam Job-Konfiguration deployen
template:
src: veeam-job.xml.j2
dest: /etc/veeam/jobs/{{ inventory_hostname }}.xml
mode: '0640'
owner: root
notify: restart veeam
- name: Veeam Service aktivieren
systemd:
name: veeamservice
enabled: yes
state: started
Backup-Variablen pro Host
# inventory/group_vars/all.yml
---
veeam_backup_path: /mnt/backup
veeam_retention_days: 14
veeam_backup_hour: 2 # 02:00 Uhr nachts
veeam_backup_minute: 30
# Backup-Ziel (z.B. NFS-Share oder lokales Verzeichnis)
veeam_repo_type: "local" # oder "nfs", "smb"
veeam_repo_path: "/mnt/backup/veeam"
# host_vars/web01.intern.at.yml – abweichende Einstellungen pro Server
veeam_backup_hour: 1 # Web01 sichert um 01:00 Uhr (kein Konflikt mit DB-Backup)
Backup-Job per CLI anlegen
Veeam Agent wird über das veeamconfig-CLI konfiguriert. Ansible ruft die Befehle direkt auf:
# roles/veeam-backup/tasks/configure-job.yml
---
- name: Prüfen ob Backup-Repository existiert
command: veeamconfig repository list
register: repo_list
changed_when: false
- name: Backup-Repository anlegen (falls nicht vorhanden)
command: >
veeamconfig repository create
--name "{{ inventory_hostname }}-repo"
--location "{{ veeam_repo_path }}/{{ inventory_hostname }}"
when: inventory_hostname + '-repo' not in repo_list.stdout
- name: Backup-Job anlegen
command: >
veeamconfig job create fileleveljob
--name "{{ inventory_hostname }}-daily"
--repoName "{{ inventory_hostname }}-repo"
--includeDirs /etc,/var/lib,/home,/opt
--schedule "daily|{{ veeam_backup_hour }}:{{ '%02d' | format(veeam_backup_minute) }}"
--maxPoints {{ veeam_retention_days }}
register: job_create
failed_when: job_create.rc != 0 and 'already exists' not in job_create.stderr
- name: Backup-Status prüfen
command: veeamconfig job list
register: job_status
changed_when: false
- name: Job-Status ausgeben
debug:
msg: "{{ job_status.stdout_lines }}"
DSGVO-konforme Logs für österreichische Compliance
Die DSGVO verlangt Nachvollziehbarkeit: Wer hat wann auf welche Systeme zugegriffen? Mit Ansible lässt sich ein einheitliches Logging-Setup auf allen Servern erzwingen.
auditd – Systemaufrufe protokollieren
# roles/common/tasks/logging.yml
---
- name: auditd installieren
apt:
name:
- auditd
- audispd-plugins
state: present
- name: Audit-Regeln für DSGVO-Compliance deployen
copy:
dest: /etc/audit/rules.d/dsgvo.rules
content: |
# Dateiänderungen in sensiblen Verzeichnissen
-w /etc/passwd -p wa -k user_modification
-w /etc/shadow -p wa -k user_modification
-w /etc/group -p wa -k group_modification
-w /etc/sudoers -p wa -k sudo_modification
-w /var/log -p wa -k log_access
# Privilegierte Befehle
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands
# SSH-Authentifizierung
-w /var/log/auth.log -p r -k auth_access
# Netzwerkverbindungen (IPv4 + IPv6)
-a always,exit -F arch=b64 -S connect -k network_connect
notify: restart auditd
- name: Logrotate für Audit-Logs konfigurieren
copy:
dest: /etc/logrotate.d/auditd-dsgvo
content: |
/var/log/audit/audit.log {
daily
rotate 90
compress
delaycompress
missingok
notifempty
postrotate
/sbin/service auditd restart > /dev/null 2>&1 || true
endscript
}
- name: Zentrales Syslog-Forwarding konfigurieren (optional: zu SIEM)
lineinfile:
path: /etc/rsyslog.conf
line: "*.* @@{{ syslog_server }}:514"
state: present
when: syslog_server is defined
notify: restart rsyslog
Log-Aufbewahrungsfristen
Gemäß DSGVO Art. 5(1)(e) gilt das Prinzip der Speicherbegrenzung. Logs sollten nicht länger aufbewahrt werden als notwendig. Empfehlungen für österreichische Unternehmen:
- Authentifizierungs-Logs (
/var/log/auth.log): 90 Tage - System-Logs (
/var/log/syslog): 30 Tage - Audit-Logs (
/var/log/audit/): 90 Tage (bei Bedarf länger für Compliance) - Webserver-Logs (
access.log): 30 Tage (Kundendaten!) - Backup-Logs: 1 Jahr (für Nachweispflichten)
Rechtlicher Hinweis: Die konkreten Aufbewahrungsfristen hängen von deiner Branche und Verarbeitungszwecken ab. Lass die Fristen von einem Datenschutzbeauftragten prüfen. Die Datenschutzbehörde Österreich bietet Muster-Verarbeitungsverzeichnisse an.
Inventory-Management für wachsende Infrastrukturen
Ab etwa 20 Servern wird ein statisches Inventory-File unübersichtlich. Dann empfehle ich dynamische Inventorys:
# Hetzner Cloud als dynamisches Inventory
pip install hcloud
ansible-galaxy collection install hetzner.hcloud
# hcloud.yml
plugin: hetzner.hcloud.hcloud
token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
groups:
webserver: "'web' in labels"
dbserver: "'db' in labels"
# Aufruf mit dynamischem Inventory
ansible-playbook -i hcloud.yml site.yml
# AWS EC2 als dynamisches Inventory
ansible-galaxy collection install amazon.aws
# aws_ec2.yml erstellen und aws-Profil konfigurieren
ansible-playbook -i aws_ec2.yml site.yml
Idempotenz und Testing
Ein häufiger Fehler: Playbooks werden einmal geschrieben und nie getestet – bis ein zweiter Durchlauf etwas zerstört. Ansible-Rollen sollten immer idempotent sein.
Molecule: Lokales Testen von Roles
Molecule ermöglicht das Testen von Ansible-Roles in Docker-Containern, ohne echte Server zu benötigen:
# Molecule installieren
pip install molecule molecule-docker docker
# Neue Role mit Molecule-Struktur erstellen
molecule init role meine-rolle --driver-name docker
# Teststruktur
roles/wireguard/
├── molecule/
│ └── default/
│ ├── molecule.yml # Docker-Konfiguration
│ ├── converge.yml # Playbook zum Testen
│ └── verify.yml # Assertions (was soll gelten?)
└── tasks/
└── main.yml
# Tests ausführen
molecule test # Kompletter Zyklus (create, converge, verify, destroy)
molecule converge # Nur ausführen (schnell)
molecule verify # Nur verifizieren
molecule idempotency # Zweimal ausführen → prüft ob "changed" = 0
Check Mode und Diff
# Dry-run: zeigt was sich ändert, ohne zu deployen
ansible-playbook site.yml --check
# Mit Diff: zeigt konkrete Dateiänderungen
ansible-playbook site.yml --check --diff
# Nur bestimmte Tags ausführen
ansible-playbook site.yml --tags "wireguard,backup"
# Bestimmte Hosts ausschließen
ansible-playbook site.yml --limit "all:!db01.intern.at"
# Schrittweise (jede Task einzeln bestätigen)
ansible-playbook site.yml --step
Best Practices für Production
Nach Jahren in österreichischen Unternehmensumgebungen sind das meine wichtigsten Empfehlungen:
- ansible-vault für alle Secrets: Passwörter, API-Keys und Private Keys gehören nicht in Klartext ins Repository
- Git für alle Playbooks: Jede Änderung versioniert, mit aussagekräftigen Commit-Messages (
feat: WireGuard peer für Mitarbeiter Müller hinzugefügt) - Separate Inventorys für Staging/Production: Niemals direkt in Production testen
- Tags konsequent nutzen:
--tags common,securityerlaubt gezieltes Ausrollen - Handlers statt immer neu starten: Services werden nur neu gestartet wenn wirklich nötig
- Roles statt monolithische Playbooks: Eine Role = eine Aufgabe, wiederverwendbar
- Regelmäßige Runs dokumentieren: Ein wöchentlicher
--checkzeigt Drift zwischen Soll-Zustand (Ansible) und Ist-Zustand (Server)
CI/CD-Integration
Mit einer GitLab-Pipeline wird Ansible automatisch bei jedem Push ausgeführt:
# .gitlab-ci.yml
stages:
- lint
- test
- deploy
ansible-lint:
stage: lint
image: pipelinecomponents/ansible-lint:latest
script:
- ansible-lint site.yml
molecule-test:
stage: test
image: python:3.11
services:
- docker:dind
script:
- pip install molecule molecule-docker ansible
- cd roles/wireguard && molecule test
deploy-staging:
stage: deploy
image: willhallonline/ansible:2.16-ubuntu-22.04
script:
- ansible-playbook -i inventory/staging site.yml
environment: staging
only:
- main
deploy-production:
stage: deploy
image: willhallonline/ansible:2.16-ubuntu-22.04
script:
- ansible-playbook -i inventory/production site.yml --vault-password-file $VAULT_PASS_FILE
environment: production
when: manual
only:
- tags
Fazit
Ansible ist das richtige Werkzeug, wenn du:
- 5–200 Linux-Server einheitlich konfigurieren möchtest
- Infrastruktur als Code dokumentieren und versionieren willst
- Wiederkehrende Aufgaben wie Software-Updates oder Backup-Checks automatisieren möchtest
- DSGVO-konforme Dokumentation über deinen Server-Zustand brauchst
Der Einstieg ist niedrig – du brauchst kein Programmier-Hintergrund, nur SSH-Zugang und 30 Minuten für die ersten Playbooks. Der Gewinn ist enorm: Was früher Stunden gedauert hat (neuen Server aufsetzen, VPN-Peer hinzufügen, Backup-Job konfigurieren), dauert mit Ansible Sekunden.
Für österreichische KMUs mit begrenzten IT-Ressourcen ist das kein Nice-to-have – es ist der Unterschied zwischen einer skalierbaren Infrastruktur und einem Flickenteppich aus händisch konfigurierten Servern.
Willst du Ansible in deiner Infrastruktur einführen oder bestehende Skripte in Playbooks überführen? Ich helfe dir gerne dabei.