Files
claudetools/.claude/memory/feedback_365_remediation_tool.md
Mike Swanson ed2819ac87 sync: auto-sync from GURU-5070 at 2026-06-17 16:18:26
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-17 16:18:26
2026-06-17 16:18:44 -07:00

6.5 KiB

name: 365 Remediation Tool Reference description: "365 remediation tool" = ACG's tiered ComputerGuru Graph/EXO app suite via the /remediation-tool skill; NOT CIPP, and NOT the deprecated fabb3421 single-app type: feedback

When the user says "365 remediation tool" or "remediation tool", they mean ACG's direct Graph/Exchange tooling against customer tenants via the /remediation-tool skill (.claude/skills/remediation-tool/). This is NOT CIPP.

App suite (current — tiered): Security Investigator bfbc12a4 (Graph read + EXO read), Exchange Operator b43e7342 (EXO write), User Manager 64fac46b (user/license/MFA/pw write), Tenant Admin 709e6eed (high-priv directory), Defender Add-on dbf8ad1a (MDE-licensed tenants ONLY). Secrets in msp-tools/computerguru-*.sops.yaml. Client-credentials auth; tenant ID via OpenID discovery (or the *.onmicrosoft.com domain when the primary domain isn't verified). Use the lowest tier needed. Each app is consented per-tenant (URLs in references/gotchas.md); privileged ops also need directory roles assigned to the SP in that tenant (onboard-tenant.sh).

DELETED — gone, do not reference: fabb3421 ("AI Remediation" / "Claude-MSP-Access", secret msp-tools/claude-msp-access-graph-api.sops.yaml). Removed from the azcomputerguru.com tenant 2026-06-14; every token request now returns AADSTS700016. It previously had ~159 perms incl. Defender ATP (admin consent broke with AADSTS650052 on no-MDE tenants). Any skill still pointing at it is broken — repoint to the suite. (Original deprecation: 2026-05-27 Quantum onboarding.)

ACG OWN-mailbox reads/sends (/mailbox) — dedicated app 1873b1b0-3377-485c-a848-bae9b2f8f1f5 ("ComputerGuru Mailbox", vault msp-tools/computerguru-mailbox.sops.yaml, Mail.ReadWrite + Mail.Send + Contacts.ReadWrite, azcomputerguru.com single-tenant). Token via get-token.sh azcomputerguru.com mailbox (a tier in get-token.sh; cert-preferred). This is what REPLACED fabb3421 for /mailbox. Its SP is disabled when idle → a token 401 "account is disabled" means enable the SP first. (/mailbox command doc repointed to it 2026-06-17 — it had been left on the dead fabb3421.)

Why (original): user clarified "remediation tool" != CIPP after a wrong CIPP navigation. How to apply: prefer the /remediation-tool skill — it wraps tenant resolution, token caching, breach check, sweep, gated remediation, and consent/onboarding URLs (references/gotchas.md, graph-endpoints.md, checklist.md).

Directory Role Requirements (discovered 2026-04-01)

Graph API permissions alone are NOT sufficient for privileged operations. The service principal also needs Entra directory roles assigned per-tenant:

Operation Required Directory Role
Password reset User Administrator
Exchange transport rules, mailbox permissions Exchange Administrator

Roles assigned so far:

  • Valleywide Plastering (5c53ae9f...): User Administrator
  • Dataforth (7dfa3ce8...): User Administrator, Exchange Administrator
  • azcomputerguru.com (ce61461e...): full set assigned 2026-06-05 — Sec-Inv + Exch-Op = Exchange Administrator; Tenant Admin = Conditional Access Administrator; User Manager = User Administrator + Authentication Administrator.

For new tenants: onboard-tenant.sh <domain> assigns the directory roles programmatically (Tenant Admin tier) — no manual portal step needed. The app cannot self-assign; the Tenant Admin SP does it.

GOTCHA — pre-2026-04-20 tenants have NO directory roles. The directory-role assignment block was added to onboard-tenant.sh in commit cd50117a on 2026-04-20. Before that, "onboarding" only did app consent + Graph/EXO API permissions. So any tenant onboarded before that date has full app permissions but zero directory role assignments — Graph reads work, but Exchange REST (quarantine, Get-Mailbox, message trace) and other privileged ops 401 until you re-run onboard-tenant.sh. This is NOT a removal/breach — the roles were simply never assigned, and with no Entra ID P2 there's no PIM to auto-expire anything. ACG's own tenant hit exactly this on 2026-06-05 (EOP quarantine check 401'd). Re-run onboard-tenant.sh on any tenant onboarded before 2026-04-20 — Valleywide, Dataforth, Cascades are prime candidates to verify proactively. Confirm actual state with roleManagement/directory/roleAssignments?$filter=principalId%20eq%20'<sp-oid>'&$expand=roleDefinition (tenant-admin token; classic endpoint, no P2 needed — the PIM roleAssignmentSchedules endpoints return AadPremiumLicenseRequired without P2).

BUG (fixed 2026-06-05): onboard-tenant.sh role_assigned() had an unencoded space in its $filter (principalId eq '...'), so the query always failed → function always returned false → script always printed "MISSING -> ASSIGNING" and leaned on the conflict-tolerant POST for idempotency (assignment still worked, but PRESENT/MISSING reporting was meaningless). Fixed to %20. The old TODO blaming PIM was a misdiagnosis.

Exchange Online REST API

For Exchange cmdlets (Get-TransportRule, Add-MailboxPermission, Get-MessageTraceV2, Get-DkimSigningConfig, New-Mailbox, etc.), use scope https://outlook.office365.com/.default and POST to https://outlook.office365.com/adminapi/beta/$TENANT_ID/InvokeCommand with {"CmdletInput":{"CmdletName":"...", "Parameters":{...}}}. (Get-MessageTrace is deprecated → use Get-MessageTraceV2.) Fresh-onboard EXO app-only auth can 401 for ~15-60 min until the Exchange Admin role + Exchange.ManageAsApp replicate, even though Graph already works — wait and retry, it's propagation not misconfig.

Sending mail + the token-audience gotcha (corrected 2026-06-15)

Exchange Operator (b43e7342) ALSO holds Graph Mail.Send + Mail.ReadWrite + MailboxSettings.ReadWrite — so the suite CAN send as any mailbox in a consented tenant via Graph POST /users/<upn>/sendMail. (This corrects an earlier "the suite has no Mail.Send" conclusion — that came from reading the wrong token.) get-token.sh exchange-op returns an Exchange-Online-audience token (outlook.office365.com) whose roles claim does NOT list Graph scopes; to call Graph you must mint a Graph-audience token with the app's client creds (scope=https://graph.microsoft.com/.default). Never conclude a permission is missing from a wrong-audience token — check the app's actual Graph app-role assignments first. reference_cloudflare_access