Add .claude/scripts/log-skill-error.sh — the canonical agent error log helper (writes errorlog.md in DATE | MACHINE | skill | [type] error format, soft-fails). Three categories: execution failures (default), user corrections (--correction), and preventable self-inflicted friction (--friction; cite ref= when it repeats a documented gotcha). Goal: stop paying tokens twice for the same avoidable mistake. - CLAUDE.md: make logging mandatory for all skills + corrections + friction. - skill-creator: new skills must wire in the helper (guidance + checklist). - Retrofit every skill script's genuine failure branches to call the helper (b2/bitdefender/mailprotector/packetdial/coord python CLIs; remediation-tool + onboard365 bash; vault, rmm-auth, post-bot-alert, agy, grok, 1password, run-onboarding-diagnostic). Handled conditions + self-tests left alone. - errorlog.md: broaden header to cover skills + harness + corrections; seed this session's corrections (INKY, Mail.Send token-audience, omnibox-strictness) and friction (git-bash /tmp, env-persistence, argv-limit, PowerShell var-case). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
4.5 KiB
Bash
116 lines
4.5 KiB
Bash
#!/usr/bin/env bash
|
|
# Onboard365 — single-consent onboarding of a customer M365 tenant to the
|
|
# ComputerGuru remediation app suite.
|
|
#
|
|
# The customer Global Admin consents ONCE to the "ComputerGuru Tenant Admin" app.
|
|
# Everything else (the other SPs, their Graph/EXO/Defender permissions, and the
|
|
# Entra directory roles) is provisioned programmatically by the reused
|
|
# remediation-tool/onboard-tenant.sh — no further customer clicks.
|
|
#
|
|
# Usage:
|
|
# onboard365.sh <domain|tenant-id> # smart: link if not consented, else provision
|
|
# onboard365.sh link <domain|tenant-id> # print the ONE consent URL + customer steps
|
|
# onboard365.sh status <domain|tenant-id> # dry-run: show current consent/role state
|
|
# onboard365.sh provision <domain|tenant-id> # after consent: provision all apps + roles
|
|
#
|
|
# Exit codes mirror onboard-tenant.sh for provision/status (0 ok, 2 not consented,
|
|
# 10 partial). link always exits 0.
|
|
set -euo pipefail
|
|
|
|
TENANT_ADMIN_APPID="709e6eed-0711-4875-9c44-2d3518c47063"
|
|
CONSENT_BASE="https://login.microsoftonline.com"
|
|
CONSENT_REDIRECT="https://azcomputerguru.com"
|
|
__ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)}"
|
|
|
|
# ── Locate the reused remediation-tool scripts ────────────────────────────────
|
|
# Prefer the applied global copy (stable path on every fleet machine); fall back
|
|
# to the repo copy via identity.json.claudetools_root.
|
|
find_rtool() {
|
|
local cands=("$HOME/.claude/skills/remediation-tool/scripts")
|
|
local idf="$HOME/.claude/identity.json"
|
|
if [[ -f "$idf" ]] && command -v jq >/dev/null 2>&1; then
|
|
local root
|
|
root=$(jq -r '.claudetools_root // empty' "$idf" 2>/dev/null || true)
|
|
[[ -n "$root" ]] && cands+=("$root/.claude/skills/remediation-tool/scripts")
|
|
fi
|
|
local c
|
|
for c in "${cands[@]}"; do
|
|
[[ -f "$c/onboard-tenant.sh" ]] && { echo "$c"; return 0; }
|
|
done
|
|
return 1
|
|
}
|
|
|
|
RT="$(find_rtool)" || {
|
|
echo "[ERROR] remediation-tool scripts not found." >&2
|
|
echo " Expected: \$HOME/.claude/skills/remediation-tool/scripts/onboard-tenant.sh" >&2
|
|
echo " Run a repo sync, or check identity.json.claudetools_root." >&2
|
|
bash "$__ROOT/.claude/scripts/log-skill-error.sh" "onboard365" "onboard365: remediation-tool scripts not found (onboard-tenant.sh missing on this machine)" >/dev/null 2>&1 || true
|
|
exit 3
|
|
}
|
|
|
|
# ── Parse args (allow a bare domain as smart mode) ────────────────────────────
|
|
SUB="${1:-}"
|
|
[[ -z "$SUB" ]] && { echo "usage: onboard365.sh <link|status|provision|auto> <domain|tenant-id>" >&2; exit 64; }
|
|
case "$SUB" in
|
|
link|status|provision|auto)
|
|
TARGET="${2:-}"
|
|
[[ -z "$TARGET" ]] && { echo "usage: onboard365.sh $SUB <domain|tenant-id>" >&2; exit 64; }
|
|
;;
|
|
*)
|
|
TARGET="$SUB"; SUB="auto"
|
|
;;
|
|
esac
|
|
|
|
resolve() { "$RT/resolve-tenant.sh" "$1" 2>/dev/null || echo "$1"; }
|
|
|
|
print_link() {
|
|
local t="$1"
|
|
cat <<EOF
|
|
============================================================
|
|
Onboard365 — Single-Consent Link
|
|
Customer: $TARGET
|
|
Tenant: $t
|
|
============================================================
|
|
Send the ONE link below to the customer's GLOBAL ADMIN. They sign
|
|
in and click Accept. That single consent is all they do — ACG
|
|
provisions everything else automatically.
|
|
|
|
${CONSENT_BASE}/${t}/adminconsent?client_id=${TENANT_ADMIN_APPID}&redirect_uri=${CONSENT_REDIRECT}&prompt=consent
|
|
|
|
App they will see: "ComputerGuru Tenant Admin"
|
|
(Customer instructions template: references/customer-consent-instructions.md)
|
|
|
|
After they confirm Accept, run:
|
|
onboard365.sh provision $TARGET
|
|
============================================================
|
|
EOF
|
|
}
|
|
|
|
# returns 0 if Tenant Admin token acquires (consented), 1 otherwise
|
|
is_consented() { "$RT/get-token.sh" "$1" tenant-admin >/dev/null 2>/tmp/onboard365-tok.err; }
|
|
|
|
case "$SUB" in
|
|
link)
|
|
print_link "$(resolve "$TARGET")"
|
|
;;
|
|
status)
|
|
echo "[INFO] Onboard365 status (dry-run) for $TARGET — no changes will be made"
|
|
"$RT/onboard-tenant.sh" "$TARGET" --dry-run
|
|
;;
|
|
provision)
|
|
echo "[INFO] Onboard365 provisioning suite for $TARGET (single-consent model)"
|
|
"$RT/onboard-tenant.sh" "$TARGET"
|
|
;;
|
|
auto)
|
|
T="$(resolve "$TARGET")"
|
|
if is_consented "$T"; then
|
|
echo "[INFO] Tenant Admin already consented for $TARGET — provisioning the suite..."
|
|
"$RT/onboard-tenant.sh" "$TARGET"
|
|
else
|
|
echo "[INFO] Tenant Admin not yet consented for $TARGET — generating the single consent link."
|
|
print_link "$T"
|
|
exit 2
|
|
fi
|
|
;;
|
|
esac
|