--- # ============================================================================= # 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: Check drift for each configured file # Loops through deploy_items and compares local vs server files # ───────────────────────────────────────────────────────────────────── - name: Check drift for each file block: # Read local file from repo - name: Read local file slurp: src: "{{ playbook_dir }}/{{ '../../' + item.src }}" delegate_to: localhost register: local_file_content failed_when: false # Read file from server - name: Read server file slurp: src: "{{ item.dest }}" register: server_file_content failed_when: false # Build drift info if file is missing - name: Add to drifted items if missing set_fact: drifted_items: "{{ drifted_items + [drift_info] }}" vars: drift_info: name: "{{ item.name }}" destination: "{{ item.dest }}" status: "MISSING" reason: "File not found on server" when: server_file_content.rc != 0 # Build drift info if file content differs - name: Add to drifted items if content differs set_fact: drifted_items: "{{ drifted_items + [drift_info] }}" vars: drift_info: name: "{{ item.name }}" destination: "{{ item.dest }}" status: "CONTENT_DIFFERS" reason: "File content differs from repository" when: - server_file_content.rc == 0 - local_file_content.content | b64decode != server_file_content.content | b64decode loop: "{{ deploy_items }}" loop_control: loop_var: item label: "{{ item.name }}" # ───────────────────────────────────────────────────────────────────── # TASK 3: Update drift detection flag # ───────────────────────────────────────────────────────────────────── - name: Set drift_detected flag set_fact: drift_detected: "{{ drifted_items | length > 0 }}" # ───────────────────────────────────────────────────────────────────── # TASK 4: Generate JSON report with drift details # ───────────────────────────────────────────────────────────────────── - name: Generate drift detection JSON report set_fact: drifted_files_json: "{{ drifted_items | to_nice_json }}" # ───────────────────────────────────────────────────────────────────── # TASK 5: 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 6: 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 7: 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