--- # ============================================================================= # Backup Playbook # ============================================================================= # # This playbook performs comprehensive backups of system configuration, # application data, and databases across all managed hosts. # # Usage: # ansible-playbook playbooks/backup.yml # ansible-playbook playbooks/backup.yml --limit production # ansible-playbook playbooks/backup.yml --tags config,databases # ansible-playbook playbooks/backup.yml --extra-vars "backup_type=full" # # Tags: # config - Backup system configuration files # data - Backup application data # databases - Backup databases # logs - Backup log files # verify - Verify backup integrity # cleanup - Clean old backups # # ============================================================================= - name: System and Application Backup hosts: all become: true gather_facts: true vars: backup_timestamp: "{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}" backup_base_dir: "/var/backups" backup_remote_dir: "{{ backup_remote_dir | default(None) }}" backup_retention_days: "{{ backup_retention_days | default(30) }}" backup_type: "{{ backup_type | default('incremental') }}" # full | incremental backup_compress: true backup_verify: true pre_tasks: - name: Display backup banner debug: msg: - "=========================================" - "Backup Process Starting" - "=========================================" - "Host: {{ inventory_hostname }}" - "Environment: {{ environment | default('unknown') }}" - "Timestamp: {{ backup_timestamp }}" - "Type: {{ backup_type }}" - "Retention: {{ backup_retention_days }} days" - "=========================================" tags: [always] - name: Create backup directories file: path: "{{ backup_base_dir }}/{{ item }}" state: directory mode: '0700' owner: root group: root loop: - config - data - databases - logs tags: [always] - name: Check available disk space shell: df -h {{ backup_base_dir }} | tail -1 | awk '{print $4}' register: backup_disk_space changed_when: false tags: [always] - name: Display available disk space debug: msg: "Available disk space for backups: {{ backup_disk_space.stdout }}" tags: [always] tasks: # ========================================================================= # Configuration Backup # ========================================================================= - name: Backup system configuration files block: - name: Backup /etc directory archive: path: /etc dest: "{{ backup_base_dir }}/config/etc_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' tags: [config] - name: Backup SSH configuration archive: path: /etc/ssh dest: "{{ backup_base_dir }}/config/ssh_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' tags: [config] - name: Backup network configuration (Debian) archive: path: /etc/network dest: "{{ backup_base_dir }}/config/network_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' when: ansible_os_family == "Debian" failed_when: false tags: [config] - name: Backup network configuration (RHEL) archive: path: /etc/sysconfig/network-scripts dest: "{{ backup_base_dir }}/config/network_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' when: ansible_os_family == "RedHat" failed_when: false tags: [config] - name: Backup firewall rules (RHEL) archive: path: /etc/firewalld dest: "{{ backup_base_dir }}/config/firewall_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' when: ansible_os_family == "RedHat" failed_when: false tags: [config] - name: Backup cron jobs archive: path: - /etc/crontab - /etc/cron.d - /var/spool/cron dest: "{{ backup_base_dir }}/config/cron_backup_{{ backup_timestamp }}.tar.gz" format: gz mode: '0600' failed_when: false tags: [config] - name: Backup systemd services shell: | mkdir -p /tmp/systemd_backup cp -r /etc/systemd/system/*.service /tmp/systemd_backup/ 2>/dev/null || true cp -r /usr/lib/systemd/system/*.service /tmp/systemd_backup/ 2>/dev/null || true tar czf {{ backup_base_dir }}/config/systemd_backup_{{ backup_timestamp }}.tar.gz -C /tmp systemd_backup rm -rf /tmp/systemd_backup changed_when: false failed_when: false tags: [config] when: "'config' in ansible_run_tags or 'all' in ansible_run_tags" tags: [config] # ========================================================================= # Application Data Backup # ========================================================================= - name: Backup application data block: - name: Backup /opt directory archive: path: /opt dest: "{{ backup_base_dir }}/data/opt_backup_{{ backup_timestamp }}.tar.gz" format: gz exclude_path: - /opt/tmp - /opt/cache when: backup_type == 'full' tags: [data] - name: Backup /var/lib application data archive: path: /var/lib dest: "{{ backup_base_dir }}/data/var_lib_backup_{{ backup_timestamp }}.tar.gz" format: gz exclude_path: - /var/lib/docker - /var/lib/containers - /var/lib/mysql - /var/lib/postgresql when: backup_type == 'full' failed_when: false tags: [data] - name: Backup user home directories archive: path: /home dest: "{{ backup_base_dir }}/data/home_backup_{{ backup_timestamp }}.tar.gz" format: gz when: backup_type == 'full' failed_when: false tags: [data] when: "'data' in ansible_run_tags or 'all' in ansible_run_tags" tags: [data] # ========================================================================= # Database Backups # ========================================================================= - name: Backup databases block: - name: Check if MySQL/MariaDB is installed command: which mysqldump register: backup_mysql_check changed_when: false failed_when: false - name: Backup MySQL/MariaDB databases shell: | mysqldump --all-databases --single-transaction --quick --lock-tables=false \ | gzip > {{ backup_base_dir }}/databases/mysql_dump_{{ backup_timestamp }}.sql.gz when: backup_mysql_check.rc == 0 no_log: true tags: [databases] - name: Check if PostgreSQL is installed command: which pg_dumpall register: backup_postgres_check changed_when: false failed_when: false - name: Backup PostgreSQL databases shell: | sudo -u postgres pg_dumpall \ | gzip > {{ backup_base_dir }}/databases/postgres_dump_{{ backup_timestamp }}.sql.gz when: backup_postgres_check.rc == 0 no_log: true tags: [databases] - name: Check if MongoDB is installed command: which mongodump register: backup_mongodb_check changed_when: false failed_when: false - name: Backup MongoDB databases command: > mongodump --out {{ backup_base_dir }}/databases/mongodb_{{ backup_timestamp }} --gzip when: backup_mongodb_check.rc == 0 tags: [databases] - name: Set database backup permissions file: path: "{{ backup_base_dir }}/databases" mode: '0700' owner: root group: root recurse: yes when: "'databases' in ansible_run_tags or 'all' in ansible_run_tags" tags: [databases] # ========================================================================= # Log Backups # ========================================================================= - name: Backup important logs block: - name: Backup /var/log directory archive: path: /var/log dest: "{{ backup_base_dir }}/logs/var_log_backup_{{ backup_timestamp }}.tar.gz" format: gz exclude_path: - /var/log/journal failed_when: false tags: [logs] - name: Backup audit logs archive: path: /var/log/audit dest: "{{ backup_base_dir }}/logs/audit_backup_{{ backup_timestamp }}.tar.gz" format: gz when: ansible_os_family in ["RedHat", "Debian"] failed_when: false tags: [logs] when: "'logs' in ansible_run_tags or 'all' in ansible_run_tags" tags: [logs] # ========================================================================= # Backup Verification # ========================================================================= - name: Verify backups block: - name: List all backups created find: paths: - "{{ backup_base_dir }}/config" - "{{ backup_base_dir }}/data" - "{{ backup_base_dir }}/databases" - "{{ backup_base_dir }}/logs" patterns: "*{{ backup_timestamp }}*" register: backup_created_files - name: Verify backup file integrity command: "gzip -t {{ item.path }}" loop: "{{ backup_created_files.files }}" when: - item.path.endswith('.gz') - backup_verify changed_when: false failed_when: false register: backup_integrity_check - name: Calculate total backup size shell: du -sh {{ backup_base_dir }} | awk '{print $1}' register: backup_total_size changed_when: false - name: Display backup verification results debug: msg: - "Backup Verification Results:" - "Files created: {{ backup_created_files.files | length }}" - "Total backup size: {{ backup_total_size.stdout }}" - "Integrity check: {{ 'Passed' if backup_integrity_check is not failed else 'Failed' }}" when: backup_verify tags: [verify, always] # ========================================================================= # Transfer to Remote Storage (Optional) # ========================================================================= - name: Transfer backups to remote storage block: - name: Sync backups to remote location synchronize: src: "{{ backup_base_dir }}/" dest: "{{ backup_remote_dir }}/" archive: yes compress: yes delete: no when: backup_remote_dir is defined and backup_remote_dir delegate_to: localhost when: backup_remote_dir is defined and backup_remote_dir tags: [remote, never] # ========================================================================= # Cleanup Old Backups # ========================================================================= - name: Clean up old backups block: - name: Find backups older than retention period find: paths: - "{{ backup_base_dir }}/config" - "{{ backup_base_dir }}/data" - "{{ backup_base_dir }}/databases" - "{{ backup_base_dir }}/logs" age: "{{ backup_retention_days }}d" patterns: "*.tar.gz" register: backup_old_files - name: Display old backups to be removed debug: msg: "Found {{ backup_old_files.files | length }} backups older than {{ backup_retention_days }} days" - name: Remove old backups file: path: "{{ item.path }}" state: absent loop: "{{ backup_old_files.files }}" when: backup_old_files.files | length > 0 tags: [cleanup] post_tasks: - name: Generate backup manifest copy: content: | Backup Manifest =============== Host: {{ inventory_hostname }} Environment: {{ environment | default('unknown') }} Timestamp: {{ backup_timestamp }} Type: {{ backup_type }} Files Backed Up: {% for file in backup_created_files.files %} - {{ file.path }} ({{ file.size | filesizeformat }}) {% endfor %} Total Size: {{ backup_total_size.stdout }} Status: Success dest: "{{ backup_base_dir }}/backup_manifest_{{ backup_timestamp }}.txt" mode: '0600' when: backup_created_files is defined tags: [always] - name: Display backup summary debug: msg: - "=========================================" - "Backup Summary" - "=========================================" - "Host: {{ inventory_hostname }}" - "Environment: {{ environment | default('unknown') }}" - "Completed: {{ ansible_date_time.iso8601 }}" - "" - "=== Backup Details ===" - "Type: {{ backup_type }}" - "Files created: {{ backup_created_files.files | length | default(0) }}" - "Total size: {{ backup_total_size.stdout | default('Unknown') }}" - "Location: {{ backup_base_dir }}" - "" - "=== Retention ===" - "Retention period: {{ backup_retention_days }} days" - "Old backups cleaned: {{ backup_old_files.files | length | default(0) }}" - "" - "=== Verification ===" - "Integrity check: {{ 'Passed' if backup_verify and backup_integrity_check is not failed else 'Skipped' }}" - "" - "Manifest: {{ backup_base_dir }}/backup_manifest_{{ backup_timestamp }}.txt" - "=========================================" tags: [always] # ============================================================================= # Backup Manifests # ============================================================================= # Manifests are saved to: /var/backups/backup_manifest_.txt # =============================================================================