#!/usr/bin/env bash # gps-rmm-progress-check.sh — daily GPS->GuruRMM enrollment progress check. # # Reads projects/gps-rmm-audit/targets.json (GPS device target per client), # pulls live GuruRMM /api/agents, computes per-client enrollment gaps, and DMs # Howard a one-message summary. When every tracked client has met its target it # reports "COMPLETE" so the scheduled task can be retired. # # Usage: # bash gps-rmm-progress-check.sh # check + DM Howard # bash gps-rmm-progress-check.sh --dry-run # check + print, no Discord # # Registered as a daily Windows scheduled task. Read-only against RMM. set -u ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" TARGETS="$ROOT/projects/gps-rmm-audit/targets.json" DRY=0; [ "${1:-}" = "--dry-run" ] && DRY=1 [ -f "$TARGETS" ] || { echo "[ERROR] targets.json not found at $TARGETS" >&2; exit 1; } # --- auth + fetch agents --- eval "$(bash "$ROOT/.claude/scripts/rmm-auth.sh" 2>/dev/null)" >/dev/null if [ -z "${TOKEN:-}" ] || [ -z "${RMM:-}" ]; then echo "[ERROR] RMM auth failed" >&2 bash "$ROOT/.claude/scripts/log-skill-error.sh" "gps-rmm-progress-check" "RMM auth failed" >/dev/null 2>&1 exit 1 fi AGENTS=$(curl -s "$RMM/api/agents" -H "Authorization: Bearer $TOKEN" | tr -d '\000-\037') [ "${AGENTS:0:1}" = "[" ] || { echo "[ERROR] /api/agents not an array: ${AGENTS:0:100}" >&2; \ bash "$ROOT/.claude/scripts/log-skill-error.sh" "gps-rmm-progress-check" "GET /api/agents non-array" >/dev/null 2>&1; exit 1; } # --- per-client live agent counts (unique hostnames, to ignore dup agent rows) --- COUNTS=$(echo "$AGENTS" | jq -r '[.[] | {c:.client_name, h:(.hostname|ascii_downcase)}] | group_by(.c) | map({client:.[0].c, n:( [.[].h] | unique | length )}) | .[] | "\(.n)\t\(.client)"') # --- compare targets vs live --- REPORT=""; DONE_ALL=1; TOTAL_TARGET=0; TOTAL_HAVE=0; GAP_CLIENTS=0 while IFS=$'\t' read -r TGT NAME BUCKET; do [ -z "$NAME" ] && continue HAVE=$(echo "$COUNTS" | awk -F'\t' -v n="$NAME" '$2==n{print $1; found=1} END{if(!found)print 0}' | head -1) HAVE=${HAVE:-0} TOTAL_TARGET=$((TOTAL_TARGET + TGT)) TOTAL_HAVE=$((TOTAL_HAVE + HAVE)) if [ "$HAVE" -lt "$TGT" ]; then DONE_ALL=0; GAP_CLIENTS=$((GAP_CLIENTS+1)) GAP=$((TGT - HAVE)) STATE=$([ "$HAVE" -eq 0 ] && echo "NO ORG/AGENTS" || echo "short") REPORT="${REPORT} - ${NAME}: ${HAVE}/${TGT} in RMM (${STATE}, gap ${GAP}) [${BUCKET}]\n" fi done < <(jq -r '.clients[] | "\(.target)\t\(.client)\t\(.bucket)"' "$TARGETS") DATE=$(date +%Y-%m-%d) if [ "$DONE_ALL" -eq 1 ]; then MSG="**GPS->RMM enrollment: COMPLETE (${DATE})** All tracked GPS clients meet their RMM device target (${TOTAL_HAVE}/${TOTAL_TARGET}). You can retire the daily check task (schtasks /Delete /TN GPS-RMM-Progress)." else MSG="**GPS->RMM enrollment check ${DATE}** — ${TOTAL_HAVE}/${TOTAL_TARGET} devices in RMM; ${GAP_CLIENTS} clients still short:\n${REPORT}(Glaz-Tech excluded pending billing review. Source: projects/gps-rmm-audit/targets.json)" fi if [ "$DRY" -eq 1 ]; then echo -e "$MSG" exit 0 fi echo -e "$MSG" | bash "$ROOT/.claude/scripts/discord-dm.sh" howard