sync: auto-sync from GURU-5070 at 2026-07-01 09:32:17
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-07-01 09:32:17
This commit is contained in:
@@ -200,3 +200,4 @@
|
||||
- [GuruScan verification IN TEST / paused](project_guruscan_in_test_paused.md) — multi-engine scanner verify on DESKTOP-MS42HNC paused 2026-06-22 (VM rebooted mid-Emsisoft run); HitmanPro done (36 removed), Emsisoft full-scan unverified; resume `guruscan-agent-test.sh DESKTOP-MS42HNC scan-one Emsisoft`; Defender RTP/Tamper still off on VM
|
||||
- [GuruRMM fleet dispatch-hang fix](project_gururmm_dispatch_hang_fix.md) — blocking send_to on a full bounded channel to one black-holed agent wedged ALL command dispatch; fixed with try_send (9dae20c, deployed); proper black-hole eviction still missing (was reverted in 80df458) — finish it if it recurs
|
||||
- [Windows won't-boot / offline DISM repair playbook](windows-offline-dism-repair-gotchas.md) — Automatic Repair loop = boot-critical fault (disk/registry/wedged update), NOT shell/appx store corruption (that's a symptom); `FaultyPackageInProgress` + 100s of Install/Uninstall-Pending packages = wedged CU -> RevertPendingActions or clean install. Offline DISM rejects `wim:` source (0x800f082e) -> MOUNT the wim, source `\Windows`. Ventoy breaks WIM mount (0xc1420134) -> use Rufus. 25H2(26200)=24H2(26100)+enablement, so match 26100 media. First hit: Four Paws AvImark #32447.
|
||||
- [Remediation-tool has full M365 access (incl. SharePoint)](reference_remediation_tool_365_access.md) — the app suite covers Graph/EXO/Defender/SharePoint; don't declare "no access" on an accessDenied. SharePoint app-only needs a CERT (secret = "Unsupported app only token"); use get-token.sh `sharepoint`/`sharepoint-admin` tiers + CSOM admin API (Graph /admin/sharepoint/settings scope not held). Full map: skill references/app-permissions-and-sharepoint.md.
|
||||
|
||||
32
.claude/memory/reference_remediation_tool_365_access.md
Normal file
32
.claude/memory/reference_remediation_tool_365_access.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: reference_remediation_tool_365_access
|
||||
description: The remediation-tool app suite has full M365 access (incl. SharePoint via cert); don't declare "no access" on an accessDenied
|
||||
metadata:
|
||||
type: reference
|
||||
---
|
||||
|
||||
The ComputerGuru remediation-tool apps collectively have **broad, working access across ALL of
|
||||
M365** — Graph, Exchange Online, Defender, AND SharePoint Online. When a call fails it's almost
|
||||
always wrong-tier / wrong-endpoint / not-consented / the SharePoint cert gotcha — **not** a real
|
||||
lack of access. Do NOT tell the user "the tool can't do X" without checking the live permission
|
||||
map first (decode the token `roles` claim).
|
||||
|
||||
Key facts:
|
||||
- **SharePoint app-only requires a CERTIFICATE.** A `client_secret` token is rejected on every
|
||||
SharePoint endpoint (REST `/_api` and CSOM `/_vti_bin/client.svc/ProcessQuery`) with
|
||||
`"Unsupported app only token"`. The Tenant Admin app has a cert in the vault and holds
|
||||
SharePoint-resource `Sites.FullControl.All`.
|
||||
- `get-token.sh` now has **`sharepoint`** (content) and **`sharepoint-admin`** (tenant admin)
|
||||
tiers — cert-forced, tenant resource auto-resolved from Graph `/sites/root`
|
||||
(override `SP_RESOURCE_ENV`). Added 2026-07-01.
|
||||
- Graph `GET /admin/sharepoint/settings` needs `SharePointTenantSettings.Read.All`, which NO app
|
||||
holds → that route 403s. Read/write SharePoint tenant settings via the **CSOM admin API**
|
||||
(`sharepoint-admin` tier) instead. Tenant settings live on the Tenant object
|
||||
(TypeId `{268004ae-ef6b-4e9b-8425-127220d84719}`) — e.g. `SelfServiceSiteCreationDisabled`.
|
||||
- Restricting employee SharePoint site creation = `SelfServiceSiteCreationDisabled=true` (CSOM)
|
||||
AND restrict M365 Group creation (Entra `Group.Unified` directory setting via `user-manager`);
|
||||
neither affects edit rights on existing sites.
|
||||
|
||||
Full detail (live per-tier permission map + CSOM examples):
|
||||
`.claude/skills/remediation-tool/references/app-permissions-and-sharepoint.md`. Surfaced by
|
||||
Syncro #32492 (Birth Biologic). See also [[feedback_syncro_billing]].
|
||||
@@ -20,6 +20,9 @@ Five multi-tenant apps cover distinct privilege tiers. Use only what the task re
|
||||
| `user-manager` | ComputerGuru User Manager | `64fac46b-8b44-41ad-93ee-7da03927576c` | `computerguru-user-manager.sops.yaml` | Graph user/group write |
|
||||
| `tenant-admin` | ComputerGuru Tenant Admin | `709e6eed-0711-4875-9c44-2d3518c47063` | `computerguru-tenant-admin.sops.yaml` | Graph high-privilege |
|
||||
| `defender` | ComputerGuru Defender Add-on | `dbf8ad1a-54f4-4bb8-8a9e-ea5b9634635b` | `computerguru-defender-addon.sops.yaml` | Defender ATP (MDE only) |
|
||||
| `sharepoint` / `sharepoint-admin` | ComputerGuru Tenant Admin | `709e6eed-0711-4875-9c44-2d3518c47063` | `computerguru-tenant-admin.sops.yaml` | **SharePoint Online** (Sites.FullControl.All) — cert-only |
|
||||
|
||||
**The suite has broad, working access across ALL of M365 — Graph, Exchange Online, Defender, AND SharePoint Online.** Before concluding "the tool can't do X / access denied," verify against the live permission map in `references/app-permissions-and-sharepoint.md` (decode the token `roles` claim). An `accessDenied` usually means wrong tier or wrong endpoint for a scope we DO hold — not a real gap. Two recurring traps: (1) **SharePoint app-only requires a certificate** — a `client_secret` token is rejected everywhere in SharePoint with `"Unsupported app only token"` (get-token.sh forces cert for the `sharepoint*` tiers); (2) Graph `GET /admin/sharepoint/settings` needs a scope no app holds — read/write SharePoint tenant settings via the **CSOM/REST admin API** (`sharepoint-admin` tier) instead. Full map, gotchas, and CSOM examples: `references/app-permissions-and-sharepoint.md`.
|
||||
|
||||
**Default for breach checks:** use `investigator` (Graph) + `investigator-exo` (Exchange read). Escalate to write tiers only when remediating.
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# App permissions (authoritative) + SharePoint access
|
||||
|
||||
Purpose: stop the recurring "I can't do this / access denied" friction. The ComputerGuru
|
||||
app suite has **broad, working access across M365 — Graph, Exchange Online, Defender, AND
|
||||
SharePoint Online**. When a call fails, the cause is almost always one of: wrong tier, wrong
|
||||
endpoint for the scope we hold, a not-consented tenant (AADSTS7000229), or the SharePoint
|
||||
**secret-vs-cert** gotcha below — NOT a genuine lack of access.
|
||||
|
||||
**Discipline: before ever telling the user "the tool can't do X", verify against reality.**
|
||||
Decode the token's `roles` claim and check it against this map:
|
||||
|
||||
```bash
|
||||
TOK=$(bash scripts/get-token.sh <tenant> <tier>)
|
||||
python - "$TOK" <<'PY' # or: cut -d. -f2 | base64 -d | jq .roles
|
||||
import sys,base64,json
|
||||
p=sys.argv[1].split('.')[1]; p+='='*(-len(p)%4)
|
||||
print(json.dumps(json.loads(base64.urlsafe_b64decode(p)).get('roles',[]),indent=2))
|
||||
PY
|
||||
```
|
||||
|
||||
## Live-verified application-permission map (decoded 2026-07-01, Birth Biologic tenant)
|
||||
|
||||
Application (app-only) roles actually present in each app's token. Fetch live to confirm for
|
||||
a given tenant (consent can differ), but this is the baseline the apps are built to.
|
||||
|
||||
| Tier (get-token.sh) | App | Resource | Key application roles |
|
||||
|---|---|---|---|
|
||||
| `investigator` | Security Investigator | Graph | Directory.Read.All, User.Read.All, AuditLog.Read.All, Application.Read.All, Organization.Read.All, Policy.Read.All, **Sites.Read.All**, Mail.Read, MailboxSettings.Read, BitlockerKey.Read.All, IdentityRiskyUser.ReadWrite.All, IdentityRiskEvent.ReadWrite.All, IdentityRiskyServicePrincipal.ReadWrite.All, IdentityRiskyAgent.ReadWrite.All, UserAuthenticationMethod.Read.All |
|
||||
| `investigator-exo` | Security Investigator | Exchange Online | EXO read (Get-InboxRule, Get-Mailbox, Get-*) |
|
||||
| `exchange-op` | Exchange Operator | Exchange Online | EXO write (Set-Mailbox, Remove-InboxRule, revoke sessions) |
|
||||
| `user-manager` | User Manager | Graph | **Directory.ReadWrite.All, Group.ReadWrite.All, User.ReadWrite.All**, Device.ReadWrite.All, User.RevokeSessions.All, UserAuthenticationMethod.ReadWrite.All, Organization.Read.All |
|
||||
| `tenant-admin` | Tenant Admin | Graph | **Application.ReadWrite.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory, Directory.ReadWrite.All, Policy.ReadWrite.ConditionalAccess, Sites.FullControl.All, Sites.ReadWrite.All**, User.ReadWrite.All, SecurityEvents.Read.All, Policy.Read.All, UserAuthenticationMethod.ReadWrite.All |
|
||||
| `tenant-admin` | Tenant Admin | **SharePoint Online** | **Sites.FullControl.All** (SharePoint resource `00000003-0000-0ff1-ce00-000000000000`) — the SharePoint tiers use this |
|
||||
| `defender` | Defender Add-on | Defender ATP | MDE machine/alert actions (MDE-licensed tenants only) |
|
||||
| `intune-manager` | Intune Manager | Graph | DeviceManagement* (Intune) |
|
||||
| `mailbox` | ACG Mailbox | Graph | ACG-INTERNAL ONLY — Mail.ReadWrite/Send, Contacts.ReadWrite |
|
||||
|
||||
Notes:
|
||||
- **CA policies ARE manageable** (tenant-admin holds `Policy.ReadWrite.ConditionalAccess` + the Conditional Access Administrator role) — report-only + break-glass exclusion first (see SKILL scope boundaries).
|
||||
- **Directory settings** (M365 Group creation restriction, etc.) are writable via `user-manager` (Directory.ReadWrite.All) or `tenant-admin`.
|
||||
|
||||
## SharePoint Online — the two gotchas
|
||||
|
||||
1. **SharePoint app-only REQUIRES a certificate. A client_secret token is rejected** with
|
||||
`"Unsupported app only token"` on every SharePoint endpoint (REST `/_api/...` and CSOM
|
||||
`/_vti_bin/client.svc/ProcessQuery`). The Tenant Admin app has a cert in the vault
|
||||
(`cert_thumbprint_b64url` + `cert_private_key_pem_b64`); `get-token.sh` forces cert for the
|
||||
SharePoint tiers automatically. If you hand-roll a token, use the cert (client_assertion),
|
||||
not the secret.
|
||||
2. **The Graph endpoint `GET /admin/sharepoint/settings` needs `SharePointTenantSettings.Read.All`,
|
||||
which NONE of the apps currently hold** — so that route returns `accessDenied`. That is NOT
|
||||
"no SharePoint access". Use the SharePoint **CSOM/REST admin API** (cert token, `Sites.FullControl.All`)
|
||||
instead — that path has full tenant-settings read/write. (If you'd rather use the Graph route,
|
||||
add `SharePointTenantSettings.Read.All`/`.ReadWrite.All` to the Tenant Admin app manifest and
|
||||
re-consent — `patch-tenant-admin-manifest.sh` + the tenant admin-consent URL.)
|
||||
|
||||
## SharePoint tiers (get-token.sh)
|
||||
|
||||
```bash
|
||||
# Tenant admin resource ("<name>-admin.sharepoint.com") — tenant settings, site provisioning
|
||||
TOK=$(bash scripts/get-token.sh <tenant> sharepoint-admin)
|
||||
# Content resource ("<name>.sharepoint.com") — site/list/file operations
|
||||
TOK=$(bash scripts/get-token.sh <tenant> sharepoint)
|
||||
# Host is auto-resolved via Graph /sites/root; override if needed:
|
||||
SP_RESOURCE_ENV=contoso.sharepoint.com bash scripts/get-token.sh <tenant> sharepoint-admin
|
||||
```
|
||||
|
||||
### Read tenant settings (e.g. site-creation) via CSOM
|
||||
|
||||
```bash
|
||||
ADMIN="https://<name>-admin.sharepoint.com"
|
||||
TOK=$(bash scripts/get-token.sh <tenant> sharepoint-admin)
|
||||
BODY='<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="ACG"><Actions><ObjectPath Id="2" ObjectPathId="1"/><Query Id="3" ObjectPathId="1"><Query SelectAllProperties="true"><Properties/></Query></Query></Actions><ObjectPaths><Constructor Id="1" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}"/></ObjectPaths></Request>'
|
||||
curl -s -X POST "$ADMIN/_vti_bin/client.svc/ProcessQuery" \
|
||||
-H "Authorization: Bearer $TOK" -H "Content-Type: text/xml" --data-binary "$BODY" \
|
||||
| jq -r '.[] | select(type=="object") | to_entries[]
|
||||
| select(.key|test("SiteCreation|SelfService|Sharing";"i")) | "\(.key) = \(.value)"'
|
||||
```
|
||||
|
||||
Relevant tenant properties: `SelfServiceSiteCreationDisabled` (false = users CAN create sites),
|
||||
`AllowClassicPublishingSiteCreation`, `SharingCapability`. Writing a setting is the same CSOM
|
||||
surface with a `SetProperty` action (gate behind explicit YES, like any remediation write).
|
||||
|
||||
### Modern site creation = M365 Group creation
|
||||
|
||||
A modern team/communication site creates an M365 Group, gated by the Entra directory setting
|
||||
`Group.Unified` -> `EnableGroupCreation` / `GroupCreationAllowedGroupId`. Read via Graph
|
||||
`GET /groupSettings` (empty result = defaults = anyone can create). Write via `user-manager`
|
||||
(Directory.ReadWrite.All): POST/PATCH `/groupSettings` using the `Group.Unified` template.
|
||||
So fully restricting employee site creation = (a) `SelfServiceSiteCreationDisabled=true` (CSOM)
|
||||
**and** (b) restrict `Group.Unified` group creation (Graph). Neither touches edit rights on an
|
||||
existing site.
|
||||
|
||||
---
|
||||
_First documented 2026-07-01 (Mike). Trigger: Syncro #32492 (Birth Biologic SharePoint site-creation
|
||||
lockdown) surfaced the secret-vs-cert + no-SharePoint-tier gaps. Added `sharepoint`/`sharepoint-admin`
|
||||
tiers to get-token.sh the same day._
|
||||
@@ -38,6 +38,9 @@ else
|
||||
TENANT_ID=$("$SCRIPT_DIR/resolve-tenant.sh" "$TARGET")
|
||||
fi
|
||||
|
||||
# SharePoint tiers resolve their (tenant-specific) resource host after decrypt; default empty.
|
||||
SP_KIND=""
|
||||
|
||||
# Map tier -> client_id, vault SOPS path, resource scope
|
||||
case "$TIER" in
|
||||
investigator)
|
||||
@@ -91,9 +94,23 @@ case "$TIER" in
|
||||
VAULT_PATH="msp-tools/computerguru-mailbox.sops.yaml"
|
||||
SCOPE_URL="https://graph.microsoft.com/.default"
|
||||
;;
|
||||
sharepoint|sharepoint-admin)
|
||||
# SharePoint Online. Uses the Tenant Admin app, which holds the SharePoint-resource
|
||||
# role Sites.FullControl.All. SharePoint app-only REJECTS client_secret tokens with
|
||||
# "Unsupported app only token" — CERT AUTH IS MANDATORY (the cert is in the vault).
|
||||
# The resource is tenant-specific, so it can't be a fixed SCOPE_URL like Graph; it is
|
||||
# resolved from Graph /sites/root after decrypt. Override with SP_RESOURCE_ENV=<host>.
|
||||
# sharepoint -> content resource ("<name>.sharepoint.com") site/list/file CSOM+REST
|
||||
# sharepoint-admin -> tenant admin resource ("<name>-admin.sharepoint.com") tenant settings via CSOM ProcessQuery
|
||||
CLIENT_ID="709e6eed-0711-4875-9c44-2d3518c47063"
|
||||
VAULT_PATH="msp-tools/computerguru-tenant-admin.sops.yaml"
|
||||
SCOPE_URL=""
|
||||
SP_KIND="content"; [[ "$TIER" == "sharepoint-admin" ]] && SP_KIND="admin"
|
||||
REMEDIATION_AUTH="cert" # SharePoint app-only requires a certificate
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown tier '$TIER'." >&2
|
||||
echo "Valid tiers: investigator | investigator-exo | exchange-op | user-manager | tenant-admin | tenant-admin-onboard | defender | intune-manager | mailbox" >&2
|
||||
echo "Valid tiers: investigator | investigator-exo | exchange-op | user-manager | tenant-admin | tenant-admin-onboard | defender | intune-manager | mailbox | sharepoint | sharepoint-admin" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -281,6 +298,28 @@ esac
|
||||
|
||||
echo "[INFO] auth=$AUTH_METHOD" >&2
|
||||
|
||||
# SharePoint tiers: resolve the tenant-specific resource host (SharePoint's resource URL is
|
||||
# per-tenant, unlike Graph's fixed URL). Uses a Graph /sites/root lookup (Sites.Read via the
|
||||
# Tenant Admin app), or SP_RESOURCE_ENV if the caller supplies the host explicitly.
|
||||
if [[ -z "$SCOPE_URL" && -n "${SP_KIND:-}" ]]; then
|
||||
SP_HOST="${SP_RESOURCE_ENV:-}"
|
||||
if [[ -z "$SP_HOST" ]]; then
|
||||
SP_GRAPH_TOKEN=$("$SCRIPT_DIR/get-token.sh" "$TENANT_ID" tenant-admin 2>/dev/null || true)
|
||||
if [[ -n "$SP_GRAPH_TOKEN" ]]; then
|
||||
SP_HOST=$(curl -s --max-time 15 "https://graph.microsoft.com/v1.0/sites/root" \
|
||||
-H "Authorization: Bearer $SP_GRAPH_TOKEN" | jq -r '.siteCollection.hostname // empty')
|
||||
fi
|
||||
fi
|
||||
if [[ -z "$SP_HOST" ]]; then
|
||||
echo "ERROR: could not resolve SharePoint host for tenant $TENANT_ID." >&2
|
||||
echo " Set SP_RESOURCE_ENV=<tenant>.sharepoint.com and retry." >&2
|
||||
exit 6
|
||||
fi
|
||||
[[ "$SP_KIND" == "admin" ]] && SP_HOST="${SP_HOST/.sharepoint.com/-admin.sharepoint.com}"
|
||||
SCOPE_URL="https://${SP_HOST}/.default"
|
||||
echo "[INFO] sharepoint resource: $SCOPE_URL" >&2
|
||||
fi
|
||||
|
||||
# Build request and POST.
|
||||
TOKEN_URL="https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user