---
description: M365 tenant investigation + remediation via the ComputerGuru tiered MSP app suite. Breach checks, tenant sweeps, consent URLs, and gated remediation actions.
---

# /remediation-tool

M365 investigation and remediation using the **ComputerGuru tiered MSP app suite** — five multi-tenant apps covering read-only investigation, Exchange write operations, user lifecycle management, high-privilege tenant admin, and optional Defender ATP.

**Default posture: READ-ONLY.** Remediation actions require explicit `YES` confirmation in chat.

---

## App Tiers (quick reference)

| Tier flag | App | App ID | Use for |
|---|---|---|---|
| `investigator` | ComputerGuru Security Investigator | `bfbc12a4` | All read-only breach checks via Graph |
| `investigator-exo` | ComputerGuru Security Investigator | `bfbc12a4` | Exchange read: Get-InboxRule (hidden), Get-Mailbox, permissions |
| `exchange-op` | ComputerGuru Exchange Operator | `b43e7342` | Exchange write: Set-Mailbox, Remove-InboxRule, session revoke |
| `user-manager` | ComputerGuru User Manager | `64fac46b` | User create/disable, license assign, MFA reset, password reset |
| `tenant-admin` | ComputerGuru Tenant Admin | `709e6eed` | App role assignments, CA policy, high-privilege directory |
| `defender` | ComputerGuru Defender Add-on | `dbf8ad1a` | Alerts, machine risk, vuln data — MDE-licensed tenants only |

Pass the tier flag to `get-token.sh`:
```bash
bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> <tier>
```

---

## Subcommands

| Form | What it does |
|---|---|
| `/remediation-tool check <upn>` | 10-point breach check on a single user |
| `/remediation-tool sweep <domain>` | Tenant-wide signals (sign-ins, audits, risky users, guests) |
| `/remediation-tool signins <domain> [--user upn] [--failed-only] [--days N]` | Ad-hoc sign-in query |
| `/remediation-tool consent-url <domain> [--app <tier>]` | Emit admin consent URL for a tenant + app |
| `/remediation-tool remediate <upn> <action>` | **GATED:** revoke-sessions, disable-forwarding, remove-inbox-rules, disable-account, password-reset |

`<domain>` accepts a tenant domain (`cascadestucson.com`), a UPN (`user@domain.com`), or a tenant GUID.

---

## Workflow Claude should follow

### 0. Parse invocation

- Extract subcommand, target, and any flags from `$ARGUMENTS`.
- Normalize: UPN -> domain (split on `@`), domain -> look up tenant-id.
- If the target is ambiguous or missing, ask the user once and proceed.

### 1. Resolve tenant ID

Run `bash .claude/skills/remediation-tool/scripts/resolve-tenant.sh <domain>` — returns tenant GUID via OpenID discovery. If it fails, the domain is not in Entra ID; surface the error and stop.

### 2. Acquire tokens (cached)

Use the minimum-privilege tier for the task. Most breach checks only need:
```bash
GT=$(bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> investigator)
ET=$(bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> investigator-exo)
```

Escalate to write tiers only for remediation:
```bash
# Exchange write (disable-forwarding, remove-inbox-rules)
EXO_WRITE=$(bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> exchange-op)

# User write (revoke-sessions, disable-account, password-reset, MFA reset)
UT=$(bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> user-manager)

# Defender (MDE tenants only)
DT=$(bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> defender)
```

Tokens cache at `/tmp/remediation-tool/{tenant}/{tier}.jwt` with 55-minute TTL.

If a token returns 403/401 on first use, check `.claude/skills/remediation-tool/references/gotchas.md` for per-tenant prerequisites and emit the appropriate consent or role-assignment link.

### 3. Run the requested checks

- **`check <upn>`** -> `bash scripts/user-breach-check.sh <tenant> <upn>`. Runs all 10 checks and dumps raw JSON to `/tmp/remediation-tool/{tenant}/user-breach/<slug>/`. Interpret against `references/checklist.md` and write report.

- **`sweep <domain>`** -> `bash scripts/tenant-sweep.sh <tenant>`. Pulls tenant-wide failed sign-ins (30d), successful non-US sign-ins, directory audits filtered for consent/auth-method/service-principal changes, risky users, B2B guest invites. Claude summarizes priority findings.

- **`signins`** — build ad-hoc `curl` against Graph `/auditLogs/signIns` with the requested filter. Use `investigator` tier.

- **`consent-url <domain> [--app <tier>]`** — emit the appropriate admin consent URL (see below). Default to Security Investigator (`investigator`) unless `--app` specifies another tier.

- **`remediate`** — see Remediation section below.

### 4. Write the report

Location: `clients/{client-slug}/reports/YYYY-MM-DD-{action}.md` (UTC date). Derive client slug from domain:
- `cascadestucson.com` -> `cascades-tucson`
- `grabblaw.com` -> `grabblaw`
- Use existing `clients/<slug>/` directory if present; if no match, ask the user for the slug.

Use `templates/breach-report.md` as skeleton. For single-user checks, fill per-check findings from raw JSON.

### 5. Summarize to the user

Short chat summary: top findings, blocked checks (with remediation links), next actions. Save raw JSON artifact paths in the report.

### 6. Auto-commit

After writing the report, delegate to the **Gitea Agent** to commit with `Remediation report: <action> for <target>`. Do not push unless the user asks.

---

## Admin Consent URLs

Each app must be individually consented in each customer tenant. Consent URL format:

```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id={app-id}&redirect_uri=https://azcomputerguru.com&prompt=consent
```

**Security Investigator** (read-only — consent this first):
```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id=bfbc12a4-f0dd-4e12-b06d-997e7271e10c&redirect_uri=https://azcomputerguru.com&prompt=consent
```

**Exchange Operator** (EXO write — consent when remediation needed):
```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id=b43e7342-5b4b-492f-890f-bb5a4f7f40e9&redirect_uri=https://azcomputerguru.com&prompt=consent
```

**User Manager** (user/license write):
```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id=64fac46b-8b44-41ad-93ee-7da03927576c&redirect_uri=https://azcomputerguru.com&prompt=consent
```

**Tenant Admin** (high-privilege — use sparingly):
```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id=709e6eed-0711-4875-9c44-2d3518c47063&redirect_uri=https://azcomputerguru.com&prompt=consent
```

**Defender Add-on** (MDE-licensed tenants only):
```
https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id=dbf8ad1a-54f4-4bb8-8a9e-ea5b9634635b&redirect_uri=https://azcomputerguru.com&prompt=consent
```

The customer admin must sign in as Global Admin of that tenant and click Accept. Redirect lands on azcomputerguru.com — that is expected. Verify consent via `/servicePrincipals/{sp-id}/appRoleAssignments` (new grants should be timestamped today).

---

## Remediation (gated)

When the user runs `/remediation-tool remediate <upn> <action>`:

1. **Confirm read-only context first**: skill must have recently run `check <upn>` in this session (check `/tmp/remediation-tool/{tenant}/user-breach/<slug>/` exists). If not, tell the user to run the check first.
2. **Display the exact action** (curl command, cmdlet name, parameters).
3. **Require explicit `YES` in chat** — not a permission prompt. Anything else aborts.
4. Execute via the appropriate app tier. Capture response to `/tmp/remediation-tool/{tenant}/remediation/<slug>-YYYY-MM-DDTHHMMSS.json`.
5. Update the user's report with a `## Remediation Actions` section.

Allowed actions and which tier handles them:

| Action | App tier | API |
|---|---|---|
| `revoke-sessions` | `user-manager` | Graph `POST /users/{upn}/revokeSignInSessions` |
| `disable-account` | `user-manager` | Graph `PATCH /users/{upn}` with `accountEnabled: false` |
| `password-reset` | `tenant-admin` | `scripts/reset-password.sh <tenant> <upn> <new-pw> [--force-change]` (Graph `PATCH /users/{upn}` passwordProfile, with JIT admin elevation — see note) |
| `disable-forwarding` | `exchange-op` | Exchange REST `Set-Mailbox -ForwardingAddress $null -ForwardingSmtpAddress $null -DeliverToMailboxAndForward $false` |
| `remove-inbox-rules` | `exchange-op` | Exchange REST `Remove-InboxRule` per non-default rule (ask which to keep first) |
| `disable-smtp-auth` | `exchange-op` | Exchange REST `Set-CASMailbox -SmtpClientAuthenticationDisabled $true` |

**Password reset of admin-role accounts (JIT elevation):** A plain `passwordProfile` PATCH works for ordinary members but returns `403 Authorization_RequestDenied` when the target holds a directory role (SharePoint/Teams/User Admin, etc.) — Microsoft requires the caller to be Global Administrator or **Privileged Authentication Administrator** to reset an admin's password. `scripts/reset-password.sh` handles this: it tries the direct reset, and on 403 it assigns the Tenant Admin service principal the Privileged Authentication Administrator role (the app holds `RoleManagement.ReadWrite.Directory`), retries, then **removes the role assignment it created** (de-elevates). If the SP already held the role, it is left untouched. Default `forceChangePasswordNextSignIn=false` (permanent — right for shared/service accounts); pass `--force-change` for a user who must change at next sign-in. Requires the tenant to have consented the Tenant Admin app. (Pattern added 2026-06-08 — birthbiologic.com operations@ was a SharePoint+Teams Admin, blocking the plain reset.)

---

## Arguments

`$ARGUMENTS` — the full invocation text. Parse freely; common forms:

- `check john.trozzi@cascadestucson.com`
- `sweep cascadestucson.com`
- `signins cascadestucson.com --user megan.hiatt@cascadestucson.com --failed-only --days 30`
- `consent-url cascadestucson.com`
- `consent-url grabblaw.com --app exchange-op`
- `remediate megan.hiatt@cascadestucson.com revoke-sessions`

If the user's phrasing is loose ("check john's box at cascades", "who's being attacked"), infer intent from CONTEXT.md and session logs. Prefer asking one clarifying question to guessing.

---

## Syncro Ticket Creation (after remediation or check)

When creating a Syncro ticket to log remediation or breach-check work — whether via `/syncro` at the end of the session or inline during the workflow — the following fields are **REQUIRED** and must always be present in the POST payload. Omitting any of them leaves the ticket unusable in the queue.

**Required fields — no exceptions:**

| Field | Rule |
|---|---|
| `priority` | Always `"2 Normal"` unless the incident is active/emergency, in which case `"4 Urgent"` |
| `user_id` | Always the API key owner's user ID: `mike` → `1735`, `howard` → `1750`, `winter` → `1737`. Never omit — never null |
| `problem_type` | Use `"Security"` for breach checks, tenant sweeps, MFA enforcement, account compromise. Use `"Remote"` for general M365 remote support. Never use `"Remote Support"` — it is not a valid Syncro dropdown value and will appear blank in the GUI |

**Payload template for POST /tickets:**

```bash
curl -s -X POST "${BASE}/tickets?api_key=${API_KEY}" \
  -H "Content-Type: application/json" \
  --data-binary @- <<JSON
{
  "customer_id": ${CUST_ID},
  "subject": "<subject>",
  "problem_type": "Security",
  "status": "New",
  "priority": "2 Normal",
  "user_id": ${TECH_USER_ID}
}
JSON
```

**Enforcement checklist — verify before POSTing:**
1. `priority` is set (not null, not omitted)
2. `user_id` is set to the correct tech ID (not null, not omitted)
3. `problem_type` is one of the valid Syncro dropdown values listed above

If any check fails, fix the payload before sending. Do not POST a ticket with missing required fields.

---

## Scope and references

- Detailed check rubric: `.claude/skills/remediation-tool/references/checklist.md`
- Permission/role gotchas + consent URLs: `.claude/skills/remediation-tool/references/gotchas.md`
- Endpoint cheatsheet: `.claude/skills/remediation-tool/references/graph-endpoints.md`
- Report template: `.claude/skills/remediation-tool/templates/breach-report.md`
