Commit Graph

58 Commits

Author SHA1 Message Date
d8581225f9 fix(grok): macOS compatibility - use gtimeout from coreutils
The ask-grok.sh wrapper script used 'timeout' command which doesn't
exist on macOS by default. Updated to detect macOS (darwin) and use
'gtimeout' from GNU coreutils instead.

Tested on macOS with:
- Text reasoning queries (working)
- Live web + X/Twitter search (working)

Requires: brew install coreutils (provides gtimeout)
2026-06-04 09:59:42 -07:00
5a78c56f36 sync: auto-sync from GURU-5070 at 2026-06-04 09:45:37
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-04 09:45:37
2026-06-04 09:45:42 -07:00
263039e21c sync: auto-sync from GURU-5070 at 2026-06-04 08:09:17
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-04 08:09:17
2026-06-04 08:09:24 -07:00
4c447ca489 sync: auto-sync from GURU-5070 at 2026-06-04 07:59:53
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-04 07:59:53
2026-06-04 07:59:58 -07:00
37ccc5f35c sync: auto-sync from GURU-5070 at 2026-06-03 20:07:24
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-03 20:07:24
2026-06-03 20:07:28 -07:00
446a6c1b1c sync: auto-sync from GURU-5070 at 2026-06-02 20:40:54
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-02 20:40:54
2026-06-02 20:40:58 -07:00
88b02cc43b docs(memory-dream): drop additive-only framing; reflect mirror-mode policy
SKILL.md still narrated the 2026-06-01-and-earlier additive-only stance.
With the policy change captured in feedback_memory_sync_destructive_ok.md
and sync-memory.sh now in mirror mode, the framing needed updating.

Behavior of the tool itself is unchanged (--apply-safe still only does
the low-risk index appends + profile->repo copies; merges/dedups still
land in PROPOSED for a human). The reasons given for that are now:
they're judgment calls, not "we might wipe useful data."

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 16:27:47 -07:00
dd414c424a sync: auto-sync from HOWARD-HOME at 2026-06-02 15:03:53
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-02 15:03:53
2026-06-02 15:04:49 -07:00
b153ff158b feat(self-check): add harness self-diagnosis / fleet conformance skill
New /self-check skill: each machine probes its own ClaudeTools harness wiring
(identity.json paths, required tooling, settings.json hooks, skill/command/script
set, vault decrypt, coord/Gitea connectivity, Ollama capability tier) and grades
RED/AMBER/GREEN against a checked-in provisional baseline manifest.

- Capability-tier model: architectural/OS/hardware differences (e.g. no local
  Ollama) select a fallback ruleset instead of failing.
- Duplicate detection: flags command/skill names that diverge between the repo
  and ~/.claude (the "same /cmd, different behaviour" cross-machine bug);
  CRLF-only diffs ignored.
- Memory check: index + orphan detection, plus a model-driven semantic pass for
  memories that contradict identity/settings.
- V1 is a census tool: --publish writes a per-machine census to coord
  (component selfcheck_<host>); fanout requests the fleet to self-check +
  self-remediate + re-publish; aggregate derives the proposed baseline. No
  machine ever fixes another.

Reviewed twice by the Code Review Agent; three CRITICAL coord-API bugs and the
CRLF false-WARN found and fixed, verified live against the coord API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:45:42 -07:00
2a1ccfac73 Add memory-dream skill + additive cross-machine memory sync
memory-dream: read-only memory lint/consolidation analyzer (index, backlinks,
stale refs, dup clusters, profile drift); additive-only --apply-safe, all
merges/deletes are proposals. sync-memory.sh: additive repo<->harness-profile
union (no delete/overwrite, conflicts surfaced), wired to a SessionStart hook.
Migrates the useful profile-only memories into the synced repo store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:22:12 -07:00
96fb4110ea Add b2 skill: Backblaze B2 management CLI (storage cost, prefix purge)
B2 Native API v3 client for the ACG B2 account: status, buckets, keys,
files, bucket-size, usage/cost ($0.00695/GB), gated create/delete bucket+key,
and gated lifecycle-based delete-prefix/lifecycle-remove for prefix purges.
Read-only by default; destructive ops require --confirm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 14:31:09 -07:00
c37fd11ee9 sync: auto-sync from GURU-KALI at 2026-05-31 19:31:53
Author: Mike Swanson
Machine: GURU-KALI
Timestamp: 2026-05-31 19:31:53
2026-05-31 19:31:56 -07:00
db6aa3683f fix(bitdefender): all-clients sweep, quarantine path, EDR controls, self-test
Several bugs found and fixed during live testing against the ACG GravityZone
tenant:
- security_sweep_all_clients: iterate each company (the companies container is
  not a valid endpoint parent; passing it 400'd the whole sweep)
- list_quarantine: use service-scoped path quarantine/computers with companyId
  (bare quarantine module 404'd; param is companyId not parentId)
- rename GZEndpointSummary.detection_active -> threat_detected with corrected
  semantics (True = active threat, tracks with infected; not an engine-on flag)
- status: readable sectioned table renderer for the nested apiKey/license dict
- portable CLAUDETOOLS_ROOT resolution (derive from file path, not a Windows
  literal) so it works on the Mac/Linux fleet

Adds scripts/selftest.py: a 29-check read-only harness (all passing) covering
every read command, --json, error exit codes, and destructive-action gating.
EDR/incident commands (blocklist, isolate/unisolate, blocklist-add/remove) and
raw destructive-method gating are included from this session's work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 07:29:55 -07:00
446d25c66b fix(bitdefender): gate raw destructive calls, allow --json after subcommand
- raw now refuses destructive methods (delete/uninstall/remove/reconfigure)
  without --confirm (it previously bypassed all gating)
- --json is now accepted after the subcommand (shared via a common parent
  parser), matching the documented usage
- drop a placeholder-less f-string
- SKILL.md: document raw gating + that raw echoes upstream responses verbatim

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 07:29:55 -07:00
8ba92bf02b feat(bitdefender): GravityZone Cloud Public API skill
Adds a /bitdefender skill that drives the ACG GravityZone partner tenant
via the JSON-RPC Public API. Read + management ops (companies, endpoints,
live security sweep, policies [read-only/shallow], packages, quarantine,
scans, groups, move/delete). Identity-tier JSON cache (24h TTL,
--refresh); volatile status is always pulled live, never cached.

Security hardening: API key loaded from SOPS vault at runtime (never on
disk/logs/argv/cache); destructive deletes gated behind --confirm; `raw`
also gates destructive methods; upstream error bodies truncated. UNVERIFIED
API methods reachable only via `raw`. Reuses the auth/JSON-RPC pattern from
api/services/gravityzone_service.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 07:29:55 -07:00
c670471c8a fix(gc-audit): correct Pass B sqlx premise (GC uses runtime sqlx)
GC's db layer uses runtime sqlx::query()/query_as() throughout - zero
compile-time macros (verified during v2 Task 1; CLAUDE.md's "compile-time
checked queries" line is stale). Pass B now treats a NEW sqlx::query! macro
as a [LOW] deviation (reintroduces the .sqlx-cache footgun + build-time
DATABASE_URL) instead of blessing macros as the GC norm. Fixed both the
intro divergence note and the Pass B check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 18:39:35 -07:00
e5ccb6a71a feat(gc-audit): use .claude/standards + reconcile specs/plans
- Load .claude/standards/ (index.yml + files) as the compliance baseline;
  Rust/TS passes now cite the specific standard each finding violates
- Glob all docs/specs/SPEC-*.md (incl. SPEC-002) + specs/*/plan.md; Pass F
  reconciles SPEC phases and plan.md [DONE] markers against code (3rd table)
- Extract a planned-work list from SPEC-002 + active plans; tag findings that
  match already-planned work as [TRACKED] so mid-rebuild audits surface net-new

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 18:23:46 -07:00
c70cd70070 feat(skills): add gc-audit skill for GuruConnect end-to-end audit
Modeled on rmm-audit but adapted to GuruConnect's architecture: 7 passes
(6 parallel + sequential CI/CD), protobuf 4-way wire-drift matrix, sqlx
compile-time macros allowed (GC norm), Gitea Actions pipeline + deploy host
checks, reconciles docs/FEATURE_ROADMAP.md + TECHNICAL_DEBT.md. Invoke via
/gc-audit; --pass=<name> for a single pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 17:24:53 -07:00
f576f7d686 sync: auto-sync from GURU-BEAST-ROG at 2026-05-29 16:34:25
Author: Mike Swanson
Machine: GURU-BEAST-ROG
Timestamp: 2026-05-29 16:34:25
2026-05-29 16:34:31 -07:00
a42d657c55 docs(session)+rules: 2026-05-27 — Quantum M365 onboarding, IX autodiscover fix, Syncro emergency/labor/attribution rules
Session logs: root (Michael #32329 hosting offer + IX simplehost.email autodiscover DNS fix + Cascades #32332 emergency correction) + Quantum client log (M365 tenant 2fd0092b onboarding, break-glass GA, CA report-only).

Syncro rule overhaul:
- Emergency billing: prepaid -> 26184 @ hours x1.5 (was 26118); non-prepaid -> 26184 with channel rate (onsite $262.50 / remote+inshop $225)
- Never make up labor items (existing product + real name; QuickBooks sync)
- Corrections preserve original tech's user_id (commission); adding notes/labor never changes ticket owner

/remediation-tool: Conditional Access may be managed programmatically (report-only first + exclude break-glass + confirm before enforce); fabb3421 deprecated for customer tenants; Quantum tenant onboarded (gotchas table).

Memory: 4 new (no-madeup-labor, corrections-preserve-tech, ca-programmatic, quantum-godaddy-tenant) + updates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:57:55 -07:00
a885b54deb feat: make FEATURE_ROADMAP a living doc — dev definition-of-done + audit default
Mike's decision (2026-05-27): the roadmap is a maintained status-and-plan
tracker ([ ]=planned, [x]=shipped, dated), consulted going in and updated
coming out.

- gururmm-development-principles memory: new "Living Roadmap (MANDATORY)"
  principle — consult before building, update the entry in the SAME change
  when shipping/modifying; roadmap update is part of definition-of-done.
  Dev is the primary maintainer; the audit is the backstop.
- rmm-audit skill: state the convention explicitly — the roadmap pass
  default is reconcile-and-flip (not annotate-only).

(Companion gururmm-repo changes — DESIGN.md principle + baseline checkbox
reconcile — pushed separately to the gururmm repo.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 06:34:41 -07:00
09c52a7d09 feat(rmm-audit): add roadmap reconciliation pass (Agent F) + cleanup step
The skill's frontmatter claimed it updated FEATURE_ROADMAP.md, but the body
had no roadmap-reconciliation logic — so stale checkboxes slipped through
(Network Discovery Node backend + BUG-001 temperature both shipped while
marked [ ]). Added:
- Agent F (parallel, read-only): cross-references every roadmap checkbox
  against code artifacts; classifies STALE-INCOMPLETE / PARTIAL /
  STALE-COMPLETE / ACCURATE with proving artifact. Conservative — only
  flips when end-to-end evidence is unambiguous; backend/scaffolding-only
  is PARTIAL, never flipped.
- Living-docs step: actually flip stale checkboxes, annotate partials,
  flag [x]-but-missing as [HIGH] regressions; every change logged in the
  report's new "FEATURE_ROADMAP.md Delta" section (no silent edits).
- Phase 0 extracts the roadmap claims list; --pass=roadmap added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 06:11:33 -07:00
072687b29a sync: auto-sync from GURU-KALI at 2026-05-25 13:52:42
Author: Mike Swanson
Machine: GURU-KALI
Timestamp: 2026-05-25 13:52:42
2026-05-25 13:52:43 -07:00
435e921300 sync: auto-sync from GURU-5070 at 2026-05-24 13:57:13
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-05-24 13:57:13
2026-05-24 13:58:35 -07:00
e80c36e6bf sync: auto-sync from DESKTOP-0O8A1RL at 2026-05-22 11:07:55
Author: Mike Swanson
Machine: DESKTOP-0O8A1RL
Timestamp: 2026-05-22 11:07:55
2026-05-22 11:07:59 -07:00
5ead5d4dee sync: auto-sync from DESKTOP-0O8A1RL at 2026-05-19 17:56:56
Author: Mike Swanson
Machine: DESKTOP-0O8A1RL
Timestamp: 2026-05-19 17:56:56
2026-05-19 17:57:02 -07:00
b804a92a05 Session log: GuruRMM 4-bug fix + MSP360 backup integration 2026-05-19
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 16:54:06 -07:00
0ad62fbc9e remediation-tool: add cert-auth (client_assertion JWT) to get-token.sh
Auth selection logic:
- Default: prefer cert when cert_thumbprint_b64url + cert_private_key_pem_b64
  are present in the vault entry's credentials block; fall back to client_secret.
- REMEDIATION_AUTH=secret  -> force client_secret flow.
- REMEDIATION_AUTH=cert    -> force cert flow; error if cert fields missing.
- Logs [INFO] auth=cert/secret to stderr so users see which path was taken.

Cert flow signs an RS256 JWT (header includes x5t) via inline Python (PyJWT
+ cryptography), POSTs client_assertion_type +
client_assertion=<jwt> in place of client_secret. Same scope, same cache, same
error handling (AADSTS7000229 still emits the consent URL).

Single sops -d to a mktemp file feeds both field reads to avoid repeated
~1s decrypt invocations on Windows; trap removes plaintext on exit.

Verified end-to-end against tedards.net for all three modes after wiping
/tmp/remediation-tool/.
2026-05-01 16:52:12 -07:00
447b90e092 Session log: Cascades audit retention design + Pro-Tech Services email investigation
Cascades:
- Approved Howard's corrected 4-policy CA bypass design
- Caught + fixed policy 3 GDAP bug (Service provider users exclusion)
- Decided hybrid LAW + Storage Account audit retention (ACG-billed,
  reuse existing Trusted Signing Azure subscription, westus2)
- Wrote full audit retention runbook for Howard
- Reshaped break-glass to two accounts (split-storage YubiKeys)
- Documented Cascades M365 admin model (admin@/sysadmin@ Connect-excluded
  by design; local AD Administrator separate identity layer)
- Decided Howard gets Owner on ACG sub with guardrails (resource lock +
  cost alert) instead of per-RG Contributor

Pro-Tech Services:
- DNS recon of pro-techhelps.com + pro-techservices.co
- Diagnosed calendar invite delivery issue (DKIM domain mismatch +
  no DMARC = strict receivers silently drop invites)
- Drafted non-technical IT-provider migration email to Michelle Sora

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:05:41 -07:00
fd933b68c3 remediation-tool: flag PIM role_assigned gap for Howard
role_assigned() only checks direct/permanent roleAssignments.
PIM-managed assignments are in roleAssignmentSchedules and won't
be found, producing noisy (non-blocking) output on re-runs against
tenants with PIM-assigned roles (e.g. Cascades).

TODO comment added at the helper — Howard to implement the fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 09:11:40 -07:00
a2f38c1038 cascades: CA unblock + Phase B buildout + onboard-tenant.sh CA Admin backfill
Day-long session unblocking the Cascades CA reconciliation that was paused on
the Tenant Admin SP directory-role gap. Discovered Microsoft also tightened
the OAuth scope for /identity/conditionalAccess/* reads (Policy.Read.All now
required, Policy.ReadWrite.ConditionalAccess no longer accepted for reads).
Patched Tenant Admin manifest accordingly and re-consented in Cascades.

Phase B Intune state turned out to be far more built than the 4/20 log
suggested -- compliance policy, Wi-Fi, device restrictions, both SDM app
configs (Authenticator + Teams), and 7 of 8 apps were already deployed and
assigned. PATCHed device restrictions to block camera/Bluetooth/roaming
and enabled Managed Home Screen multi-app kiosk (ALIS + Teams visible,
10-min auto-signout). PATCHed Cascades named location to add primary WAN
(184.191.143.62/32). Howard added Outlook from Managed Play; SMB encryption
enabled on \CS-SERVER\homes.

CA bypass design corrected -- original §5 plan in user-account-rollout-plan.md
called for "block off-site + MFA on-site" which doesn't match the actual goal
of bypass when network + device assurance present. Reshaped to three policies
that produce on-site-compliant = password only, anything else = MFA or block.

onboard-tenant.sh patched to:
  1. Backfill Policy.Read.All on Tenant Admin SP if missing (idempotent --
     for tenants consented before the 2026-04-29 manifest update).
  2. Assign Conditional Access Administrator directory role to Tenant Admin
     SP at onboard time. Mirrors the Exchange Operator fix Mike landed in
     16f95e8.

Validated with --dry-run against Cascades. Customer-facing tenants already
onboarded should be re-run with this script to backfill both items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:32:23 -07:00
97f4218926 remediation: mark SANDTEKO MACHINERY consent status as done in tenant-consent.html
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 20:13:20 -07:00
16f95e8235 fix(onboard): auto-assign Exchange Admin to Exchange Operator SP; mark Sandteko fully onboarded
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 20:13:20 -07:00
b7bc99174f onboard: SANDTEKO MACHINERY LLC (partial) — all apps consented, roles assigned, Exch Op Exchange Admin pending
- tenants.md: updated status to PARTIAL with full detail note
- clients/sandteko-machinery/: new client directory with reports/ and session-logs/ scaffolding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 20:13:20 -07:00
327dc329ab remediation-tool: fix tenant-sweep tier name; mark Kittle partially onboarded
- tenant-sweep.sh line 12: renamed tier `graph` to `investigator` to match
  the valid tier name expected by get-token.sh
- tenants.md: updated Kittle Design & Construction consent status from NO
  to PARTIAL with notes on what was consented and what remains pending

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 07:13:16 -07:00
5ec20ac9dd session log: Dataforth SMTP fix, GuruRMM GAGETRAK onboarding, Cloudflare grey-cloud, ticket #32142 billed
- Resolved calibration@dataforth.com SMTP AUTH per-mailbox block in Exchange Online
- Full Dataforth tenant onboarding (all 5 ComputerGuru apps consented)
- GuruRMM agent deployed on DF-GAGETRAK; diagnosed and fixed two issues:
  - rmm-api.azcomputerguru.com grey-clouded (Cloudflare was blocking WSS)
  - enrolled_agents auth gap workaround (site API key in AgentKey registry)
- Syncro ticket #32142 billed: 2 hrs prepaid, invoice #67447, status Invoiced
- syncro.md: fix .comment.id jq path (was .id, caused duplicate comments twice)
- tenants.md: Dataforth marked fully onboarded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 09:20:00 -07:00
741b259760 feat: add intune-manager tier to get-token.sh 2026-04-21 20:02:19 -07:00
90f9d9eda1 fix: two bugs in get-token.sh vault path resolution
1. Variable name collision: VAULT_PATH was used for both the SOPS file
   relative path (set by case statement) and the vault root override env
   var. Renamed env var override to VAULT_ROOT_ENV to avoid collision.

2. Wrong directory depth: CLAUDETOOLS_ROOT was navigating 3 levels up
   from scripts/ landing at .claude/ instead of repo root. Fixed to 4
   levels (scripts -> remediation-tool -> skills -> .claude -> repo root).

Also added jq as primary vault_path reader (handles Unix paths on Windows),
with cygpath-converted Python fallback.

Bugs discovered during Mac testing 2026-04-21. Windows worked only because
tokens were served from /tmp cache after first acquisition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 19:12:15 -07:00
c37816736b sync: auto-sync from DESKTOP-0O8A1RL at 2026-04-21 19:10:13
Author: Mike Swanson
Machine: DESKTOP-0O8A1RL
Timestamp: 2026-04-21 19:10:13
2026-04-21 19:10:25 -07:00
14e7354ba5 sync: auto-sync from Mikes-MacBook-Air.local at 2026-04-21 19:02:07
Author: Mike Swanson
Machine: Mikes-MacBook-Air.local
Timestamp: 2026-04-21 19:02:07
2026-04-21 19:02:09 -07:00
a86df117d2 fix: vault path from per-machine identity.json, not hardcoded paths
- Add .claude/scripts/vault.sh wrapper (reads vault_path from identity.json)
- get-token.sh + patch-tenant-admin-manifest.sh read identity.json for vault root
- syncro.md uses wrapper via CLAUDETOOLS_ROOT
- CLAUDE.md + ONBOARDING.md document the pattern and prompt for vault_path on onboarding
- identity.json now includes vault_path (D:/vault on DESKTOP-0O8A1RL)

Howard and Mac need vault_path added to their identity.json after pulling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 19:01:27 -07:00
0a7cd6b778 fix: portable vault path resolution across Windows/Mac/Linux
Replace hardcoded D:/vault references with candidate-list pattern
that also checks $HOME/vault, ~/.vault, and respects VAULT_PATH
env var override. Fixes vault.sh lookup failures on Mac and
Howard's machine.

Affected: CLAUDE.md, syncro.md, get-token.sh, patch-tenant-admin-manifest.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 18:58:43 -07:00
db4e3c25a5 Session log: GuruRMM MSI build fix + DESIGN.md + BirthBiologic onboarding
- Fixed MSI build on Pluto (missing WixToolset.Util.wixext in install.rs)
- Created docs/DESIGN.md in gururmm repo (per-component design guide)
- Saved BirthBiologic GuruRMM site credentials to vault
- Added birth-biologic and mvan-inc client session logs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:02:23 -07:00
31afc61a55 docs: mark martylryan.com and grabblaw.com as done after successful re-onboard 2026-04-20 21:04:02 -07:00
821435594b docs: update tenant-consent.html — 17 tenants marked done after batch sweep 2026-04-20 20:16:44 -07:00
89300e7ac7 fix: add sleep after SP creation + handle null appRoleAssignments in jq
New SPs need ~5s to replicate before appRoleAssignments can be granted.
Also fixes jq null iterator error when SP has no existing assignments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:51:48 -07:00
7a2e41c28c docs: add tenant-consent.html — clickable consent links for all 41 tenants
Dark-theme HTML page with one-click consent URLs for each tenant.
Tracks done/pending state in localStorage. Re-consent tenants (martylryan,
grabblaw) highlighted separately. No copy-paste needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:39:00 -07:00
fb38fdeef7 feat: onboard-tenant.sh now programmatically consents full app suite
After Tenant Admin is consented by customer admin, the script automatically:
- Creates SPs for Security Investigator, Exchange Operator, User Manager,
  and Defender Add-on (programmatic consent, no extra customer clicks needed)
- Grants all required Graph, Exchange Online, and Defender ATP appRoleAssignments
- Idempotent: skips any permissions already granted

Also added AppRoleAssignment.ReadWrite.All to Tenant Admin manifest so
fresh consents include this permission. Existing tenants (martylryan.com,
grabblaw.com) need a one-time Tenant Admin re-consent to pick it up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:33:50 -07:00
fd6c96513d docs: add tenants.md with full partner tenant list + Tenant Admin consent URLs
41 CIPP-managed tenants sourced from ListTenants API. Includes onboarding
status, tenant IDs, and pre-built Tenant Admin consent URLs for each.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:23:13 -07:00
41eac14c33 docs: mark Grabblaw fully onboarded — all three directory roles assigned
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 17:07:06 -07:00