name: Drift Detection on: schedule: # Daily at 06:00 AEST (20:00 UTC previous day) - cron: "0 20 * * *" workflow_dispatch: inputs: target: description: "Which layer to check: all | terraform | ansible" required: false default: "all" env: TF_IN_AUTOMATION: "true" jobs: terraform-drift: name: Terraform — detect drift runs-on: ubuntu-latest if: ${{ github.event.inputs.target == 'all' || github.event.inputs.target == 'terraform' || github.event_name == 'schedule' }} steps: - uses: actions/checkout@v4 - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: "~1.9" - name: Terraform init + plan (proxmox) id: tf_proxmox env: TF_VAR_proxmox_endpoint: ${{ secrets.PROXMOX_ENDPOINT }} TF_VAR_proxmox_api_token_id: ${{ secrets.PROXMOX_API_TOKEN_ID }} TF_VAR_proxmox_api_token_secret: ${{ secrets.PROXMOX_API_TOKEN_SECRET }} run: | cd terraform/proxmox terraform init -input=false set +e terraform plan -detailed-exitcode -input=false -no-color -out=plan.out 2>&1 | tee plan.log PLAN_EXIT=${PIPESTATUS[0]} set -e if [ "$PLAN_EXIT" -eq 2 ]; then echo "drift=true" >> "$GITHUB_OUTPUT" echo "### Proxmox drift detected" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" cat plan.log >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" elif [ "$PLAN_EXIT" -eq 0 ]; then echo "drift=false" >> "$GITHUB_OUTPUT" echo "Proxmox: no drift" >> "$GITHUB_STEP_SUMMARY" else exit $PLAN_EXIT fi - name: Terraform init + plan (dns) id: tf_dns env: TF_VAR_dynu_api_key: ${{ secrets.DYNU_API_KEY }} run: | cd terraform/dns terraform init -input=false set +e terraform plan -detailed-exitcode -input=false -no-color 2>&1 | tee plan.log PLAN_EXIT=${PIPESTATUS[0]} set -e if [ "$PLAN_EXIT" -eq 2 ]; then echo "drift=true" >> "$GITHUB_OUTPUT" echo "### DNS drift detected" >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" cat plan.log >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" fi - name: Notify on drift if: steps.tf_proxmox.outputs.drift == 'true' || steps.tf_dns.outputs.drift == 'true' env: GOTIFY_URL: ${{ secrets.GOTIFY_URL }} GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }} run: | curl -s -X POST "${GOTIFY_URL}/message" \ -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \ -F "title=Infrastructure drift detected" \ -F "message=Terraform plan found changes. Check the CI run for details." \ -F "priority=7" ansible-drift: name: Ansible — check mode runs-on: ubuntu-latest if: ${{ github.event.inputs.target == 'all' || github.event.inputs.target == 'ansible' || github.event_name == 'schedule' }} steps: - uses: actions/checkout@v4 - name: Install Ansible run: pip install ansible - name: Install collections run: ansible-galaxy collection install -r ansible/collections/requirements.yml - name: Write SSH key env: ANSIBLE_SSH_KEY: ${{ secrets.ANSIBLE_SSH_KEY }} run: | install -m 600 /dev/null /tmp/ansible_key echo "$ANSIBLE_SSH_KEY" > /tmp/ansible_key - name: Ansible check mode (site.yml) env: ANSIBLE_PRIVATE_KEY_FILE: /tmp/ansible_key ANSIBLE_HOST_KEY_CHECKING: "False" run: | cd ansible ansible-playbook playbooks/site.yml --check --diff \ -e "@inventory/group_vars/all.yml" \ 2>&1 | tee check.log # Summarise echo "### Ansible check summary" >> "$GITHUB_STEP_SUMMARY" grep -E '(changed|failed|ok)=' check.log | tail -20 >> "$GITHUB_STEP_SUMMARY" || true - name: Notify on changes detected if: failure() env: GOTIFY_URL: ${{ secrets.GOTIFY_URL }} GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }} run: | curl -s -X POST "${GOTIFY_URL}/message" \ -H "X-Gotify-Key: ${GOTIFY_TOKEN}" \ -F "title=Ansible drift detected" \ -F "message=ansible --check found pending changes. Check CI for details." \ -F "priority=7"