Ansible Praxisleitfaden

Ansible Automation für Linux Server – Von Basics bis Production

Warum Ansible die richtige Wahl für kleine bis mittlere IT-Infrastrukturen ist, wie du Playbooks strukturierst, ein WireGuard-VPN ausrollst und Veeam Agent Backups automatisierst – mit DSGVO-konformen Logs.

E
Emre Hayta
6. März 2026 12 Min. Lesezeit
Ansible Linux WireGuard Veeam DSGVO
Inhaltsverzeichnis

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):

Bash
# 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:

INI
# 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:

Bash
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:

INI
# 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:

YAML
# 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.

Termin buchen →

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

YAML
# 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

YAML
# 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

YAML
# 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

Bash
# 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

YAML
# 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

YAML
# 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:

YAML
# 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

YAML
# 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:

Bash
# 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:

Bash
# 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

Bash
# 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,security erlaubt 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 --check zeigt Drift zwischen Soll-Zustand (Ansible) und Ist-Zustand (Server)

CI/CD-Integration

Mit einer GitLab-Pipeline wird Ansible automatisch bei jedem Push ausgeführt:

YAML
# .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.

Ansible-Automatisierung für deine Infrastruktur?

Du möchtest manuelle Server-Verwaltung durch reproduzierbare Ansible-Playbooks ersetzen? Ich analysiere deine Infrastruktur und erstelle ein maßgeschneidertes Automatisierungskonzept.

Weitere Artikel