121 lines
7.4 KiB
Markdown
121 lines
7.4 KiB
Markdown
---
|
|
name: onboard365
|
|
description: "Single-consent onboarding of a customer Microsoft 365 tenant to the ComputerGuru remediation app suite (Security Investigator / Exchange Operator / User Manager / Tenant Admin / Defender). The customer Global Admin clicks ONE admin-consent link (Tenant Admin); everything else — service principals, Graph/EXO/Defender permissions, and Entra directory roles — is provisioned automatically, no further clicks. Triggers: onboard 365, onboard a tenant, add tenant to remediation tools, single consent, consent link for new client, provision tenant apps, new M365 client onboarding, get a tenant ready for breach checks."
|
|
---
|
|
|
|
# Onboard365 — Single-Consent M365 Tenant Onboarding
|
|
|
|
Gets a customer M365 tenant ready for the `remediation-tool` suite with **one** customer
|
|
action: a single admin-consent click on the **ComputerGuru Tenant Admin** app. After that,
|
|
ACG provisions every other app and role programmatically using the Tenant Admin token — the
|
|
customer never sees five separate consent prompts.
|
|
|
|
This skill is a thin orchestrator. The provisioning logic, role GUIDs, and app IDs live in the
|
|
`remediation-tool` skill (`onboard-tenant.sh`) and are reused here so they never drift. Do NOT
|
|
duplicate that script — call it.
|
|
|
|
## Why "single consent"
|
|
|
|
Microsoft admin consent is per-application, so naively onboarding the 5-app suite would mean
|
|
5 customer clicks. We avoid that: the **Tenant Admin** app holds `Application.ReadWrite.All` +
|
|
`AppRoleAssignment.ReadWrite.All` + `RoleManagement.ReadWrite.Directory`. Once the customer
|
|
consents to Tenant Admin, our automation can, on their behalf:
|
|
|
|
1. Create the service principal for each other app (this IS admin consent for that app).
|
|
2. Grant every required Graph / Exchange Online / Defender app-role assignment.
|
|
3. Assign the required Entra directory roles to each SP.
|
|
|
|
Net: **one** customer click; the rest is `onboard-tenant.sh`.
|
|
|
|
## The flow
|
|
|
|
```
|
|
onboard365.sh <domain> # smart: prints the consent link if not yet consented,
|
|
# or provisions the whole suite if it is
|
|
onboard365.sh link <domain> # just print the single consent URL + customer instructions
|
|
onboard365.sh status <domain> # dry-run: show current consent/role state, change nothing
|
|
onboard365.sh provision <domain> # after the customer consents: provision all apps + roles
|
|
```
|
|
|
|
Script lives at `scripts/onboard365.sh` in this skill. It auto-locates the `remediation-tool`
|
|
scripts at `$HOME/.claude/skills/remediation-tool/scripts` (repo fallback via
|
|
`identity.json.claudetools_root`).
|
|
|
|
### Step-by-step (what to actually do)
|
|
|
|
1. **Identify the tenant.** Accept a domain (e.g. `acme.com`), an `.onmicrosoft.com`, or a
|
|
tenant GUID. Run `onboard365.sh link <domain>` to resolve it and produce the consent URL.
|
|
2. **Send the single link to the customer's Global Admin.** Use the template at
|
|
`references/customer-consent-instructions.md`. They sign in and click **Accept**. That is
|
|
the only thing they do. The app shown will be **"ComputerGuru Tenant Admin"**.
|
|
3. **Provision.** Once they confirm they accepted, run `onboard365.sh provision <domain>`
|
|
(or just `onboard365.sh <domain>` — it detects consent and proceeds). This runs
|
|
`onboard-tenant.sh`, which creates the other SPs, grants all permissions, and assigns the
|
|
directory roles. Watch the final status table — every row should be `OK` / `ASSIGNED`.
|
|
4. **Verify.** Re-run `onboard365.sh status <domain>` (dry-run). All roles should read
|
|
`PRESENT`. Optionally confirm an Exchange path with
|
|
`remediation-tool/scripts/assign-exchange-role.sh <domain> --verify`.
|
|
5. **Record it** (see Recording below).
|
|
|
|
## What gets provisioned (handled by onboard-tenant.sh — do not re-implement)
|
|
|
|
| App | Graph/EXO/Defender perms | Directory role assigned |
|
|
|---|---|---|
|
|
| Tenant Admin (consented by customer) | high-privilege Graph (incl. `Policy.Read.All` backfill) | Conditional Access Administrator |
|
|
| Security Investigator | Graph read + EXO read | Exchange Administrator |
|
|
| Exchange Operator | Graph + EXO write | Exchange Administrator |
|
|
| User Manager | Graph user/group/auth write | User Administrator + Authentication Administrator |
|
|
| Defender Add-on | Graph + Defender ATP | (Defender API; no directory role) — **MDE-licensed tenants only** |
|
|
|
|
The script auto-detects MDE licensing: if the Defender ATP resource SP isn't present, it
|
|
skips Defender cleanly (not an error).
|
|
|
|
## Recording (durable — do this after a successful provision)
|
|
|
|
The script provisions but does NOT write your records. After a clean run:
|
|
|
|
1. **Tenant registry — edit the REPO copy** (so it persists + syncs to the fleet), not the
|
|
applied global copy. Path: `$CLAUDETOOLS_ROOT/.claude/skills/remediation-tool/references/tenants.md`
|
|
(resolve `$CLAUDETOOLS_ROOT` from `identity.json`). Set the tenant's **Onboarded** column to
|
|
`YES` and add a dated Notes line listing what was consented + roles assigned. If the tenant
|
|
isn't in the table yet, add a row (Display Name | Domain | Tenant ID | Onboarded | Notes).
|
|
2. **Client wiki / CONTEXT.** If `wiki/clients/<slug>.md` exists, note tenant onboarding under
|
|
Cloud/M365 (tenant ID, "remediation suite onboarded YYYY-MM-DD, all apps + roles").
|
|
3. Use UTC dates.
|
|
|
|
## Conventions & guardrails
|
|
|
|
- **Outward-facing action.** Sending a consent link to a customer and provisioning apps in
|
|
their tenant is customer-facing. Confirm the target tenant with the user before generating
|
|
the link, and again before running `provision` (the Tenant Admin grant is high-privilege).
|
|
- **One link only.** Do not send the customer the per-app consent URLs unless `provision`
|
|
reports that a specific app failed programmatic consent — then `onboard-tenant.sh` prints the
|
|
fallback per-app URLs. The whole point is a single click.
|
|
- **Idempotent.** Re-running `provision` on an already-onboarded tenant is safe — every grant
|
|
and role assignment checks-before-creating and treats "already exists" as success.
|
|
- **Vault.** Token acquisition uses the SOPS vault via `identity.json.vault_path`. On a machine
|
|
where the skill resolves the wrong identity.json (no `vault_path`), export
|
|
`VAULT_ROOT_ENV=<vault path>` before running (known remediation-tool quirk).
|
|
- **Break-glass / least privilege.** This skill only provisions the standing app suite. It does
|
|
NOT touch customer user accounts, CA policies, or break-glass accounts — that's
|
|
`remediation-tool` territory.
|
|
|
|
## Common results / troubleshooting
|
|
|
|
- `[WARNING] Tenant Admin app not yet consented` (exit 2): the customer hasn't accepted yet, or
|
|
accepted as a non-Global-Admin. Re-send the link; confirm they're a Global Administrator.
|
|
- `AADSTS7000229`: the SP isn't in the tenant — same as not-consented; resend the link.
|
|
- A role row shows `ERROR`: usually transient Graph replication. Re-run `provision` once; if it
|
|
persists, the customer may need to re-accept the Tenant Admin consent (the script prints the
|
|
re-consent URL).
|
|
- `vault_path not set`: export `VAULT_ROOT_ENV` (see Vault above).
|
|
- Exchange tasks 403 later despite onboarding: run
|
|
`remediation-tool/scripts/assign-exchange-role.sh <domain>` — the Exchange Administrator role
|
|
on the Exchange Operator SP is the recurring gap; onboarding assigns it, but verify.
|
|
|
|
## Relationship to remediation-tool
|
|
|
|
Onboard365 = the front door (get a tenant consented + provisioned). `remediation-tool` = the
|
|
work (breach checks, sweeps, mailbox/user/CA remediation) once the tenant is onboarded. After a
|
|
successful onboard, a breach check is `remediation-tool` territory.
|