fix: remediation tool onboarding — add RoleManagement.ReadWrite.Directory + auto role assignment

Root cause: app-only Graph operations (password reset, Exchange REST) require
directory roles on each SP in the customer tenant, not just admin consent.
RoleManagement.ReadWrite.Directory was missing from all app manifests, making
role assignment impossible without manual portal work that was never being done.

Changes:
- patch-tenant-admin-manifest.sh: adds RoleManagement.ReadWrite.Directory to
  Tenant Admin app manifest via Management app, grants home-tenant consent
- onboard-tenant.sh: new script — resolves tenant, acquires Tenant Admin token,
  assigns Exchange Administrator to Security Investigator SP and User/Auth
  Administrator to User Manager SP; --dry-run supported; idempotent
- get-token.sh: detects AADSTS7000229, emits consent URL + onboard-tenant.sh
  reminder instead of silent failure
- gotchas.md: onboarding steps at top, tenant table expanded with role columns,
  all known tenants updated including martylryan.com (first fully onboarded)

Verified: martylryan.com fully onboarded, password reset to MLR2026!! succeeded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 16:56:47 -07:00
parent 749a472089
commit cd50117aaf
4 changed files with 528 additions and 8 deletions

View File

@@ -52,6 +52,13 @@ case "$TIER" in
VAULT_PATH="msp-tools/computerguru-tenant-admin.sops.yaml"
SCOPE_URL="https://graph.microsoft.com/.default"
;;
tenant-admin-onboard)
# Same app as tenant-admin; this alias signals the token is being used
# during initial tenant onboarding (clearer error messages on failure).
CLIENT_ID="709e6eed-0711-4875-9c44-2d3518c47063"
VAULT_PATH="msp-tools/computerguru-tenant-admin.sops.yaml"
SCOPE_URL="https://graph.microsoft.com/.default"
;;
defender)
CLIENT_ID="dbf8ad1a-54f4-4bb8-8a9e-ea5b9634635b"
VAULT_PATH="msp-tools/computerguru-defender-addon.sops.yaml"
@@ -59,7 +66,7 @@ case "$TIER" in
;;
*)
echo "ERROR: unknown tier '$TIER'." >&2
echo "Valid tiers: investigator | investigator-exo | exchange-op | user-manager | tenant-admin | defender" >&2
echo "Valid tiers: investigator | investigator-exo | exchange-op | user-manager | tenant-admin | tenant-admin-onboard | defender" >&2
exit 2
;;
esac
@@ -130,6 +137,24 @@ RESP=$(curl -s --max-time 15 -X POST \
TOKEN=$(echo "$RESP" | jq -r '.access_token // empty')
if [[ -z "$TOKEN" ]]; then
ERROR_CODE=$(echo "$RESP" | jq -r '.error_codes[0] // empty' 2>/dev/null || true)
ERROR_DESC=$(echo "$RESP" | jq -r '.error_description // empty' 2>/dev/null || true)
# AADSTS7000229 — service principal not found in tenant (not consented)
if echo "$ERROR_DESC" | grep -qi "7000229\|AADSTS7000229" || [[ "$ERROR_CODE" == "7000229" ]]; then
echo "ERROR: AADSTS7000229 — app not consented in tenant $TENANT_ID (tier=$TIER)" >&2
echo "" >&2
echo " The '${TIER}' service principal has not been authorized in this tenant." >&2
echo " Send this consent URL to the customer Global Admin:" >&2
echo "" >&2
echo " https://login.microsoftonline.com/${TENANT_ID}/adminconsent?client_id=${CLIENT_ID}&redirect_uri=https://azcomputerguru.com&prompt=consent" >&2
echo "" >&2
echo " After the admin accepts, run onboard-tenant.sh to assign required directory roles:" >&2
SCRIPT_DIR_ERR="$(dirname "${BASH_SOURCE[0]}")"
echo " bash ${SCRIPT_DIR_ERR}/onboard-tenant.sh ${TARGET}" >&2
exit 5
fi
echo "ERROR: token request failed (tenant=$TENANT_ID tier=$TIER)" >&2
echo "$RESP" >&2
exit 5