--- # ============================================================================= # DRIFT-CHECK PLAYBOOK # Purpose: Check file drift and output detailed file-level JSON report # Usage: ansible-playbook drift-check.yml # Output: Structured JSON with sync status and drifted files info # Registers: drift_detected, drifted_files_json for consumption by update script # ============================================================================= - name: Check file drift hosts: all gather_facts: false vars_files: - ../deploy-config.yml tasks: # ───────────────────────────────────────────────────────────────────── # TASK 1: Initialize drift tracking variables # ───────────────────────────────────────────────────────────────────── - name: Initialize drift tracking set_fact: drift_detected: false drifted_items: [] # ───────────────────────────────────────────────────────────────────── # TASK 2: Read local files from repository # ───────────────────────────────────────────────────────────────────── - name: Read local files slurp: src: "{{ playbook_dir }}/{{ '../../' + item.src }}" delegate_to: localhost loop: "{{ deploy_items }}" loop_control: loop_var: item label: "{{ item.name }}" register: local_files failed_when: false # ───────────────────────────────────────────────────────────────────── # TASK 3: Read server files # ───────────────────────────────────────────────────────────────────── - name: Read server files slurp: src: "{{ item.dest }}" loop: "{{ deploy_items }}" loop_control: loop_var: item label: "{{ item.name }}" register: server_files failed_when: false # ───────────────────────────────────────────────────────────────────── # TASK 4: Compare files and detect drift # Builds list of drifted files by comparing local vs server # ───────────────────────────────────────────────────────────────────── - name: Detect drift by comparing files set_fact: drifted_items: "{{ drifted_items | default([]) + [drift_item] }}" vars: local_result: "{{ local_files.results[item_index] }}" server_result: "{{ server_files.results[item_index] }}" item_index: "{{ loop_index0 }}" drift_item: | {%- if server_result.rc != 0 -%} { "name": "{{ item.name }}", "destination": "{{ item.dest }}", "status": "MISSING", "reason": "File not found on server" } {%- elif local_result.content | b64decode != server_result.content | b64decode -%} { "name": "{{ item.name }}", "destination": "{{ item.dest }}", "status": "CONTENT_DIFFERS", "reason": "File content differs from repository" } {%- endif -%} loop: "{{ deploy_items }}" loop_control: loop_var: item label: "{{ item.name }}" # ───────────────────────────────────────────────────────────────────── # TASK 5: Update drift detection flag and filter results # ───────────────────────────────────────────────────────────────────── - name: Set drift_detected flag set_fact: drifted_items: "{{ drifted_items | map('from_json') | selectattr('status', 'defined') | list }}" drift_detected: "{{ (drifted_items | map('from_json') | selectattr('status', 'defined') | list | length) > 0 }}" # ───────────────────────────────────────────────────────────────────── # TASK 6: Generate JSON report with drift details # ───────────────────────────────────────────────────────────────────── - name: Generate drift detection JSON report set_fact: drifted_files_json: "{{ drifted_items | to_nice_json }}" # ───────────────────────────────────────────────────────────────────── # TASK 7: Save drift report to file for script consumption # ───────────────────────────────────────────────────────────────────── - name: Save drift report to file copy: content: "{{ drifted_files_json }}" dest: "/tmp/drifted_files_{{ inventory_hostname }}.json" owner: root group: root mode: "0644" delegate_to: localhost # ───────────────────────────────────────────────────────────────────── # TASK 8: Output status summary # ───────────────────────────────────────────────────────────────────── - name: Output SYNCED status debug: msg: | ✓ All files are in sync Total files monitored: {{ deploy_items | length }} when: not drift_detected - name: Output OUT_OF_SYNC status with details debug: msg: | ✗ Configuration drift detected! Drifted files: {{ drifted_items | length }} Details: {{ drifted_files_json }} when: drift_detected # ───────────────────────────────────────────────────────────────────── # TASK 9: Fail if drift detected (for CI/CD pipeline) # ───────────────────────────────────────────────────────────────────── - name: Fail if drift detected fail: msg: "Configuration drift detected. {{ drifted_items | length }} file(s) out of sync." when: drift_detected