Files
claudetools/.claude/skills/msp360/SKILL.md
Howard Enos 976bfe7957 sync: auto-sync from HOWARD-HOME at 2026-07-05 19:59:52
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-07-05 19:59:52
2026-07-05 20:00:20 -07:00

6.1 KiB

name, description
name description
msp360 Manage ACG's MSP360 Managed Backup Service (MBS) tenant via the provider REST API: the authoritative fleet backup-status source (who is backing up, last run, failures, stale plans) plus user/company/license provisioning. Reads run freely; writes (create/delete user or company, grant/release/revoke license) are gated --confirm. Triggers: msp360, mspbackups, managed backup, is X backing up, backup status, backup failures, create backup user, add backup company, grant backup license.

MSP360 Managed Backup Service Skill

Live client for ACG's MSP360 Managed Backup Service (MBS) provider tenant. This is the authoritative answer to "is client X actually backing up" — the monitoring feed reports every backup plan's last run, status, and data across the whole fleet. B2/Seafile/ownCloud are just where the data lands; MSP360 is where you see whether a backup ran.

Built as a sibling to the b2 / seafile / owncloud / datto-workplace backup skills; shares .claude/scripts/backup_common.py for repo-root/vault/HTTP plumbing.

Running

MSP=".claude/skills/msp360/scripts/msp360.py"

# --- reads (free) ---
py "$MSP" status                              # reachable + counts + plans needing attention
py "$MSP" monitoring                          # every plan: company/computer/status/last-run/data
py "$MSP" monitoring --failed                 # only plans NOT in a clean state (the triage view)
py "$MSP" monitoring --company Dataforth       # scope to one client
py "$MSP" monitoring --stale 30               # plans whose last run is >=30 days old (or never)
py "$MSP" companies                           # 37 companies with ids + storage limits
py "$MSP" users --company "Business Services"  # backup users (+ space used) in a company
py "$MSP" licenses --available                 # free vs taken licenses
py "$MSP" billing --json                       # billing feed
py "$MSP" raw GET /api/Users/{id}              # generic passthrough for any GET endpoint

# --- writes (require --confirm; without it you get a dry-run preview) ---
py "$MSP" create-company --name "New Client LLC" --confirm
py "$MSP" create-user --email backup@client.com --company "New Client LLC" \
       --first Jane --last Doe --notify admin@acg.com --confirm
py "$MSP" grant-license   --user-id <UUID> --license-id <UUID> --confirm
py "$MSP" release-license --user-id <UUID> --confirm
py "$MSP" delete-user  --id <UUID> [--keep-data] --confirm
py "$MSP" delete-company --id <UUID> --confirm
py "$MSP" raw POST /api/Accounts/CreateDestination --data '{...}' --confirm

Add --json to any read for machine-readable output. --base overrides the API URL.

Credentials

Loaded at runtime from the SOPS vault (never hardcoded): msp-tools/msp360-api.sops.yaml -> credentials.login / credentials.password. These are the API-specific creds generated in the MSP360 console (Settings > General) by the main admin account — NOT the console login. Auth flow: POST /api/Provider/Login {UserName, Password} -> Bearer access_token (per-run, never cached to disk) -> Authorization: Bearer <token> on every request. Base: https://api.mspbackups.com.

What "backing up?" looks like

monitoring returns one row per backup plan with Status (0/1=Success, 2=Warning, 3=Failed, 4=Running, 6=Interrupted, 7=Completed-with-warnings), LastStart, DataCopied, CompanyName, ComputerName, PlanName. Key reads:

  • A company with 0 rows in monitoring is not backing up even if it exists (this is how the GPS audit found Arizona Medical Transit + T&C Sorensen billed-but-unprotected).
  • --failed surfaces failing/warning plans; --stale N surfaces plans that stopped running.
  • A B2 bucket with files does NOT prove a recent backup — always confirm with monitoring.

Writes & safety

  • Every write is gated --confirm. Without it the command prints a [DRY-RUN] line (and, for create-*, the exact JSON payload) and changes nothing.
  • delete-user defaults to removing metadata and all backup data; pass --keep-data to drop only the metadata (DELETE /api/Users/{id}/Account) and retain the backups.
  • delete-company / delete-user print a [WARNING] describing the blast radius first.
  • create-user / grant-license payloads are built from the live API schema (User fields: Email/FirstName/LastName/Company/Enabled/NotificationEmails; grant: UserID + LicenseID). On first real provisioning, eyeball the API's response — it returns the created object or a validation error, which the command prints verbatim.

Full endpoint surface (via raw when a typed command doesn't exist)

The MBS API groups (all reachable through raw <METHOD> <path>): Provider (login), Users (GET/POST/PUT, DELETE /{id} or /{id}/Account), Companies (GET/POST/PUT/DELETE), Monitoring (GET), Licenses (GET, POST Grant/Release/Revoke), Destinations (GET/POST/PUT/DELETE), Accounts (GET/POST/PUT + Add/Create/Edit/RemoveDestination), Packages (GET/POST/PUT/DELETE), Billing (GET/PUT), Builds (GET, POST RequestCustomBuilds), Administrators (GET/POST/PUT/DELETE). Reference: https://help.mspbackups.com/mbs-api-specification/methods.

Notes / gotchas

  • /api/Computers returns HTTP 400 "Remote Management API methods are not enabled" — that feature is off on this account. computers prints a clean [INFO] and points you at users + monitoring, which cover endpoint/backup data without it.
  • Git-Bash path mangling: a leading-slash raw path like /api/Users gets rewritten to C:/Program Files/Git/api/Users by MSYS. The raw command auto-recovers the /api/... tail, so pass the path normally.
  • MSP360 CompanyName is the join key to B2 buckets (e.g. ACG-PST=Peaceful Spirit, ACG-TCA=Tucson Coin and Autograph) — useful when reconciling destinations.
  • Backup targets are a per-client decision: report mismatches, don't provision jobs off a billing count. See memory feedback_backup_targets_never_guessed + reference_msp360_backup_monitoring.
  • Failures are logged to the fleet errorlog.md via the canonical helper; expected conditions (feature-gated endpoint, no matching rows) are not.