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/.
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>
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>
- 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>
- 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>
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>
- 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>
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>
- 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>
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>
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>
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>
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>
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>
Windows Store python3 stub returns exit 49 instead of running Python.
Replace with: py (Windows launcher) for actual Python code, jq for
simple JSON extraction. Reorder fallback loops to try py first.
Add Bash(py:*) to settings.local.json allowlist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rewrote get-token.sh: tiered app system (investigator/exchange-op/user-manager/tenant-admin/defender)
- Updated SKILL.md, command, gotchas, checklist, graph-endpoints for new app suite
- Cascades breach check: mailbox clean, inbound phishing received by John, DMARC gap noted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Activated 1Password skill for Claude Code (extracted from .skill ZIP)
- Resolved Lonestar Electrical MDM issue: ManageEngine was configured as
third-party EMM in Google Workspace, causing persistent enrollment prompts
on joser's personal phone
- Scoped credentials.md migration to 1Password (op:// refs + MSP vaults)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>