--- - name: Check rsyslog configuration drift hosts: rsyslog_servers gather_facts: false # NOTE: This playbook compares file CONTENT ONLY using md5 checksums. # It ignores permissions/ownership differences. # Permissions are enforced during deploy (apply.yml) tasks: # ------------------------------------------------------------------------- # Checksum-based content comparison (ignores permissions/ownership) # This is the only reliable way to detect actual config changes # after deployment when permissions have been set. # ------------------------------------------------------------------------- - name: Get MD5 checksum of Git version of rsyslog.conf stat: path: "{{ playbook_dir }}/../../files/rsyslog.conf" delegate_to: localhost register: git_rsyslog_conf_stat - name: Get MD5 checksum of server version of rsyslog.conf stat: path: "{{ rsyslog_main_config }}" register: server_rsyslog_conf_stat - name: Compare rsyslog.conf content set_fact: main_config_check: changed: "{{ git_rsyslog_conf_stat.stat.checksum != server_rsyslog_conf_stat.stat.checksum }}" checksum_git: "{{ git_rsyslog_conf_stat.stat.checksum }}" checksum_server: "{{ server_rsyslog_conf_stat.stat.checksum }}" - name: Get checksums for rsyslog.d directory files block: - name: Find Git rsyslog.d files find: paths: "{{ playbook_dir }}/../../files/rsyslog.d" patterns: "*.conf" recurse: false delegate_to: localhost register: git_confd_files - name: Find server rsyslog.d files find: paths: "{{ rsyslog_config_dir }}" patterns: "*.conf" recurse: false register: server_confd_files - name: Get checksums for all Git rsyslog.d files stat: path: "{{ item.path }}" delegate_to: localhost loop: "{{ git_confd_files.files }}" register: git_confd_checksums - name: Get checksums for all server rsyslog.d files stat: path: "{{ item.path }}" loop: "{{ server_confd_files.files }}" register: server_confd_checksums - name: Compare rsyslog.d file checksums set_fact: rsyslogd_check: changed: "{{ git_confd_checksums.results | map(attribute='stat.checksum') | list != server_confd_checksums.results | map(attribute='stat.checksum') | list }}" - name: Check for extra files on server not present in Git block: - name: Find config files on server ansible.builtin.find: paths: "{{ rsyslog_config_dir }}" patterns: "*.conf" recurse: false register: server_configs - name: Find config files in Git (controller) ansible.builtin.find: paths: "{{ playbook_dir }}/../../files/rsyslog.d" patterns: "*.conf" recurse: false delegate_to: localhost register: repo_configs - name: Build list of Git-managed filenames ansible.builtin.set_fact: git_filenames: "{{ repo_configs.files | map(attribute='path') | map('basename') | list }}" - name: Build list of server filenames ansible.builtin.set_fact: server_filenames: "{{ server_configs.files | map(attribute='path') | map('basename') | list }}" - name: Find server files that are managed by Git but missing on server ansible.builtin.set_fact: missing_on_server: "{{ git_filenames | difference(server_filenames) }}" - name: Show missing files ansible.builtin.debug: msg: "Files in Git but missing on server: {{ missing_on_server }}" when: missing_on_server | length > 0 # Initialize missing_on_server with default empty list to avoid undefined variable errors - name: Initialize missing files tracking ansible.builtin.set_fact: missing_on_server: [] - name: Set overall drift flag ansible.builtin.set_fact: # Drift detected if: main config changed OR rsyslog.d changed OR any git-managed files missing from server # Using | default([]) to safely handle undefined variables in container environment drift_detected: "{{ main_config_check.changed or rsyslogd_check.changed or (missing_on_server | default([]) | length > 0) }}" # ───────────────────────────────────────────────────────────────────────── # Debug: Show WHAT changed (for troubleshooting) # ───────────────────────────────────────────────────────────────────────── - name: Show main config change status ansible.builtin.debug: msg: "Main config (rsyslog.conf) changed: {{ main_config_check.changed }}" - name: Show rsyslog.d change status ansible.builtin.debug: msg: "rsyslog.d directory changed: {{ rsyslogd_check.changed }}" - name: Show main config diff if changed ansible.builtin.debug: var: main_config_check.diff when: main_config_check.changed and main_config_check.diff is defined - name: Show rsyslog.d diff if changed ansible.builtin.debug: var: rsyslogd_check.diff when: rsyslogd_check.changed and rsyslogd_check.diff is defined - name: Show rsyslog.d diff list ansible.builtin.debug: msg: "rsyslogd_check details: {{ rsyslogd_check }}" when: rsyslogd_check.changed - name: Debug rsyslogd_check.diff structure ansible.builtin.debug: msg: | rsyslogd_check.diff is list: {{ rsyslogd_check.diff is iterable and rsyslogd_check.diff is not string }} rsyslogd_check.diff length: {{ rsyslogd_check.diff | length if rsyslogd_check.diff is iterable else 'N/A' }} rsyslogd_check.diff first item: {{ rsyslogd_check.diff[0] if rsyslogd_check.diff is iterable and rsyslogd_check.diff | length > 0 else 'empty' }} Full diff content: {{ rsyslogd_check.diff }} when: rsyslogd_check.changed and rsyslogd_check.diff is defined # ───────────────────────────────────────────────────────────────────────── # Build structured list of changed files for GitOps status server # This data is parsed by the update-gitops-status.sh wrapper script # ───────────────────────────────────────────────────────────────────────── - name: Initialize list of drifted files ansible.builtin.set_fact: drifted_files: [] - name: Add main config to drifted files if changed ansible.builtin.set_fact: drifted_files: "{{ drifted_files + ['/etc/rsyslog.conf'] }}" when: main_config_check.changed - name: Mark rsyslog.d directory as changed (simplified) ansible.builtin.set_fact: drifted_files: "{{ drifted_files + ['/etc/rsyslog.d/'] }}" when: rsyslogd_check.changed # NOTE: missing_on_server files are tracked in drift_detected flag but not in drifted_files list # This is intentional - they indicate missing deployed files, which is a drift condition # ───────────────────────────────────────────────────────────────────────── # Debug output: Show structured drifted files for parsing # Format: DRIFTED_FILES=file1,file2,file3 (or empty if no drift) # This makes it easy for update-gitops-status.sh to extract changed files # ALWAYS output this line for reliable parsing, even when empty # ───────────────────────────────────────────────────────────────────────── - name: Output structured list of drifted files for GitOps status server ansible.builtin.debug: msg: "DRIFTED_FILES={{ drifted_files | join(',') if drifted_files | length > 0 else '' }}" - name: Output sync status marker for parsing ansible.builtin.debug: msg: "SYNC_STATUS=SYNCED" when: not drift_detected - name: Output sync status marker for parsing ansible.builtin.debug: msg: "SYNC_STATUS=OUT_OF_SYNC" when: drift_detected - name: Print SYNCED status ansible.builtin.debug: msg: | ╭─────────────────────────────╮ │ ✓ SYNCED │ │ Configuration is up-to-date │ ╰─────────────────────────────╯ when: not drift_detected - name: Print OUT OF SYNC status ansible.builtin.debug: msg: | ╭─────────────────────────────╮ │ ✗ OUT OF SYNC │ │ Configuration has drifted │ ╰─────────────────────────────╯ when: drift_detected - name: Fail if drift detected ansible.builtin.fail: msg: "Configuration drift detected. Live system does not match repository." when: drift_detected