---
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`).

**DEPRECATED — do NOT consent to customer tenants:** `fabb3421` ("AI Remediation" / "Claude-MSP-Access", secret `msp-tools/claude-msp-access-graph-api.sops.yaml`). ~159 perms incl. Defender ATP, so admin consent **breaks with AADSTS650052 on any tenant lacking an MDE license**. It still works where already consented (e.g. ACG's own tenant — the `/mailbox` skill reads our own mailboxes with it), but new onboarding MUST use the tiered suite. (Corrected 2026-05-27 during Quantum onboarding — nearly consented the deprecated app to a no-MDE tenant.)

**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, etc.), use scope `https://outlook.office365.com/.default` and POST to `https://outlook.office365.com/adminapi/beta/$TENANT_ID/InvokeCommand` with `{"CmdletInput":{"CmdletName":"...", "Parameters":{...}}}`.
