--- # ============================================================================= # proxmox_config_backup — tasks # Creates a tarball of critical Proxmox config files and stores it locally # or transfers via SFTP. Git destination is a TODO pending secure handling # of sensitive files (SSL keys, shadow.cfg, etc). # # Required vars: # current_node — the node being backed up (for filename) # client_id — client identifier (for filename) # ============================================================================= - name: "Backup | {{ current_node }} | Gather date/time facts" ansible.builtin.setup: gather_subset: - date_time when: ansible_date_time is not defined - name: "Backup | {{ current_node }} | Set backup filename" ansible.builtin.set_fact: backup_filename: >- proxmox_{{ client_id | lower | replace('-', '_') }}_{{ current_node }}_{{ ansible_date_time.date }} - name: "Backup | {{ current_node }} | Set backup paths" ansible.builtin.set_fact: backup_paths: "{{ backup_paths_proxmox }}" # ── Create tarball on node ──────────────────────────────────────────────────── - name: "Backup | {{ current_node }} | Create config tarball" ansible.builtin.shell: | tar czf /tmp/{{ backup_filename }}.tar.gz \ --ignore-failed-read \ --dereference \ {{ backup_paths | join(' ') }} 2>/dev/null || true echo "done" changed_when: true register: backup_tarball # ── Local backup ────────────────────────────────────────────────────────────── - name: "Backup | {{ current_node }} | Local | Ensure backup dir exists" ansible.builtin.file: path: "{{ backup_local_dir }}" state: directory mode: '0700' when: backup_destination == 'local' - name: "Backup | {{ current_node }} | Local | Move tarball to backup dir" ansible.builtin.copy: src: "/tmp/{{ backup_filename }}.tar.gz" dest: "{{ backup_local_dir }}/{{ backup_filename }}.tar.gz" remote_src: true mode: '0600' when: backup_destination == 'local' - name: "Backup | {{ current_node }} | Local | Rotate old backups" ansible.builtin.shell: | ls -1t {{ backup_local_dir }}/proxmox_*_{{ current_node }}_*.tar.gz 2>/dev/null \ | tail -n +{{ (backup_local_keep | int) + 1 }} \ | xargs -r rm -f changed_when: false when: backup_destination == 'local' - name: "Backup | {{ current_node }} | Local | Log result" ansible.builtin.debug: msg: "✓ Config backed up locally: {{ backup_local_dir }}/{{ backup_filename }}.tar.gz" when: backup_destination == 'local' # ── SFTP backup ─────────────────────────────────────────────────────────────── - name: "Backup | {{ current_node }} | SFTP | Validate required vars" ansible.builtin.fail: msg: "SFTP backup requires backup_sftp_host and backup_sftp_user to be set." when: - backup_destination == 'sftp' - backup_sftp_host == '' or backup_sftp_user == '' - name: "Backup | {{ current_node }} | SFTP | Fetch tarball to controller" ansible.builtin.fetch: src: "/tmp/{{ backup_filename }}.tar.gz" dest: "/tmp/{{ backup_filename }}.tar.gz" flat: true when: backup_destination == 'sftp' - name: "Backup | {{ current_node }} | SFTP | Transfer to remote host" ansible.builtin.shell: | sftp_opts="-o StrictHostKeyChecking=no -o BatchMode=yes" {% if backup_sftp_key != '' %} sftp_opts="$sftp_opts -i {{ backup_sftp_key }}" {% endif %} sftp $sftp_opts {{ backup_sftp_user }}@{{ backup_sftp_host }} << EOF cd {{ backup_sftp_remote_dir }} put /tmp/{{ backup_filename }}.tar.gz EOF delegate_to: localhost changed_when: true when: backup_destination == 'sftp' - name: "Backup | {{ current_node }} | SFTP | Clean up local temp tarball" ansible.builtin.file: path: "/tmp/{{ backup_filename }}.tar.gz" state: absent delegate_to: localhost when: backup_destination == 'sftp' - name: "Backup | {{ current_node }} | SFTP | Log result" ansible.builtin.debug: msg: "✓ Config backed up via SFTP: {{ backup_sftp_host }}:{{ backup_sftp_remote_dir }}/{{ backup_filename }}.tar.gz" when: backup_destination == 'sftp' # ── Cleanup ─────────────────────────────────────────────────────────────────── - name: "Backup | {{ current_node }} | Clean up temp tarball on node" ansible.builtin.file: path: "/tmp/{{ backup_filename }}.tar.gz" state: absent