--- - name: Install and Configure Borg Backup Client hosts: backup_clients gather_facts: yes become: yes vars: backup_repo_server: "backup01.mbotechit.de" backup_repo_base: "/backup/repos" backup_user: "backup" backup_ssh_key_path: "/root/.ssh/mbo-backup-key.ed25519" backup_script_dir: "/usr/local/bin/mbo-backup" backup_log_dir: "/var/log/mbo-backup" borg_version: "1.4.0" tasks: # ====================== # 1. System Dependencies # ====================== - name: Update APT cache apt: update_cache: yes cache_valid_time: 3600 when: ansible_os_family == "Debian" - name: Install Borg + Dependencies apt: name: - borgbackup - openssh-client - python3 - python3-pip - curl - jq state: present when: ansible_os_family == "Debian" - name: Install Borg + Dependencies (RedHat) yum: name: - borgbackup - openssh-clients - python3 - python3-pip - curl - jq state: present when: ansible_os_family == "RedHat" # ====================== # 2. SSH Key Setup # ====================== - name: Create SSH directory for root file: path: "/root/.ssh" state: directory mode: "0700" - name: Copy Borg SSH key from Ansible host copy: src: "files/mbo-backup-key.ed25519" dest: "{{ backup_ssh_key_path }}" mode: "0600" owner: root group: root register: ssh_key_copied - name: Generate SSH key if not provided command: "ssh-keygen -t ed25519 -N '' -f {{ backup_ssh_key_path }} -C 'borg-backup@{{ inventory_hostname }}'" when: ssh_key_copied is failed ignore_errors: yes - name: Ensure SSH public key exists command: "ssh-keygen -y -f {{ backup_ssh_key_path }} > {{ backup_ssh_key_path }}.pub" when: not ansible_check_mode # ====================== # 3. Backup Script Installation # ====================== - name: Create backup script directory file: path: "{{ backup_script_dir }}" state: directory mode: "0755" - name: Create backup log directory file: path: "{{ backup_log_dir }}" state: directory mode: "0755" owner: root group: root - name: Deploy Borg backup wrapper script template: src: "borg-backup.sh.j2" dest: "{{ backup_script_dir }}/borg-backup.sh" mode: "0755" owner: root group: root - name: Deploy Docker pre-backup hook template: src: "pre-backup-docker.sh.j2" dest: "{{ backup_script_dir }}/pre-backup-docker.sh" mode: "0755" owner: root group: root when: '"docker" in ansible_facts.packages or ansible_docker_containers is defined' - name: Deploy backup status reporter template: src: "backup-status-reporter.sh.j2" dest: "{{ backup_script_dir }}/backup-status-reporter.sh" mode: "0755" owner: root group: root # ====================== # 4. SSH Config for Backup Server # ====================== - name: Create SSH config entry for backup server blockinfile: path: "/root/.ssh/config" create: yes mode: "0600" block: | Host {{ backup_repo_server }} HostName {{ backup_repo_server }} User backup IdentityFile {{ backup_ssh_key_path }} StrictHostKeyChecking accept-new UserKnownHostsFile /root/.ssh/known_hosts # ====================== # 5. Systemd Service & Timer # ====================== - name: Create systemd service file template: src: "borg-backup.service.j2" dest: "/etc/systemd/system/mbo-backup.service" mode: "0644" - name: Create systemd timer file template: src: "borg-backup.timer.j2" dest: "/etc/systemd/system/mbo-backup.timer" mode: "0644" - name: Reload systemd daemon systemd: daemon_reload: yes - name: Enable and start Borg backup timer systemd: name: mbo-backup.timer enabled: yes state: started # ====================== # 6. Verify Installation # ====================== - name: Check Borg version command: "borg --version" register: borg_version_check changed_when: false - name: Test SSH connection to backup server command: "ssh -o ConnectTimeout=5 {{ backup_repo_server }} 'echo OK'" register: ssh_test changed_when: false ignore_errors: yes - name: Display verification results debug: msg: | ✓ Borg {{ borg_version_check.stdout }} ✓ SSH Test: {{ ssh_test.stdout | default('FAILED') }} ✓ Backup Script Dir: {{ backup_script_dir }} ✓ Timer Status: systemctl status mbo-backup.timer ✓ Next Backup: systemctl list-timers mbo-backup.timer handlers: - name: Reload systemd systemd: daemon_reload: yes