From a2f38c1038762f69d045a2be1851f6bae2edba12 Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Wed, 29 Apr 2026 07:32:14 -0700 Subject: [PATCH] cascades: CA unblock + Phase B buildout + onboard-tenant.sh CA Admin backfill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../scripts/onboard-tenant.sh | 58 +++ ...-cascades-bypass-pilot-phase-b-buildout.md | 329 ++++++++++++++++++ 2 files changed, 387 insertions(+) create mode 100644 clients/cascades-tucson/session-logs/2026-04-29-howard-cascades-bypass-pilot-phase-b-buildout.md diff --git a/.claude/skills/remediation-tool/scripts/onboard-tenant.sh b/.claude/skills/remediation-tool/scripts/onboard-tenant.sh index ad595f4..f0ed052 100755 --- a/.claude/skills/remediation-tool/scripts/onboard-tenant.sh +++ b/.claude/skills/remediation-tool/scripts/onboard-tenant.sh @@ -44,6 +44,7 @@ DEFENDER_APP_ID="fc780465-2017-40d4-a0c5-307022471b92" ROLE_EXCHANGE_ADMIN="29232cdf-9323-42fd-ade2-1d097af3e4de" ROLE_USER_ADMIN="fe930be7-5e62-47db-91af-98c3a49a38b1" ROLE_AUTH_ADMIN="c4e39bd9-1100-46d3-8c65-fb160da0071f" +ROLE_CA_ADMIN="b1be1c3e-b65d-4f19-8427-f6fa0d97feb9" # ── Graph appRole GUIDs per app (from requiredResourceAccess in home tenant) ── # Security Investigator — Graph @@ -435,6 +436,29 @@ if [[ -z "$GRAPH_SP_OID" ]]; then exit 1 fi +# ── Step 3.5: Backfill Tenant Admin SP — Policy.Read.All ───────────────────── +# Microsoft tightened LIST /identity/conditionalAccess/* to require Policy.Read.All +# (Policy.ReadWrite.ConditionalAccess no longer accepted for reads). Added to manifest +# 2026-04-29; tenants consented before that need backfill. +POLICY_READ_ALL_ID="246dd0d5-5bd0-4def-940b-0421030a5b68" +HAS_POLICY_READ=$(curl -s --max-time 15 \ + -H "Authorization: Bearer $TENANT_ADMIN_TOKEN" \ + "https://graph.microsoft.com/v1.0/servicePrincipals/$TA_SP_OID/appRoleAssignments" \ + | jq --arg rid "$POLICY_READ_ALL_ID" '[.value[]? | select(.appRoleId == $rid)] | length > 0') + +if [[ "$HAS_POLICY_READ" != "true" ]]; then + echo "" + echo "[INFO] Tenant Admin SP missing Policy.Read.All — backfilling..." + if [[ "$DRY_RUN" == "true" ]]; then + echo " [DRY-RUN] Would grant Policy.Read.All ($POLICY_READ_ALL_ID) to Tenant Admin SP" + elif grant_app_role "$TENANT_ADMIN_TOKEN" "$TA_SP_OID" "$GRAPH_SP_OID" "$POLICY_READ_ALL_ID"; then + echo " [OK] Policy.Read.All granted to Tenant Admin SP" + else + echo " [WARNING] Could not grant Policy.Read.All programmatically — customer re-consent may be needed" + echo " Re-consent URL: ${CONSENT_BASE}/${TENANT_ID}/adminconsent?client_id=${APP_TENANT_ADMIN}&redirect_uri=${CONSENT_REDIRECT}&prompt=consent" + fi +fi + # ── Step 4: Programmatic consent — create SPs + grant appRoleAssignments ────── echo "" echo "[INFO] Consenting app suite in tenant (programmatic — no customer click needed)..." @@ -522,6 +546,36 @@ else fi fi +# Tenant Admin -> Conditional Access Administrator +# Required because Microsoft Graph evaluates SP membership in CA-relevant directory +# roles in addition to OAuth scopes when calling /identity/conditionalAccess/* endpoints. +# Without this role, the Tenant Admin SP gets 403 even with Policy.ReadWrite.ConditionalAccess +# in its token. Discovered Cascades 2026-04-28; backfilled here so future tenants don't trip on it. +echo "" +echo "[CHECK] Tenant Admin SP: $TA_SP_OID" +# Activate CA Admin role in tenant first (idempotent — returns 400 if already activated) +ACTIVATE_RESP=$(curl -s --max-time 15 \ + -H "Authorization: Bearer $TENANT_ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -X POST \ + "https://graph.microsoft.com/v1.0/directoryRoles" \ + -d "{\"roleTemplateId\":\"$ROLE_CA_ADMIN\"}" 2>/dev/null || true) +# Don't fail on activation — directly-assigned role POST handles the case where already active + +IS_CA=$(role_assigned "$TENANT_ADMIN_TOKEN" "$TA_SP_OID" "$ROLE_CA_ADMIN") +if [[ "$IS_CA" == "true" ]]; then + echo " Conditional Access Administrator: PRESENT" + STATUS_MAP["Tenant Admin:Conditional Access Administrator"]="OK" +else + echo " Conditional Access Administrator: MISSING -> ASSIGNING..." + if assign_role "$TENANT_ADMIN_TOKEN" "$TA_SP_OID" "$ROLE_CA_ADMIN" "Conditional Access Administrator"; then + STATUS_MAP["Tenant Admin:Conditional Access Administrator"]=$( [[ "$DRY_RUN" == "true" ]] && echo "DRY-RUN" || echo "ASSIGNED" ) + else + STATUS_MAP["Tenant Admin:Conditional Access Administrator"]="ERROR" + PARTIAL_FAILURE=true + fi +fi + # User Manager -> User Administrator + Authentication Administrator if [[ -z "$USER_MGR_OID" ]]; then echo "[WARNING] User Manager SP still not found after consent attempt" @@ -573,6 +627,10 @@ else fi echo "SP roles status:" +TA_CA="${STATUS_MAP["Tenant Admin:Conditional Access Administrator"]:-SKIPPED}" +echo " Tenant Admin:" +printf " Conditional Access Administrator: %s\n" "[$TA_CA]" + SEC_EXCH="${STATUS_MAP["Security Investigator:Exchange Administrator"]:-SKIPPED}" echo " Security Investigator:" printf " Exchange Administrator: %s\n" "[$SEC_EXCH]" diff --git a/clients/cascades-tucson/session-logs/2026-04-29-howard-cascades-bypass-pilot-phase-b-buildout.md b/clients/cascades-tucson/session-logs/2026-04-29-howard-cascades-bypass-pilot-phase-b-buildout.md new file mode 100644 index 0000000..f4e359f --- /dev/null +++ b/clients/cascades-tucson/session-logs/2026-04-29-howard-cascades-bypass-pilot-phase-b-buildout.md @@ -0,0 +1,329 @@ +# 2026-04-29 — Cascades CA unblock + Phase B Intune buildout + bypass pilot prep + +## User +- **User:** Howard Enos (howard) +- **Machine:** HOWARD-HOME +- **Role:** tech +- **Session span:** 2026-04-28 evening into 2026-04-29 early morning (continuous, ~7 hours) + +## Resume point (READ THIS FIRST when picking back up) + +We are on the **Cascades caregiver shared-phone bypass pilot**, Path B (cloud-only test user). Phase B Intune is fully built. Cascades named location has both WANs trusted. Phase A "Track" plan reshaped — original §5 design did not match Howard's actual goal of MFA bypass on trusted-network + compliant-device. + +**Where we paused:** awaiting Howard's next decision after Phase B is fully populated and one phone successfully completes a real user sign-in flow to flip compliant. Outlook is being added by Howard via Managed Play. + +### What's still to do, in order + +1. Howard finishes Outlook add (Managed Play), then sync and tell me. I'll assign it + add to MHS visible apps (currently ALIS + Teams; will become ALIS + Teams + Outlook). +2. Howard adds LinkRx + Helpany web apps in Intune portal when he has the URLs. +3. Pilot user + cloud group (Path B): + - Create cloud-only Entra group `SG-Caregivers-Pilot` + - Create cloud-only test user (or use an existing cloud user; Howard's `howard.enos` cloud account doesn't exist yet because Entra Connect is in staging) + - License test user with Business Premium (one of 34 spare seats) + - Add user to `SG-Caregivers-Pilot` +4. **Break-glass admin** — design + create. Need: cloud-only Global Admin, FIDO2 (Howard has 1 YubiKey), excluded from all CA, vaulted password, sign-in alerts. Pre-flight before CA changes. +5. New CA bypass policies (corrected design — see §"CA design correction" below): + - DELETE `Require multifactor authentication for all users` (existing) + - CREATE `CSC - Require MFA off Cascades network` (all users, location not Cascades → require MFA) + - CREATE `CSC - Require MFA on non-compliant device` (all users, device.isCompliant -eq False → require MFA) + - CREATE `CSC - Block off-network unless allow-listed` (all except `SG-External-Signin-Allowed` and break-glass, location not Cascades → block) + - CREATE `CSC - Sign-in frequency 8h` session control on `SG-Caregivers-Pilot` (and later SG-Caregivers) +6. Stage all new CA in **Report-only** for 24-48h, review logs, fix gaps, then flip to On. +7. Phone enrollment dry-run with the pilot user → expect no MFA on Cascades Wi-Fi + compliant device, expect block off-network. + +### Decisions still pending + +- Pilot user identity (create new `pilot.test@` vs use existing cloud user) +- Audit retention: deferred — Azure pay-as-you-go subscription still needs Howard or Mike billing call +- Whether to backfill `onboard-tenant.sh` against other already-onboarded tenants for the new CA Admin + Policy.Read.All (script now handles this idempotently when re-run; just needs a re-run per tenant) + +--- + +## Session Summary + +Picked up from the 2026-04-28 morning save where the Tenant Admin SP had no directory role in Cascades and every `/identity/conditionalAccess/*` call returned 403. Howard granted the Conditional Access Administrator role via PIM (Active assignment, permanent, Path B from yesterday's options). After that landed, CA endpoints **still** returned 403 with the same scope-missing error. Investigation showed Microsoft tightened `LIST /identity/conditionalAccess/namedLocations` to require `Policy.Read.All` as the application permission — `Policy.ReadWrite.ConditionalAccess` (which the SP carried) is no longer accepted for reads on this endpoint. Patched the Tenant Admin app manifest in ACG home to add `Policy.Read.All`, granted the appRoleAssignment in ACG home, Howard re-consented in Cascades. CA reads then worked. + +Reading the CA state revealed the existing `Require multifactor authentication for all users` policy is in `enabled` state (not Report-only), which makes the original Track A "Gate A7 — flip from Report-only to On" obsolete. Howard re-stated the pilot goal in plain language: caregivers signing into shared phones on Cascades Wi-Fi with a compliant Intune-managed device should bypass MFA entirely; off-site they should be blocked (no personal device for MFA fallback exists). Eventually this rolls out to all users at Cascades. The existing `user-account-rollout-plan.md §5` design said "Block sign-in unless from Cascades + MFA" — meaning even on-site users get MFA prompted — which doesn't match the goal. Reshaped the CA design to three policies that together produce on-site-compliant = password only, anything else = MFA or block. + +In parallel, discovered the `ComputerGuru - Intune Manager` app was still single-tenant, so it couldn't be consented in Cascades. PATCHed `signInAudience` to `AzureADMultipleOrgs` in ACG home, Howard re-consented in Cascades, then queried Intune state. **Phase B is far more built than the 4/20 session log indicated** — at some point between 4/20 and 4/21 someone (likely Howard) finished it. The compliance policy (`CSC - Android Compliance`, type `androidDeviceOwnerCompliancePolicy`), Wi-Fi config (`CSC - CSCNet Wi-Fi`), device restrictions (`CSC - Android Shared Phones Restrictions`), and both Shared Device Mode app config policies (Authenticator + Teams, both with `shared_device_mode_enabled=true`) all exist and are assigned to the `Cascades - Shared Phones` dynamic group. 7 of 8 apps are deployed and assigned (Microsoft Launcher is the only unassigned one). + +PATCHed the Cascades named location to add `184.191.143.62/32` (primary WAN). Both WANs now `isTrusted=true`. PATCHed device restrictions to block camera, Bluetooth, and data roaming (per Howard's spec). Enabled Managed Home Screen multi-app kiosk mode with ALIS + Microsoft Teams as visible apps, 10-min idle auto-signout with 1-min warning, no MHS-level PIN (relies on the device PIN already required by compliance). Howard added Microsoft Outlook from Managed Play in the Intune portal. Walked through Wave 0 HIPAA pre-flight: HIPAA BAA is automatic via Microsoft Online Services Terms (no separate signature needed); SMB encryption on `\\CS-SERVER\homes` was False, Howard ran `Set-SmbShare -Name homes -EncryptData $true -Force` to flip it; Diagnostic Settings deferred (will set up Azure pay-as-you-go later for Log Analytics workspace). After the session, ran `/sync` which surfaced Mike's note from earlier in the day approving Path A for the CA fix — superseded by Path B, which Howard already executed. Patched `onboard-tenant.sh` to programmatically assign Conditional Access Administrator role to the Tenant Admin SP and backfill `Policy.Read.All` on existing tenants where the manifest update post-dates their consent. + +## Key Decisions + +- **Path B (manual portal PIM assignment) for the CA Admin role** instead of the Graph-API Path A Mike approved. Both achieve the same end state. Path B was faster on the day; Path A is now the backfilled default in `onboard-tenant.sh`. +- **Patch `Policy.Read.All` into Tenant Admin manifest**. Microsoft tightened `LIST /identity/conditionalAccess/*` endpoints to require `Policy.Read.All` as the application permission. `Policy.ReadWrite.ConditionalAccess` (legacy) is no longer accepted for reads. New tenants get this from the manifest at consent time; existing tenants get it programmatically via the patched onboard script. +- **Reshape the CA bypass design**. Replace the existing all-users-MFA policy with three policies that together produce: on-site + compliant = password only, off-site or non-compliant = MFA or block. The original Track A §5 design conflated "block off-site" with "require MFA on-site," which doesn't match the actual goal of bypassing MFA when network + device assurance are present. +- **Path B for the pilot** (cloud-only test user) instead of Path A (exit Entra Connect staging first). Faster path to a meaningful test; the production rollout will use the synced `SG-Caregivers` group once staging exits, but that's a separate operation gated on the AD prereq cleanup (Wave 0.5 G1 from `user-account-rollout-plan.md`). +- **Sign-in frequency: 8 hours** for caregivers (revised from initial "every hour" — caregivers re-auth at start of shift and work through without re-prompts; HIPAA-defensible as one strong factor among three). +- **Off-network behavior: block for caregivers, MFA for office allow-list**. Caregivers have no personal device for MFA, so off-network access is denied entirely. Office staff in `SG-External-Signin-Allowed` get MFA off-network without compliant-device requirement (Howard's call — simpler, less brittle). +- **Path X (Managed Home Screen multi-app kiosk)** instead of standard Android launcher. Provides keyboard lockdown, restricted app access, and HIPAA-defensible least-privilege UI for caregivers. +- **MHS auto-signout: 10 min idle, 1 min warning**. Standard healthcare convention for shared-device session lifetime, separate from the device's 1-min screen lock and the 8-hour CA sign-in frequency. +- **Microsoft Launcher: leave unassigned**. With MHS as the kiosk launcher, the Microsoft Launcher app is redundant. +- **Intune Manager app: convert to multi-tenant**. Single-tenant gave AADSTS700016 when consenting in Cascades. PATCH on `signInAudience` to `AzureADMultipleOrgs` was the cleaner fix vs. adding DeviceManagement scopes to Tenant Admin. +- **Audit retention: defer to later**. Microsoft default 30-day Entra retention plus Purview 180-day retention covers near-term forensics. Long-term 6-year HIPAA retention will be added via Azure Storage Account or Log Analytics workspace once an Azure pay-as-you-go subscription is set up. Cost estimate: Storage Account ~$0.20/mo at year-6 max; LAW with live monitoring ~$0.50-1/mo. +- **HIPAA BAA: confirmed automatic via Microsoft Online Services Terms**. The old Admin Center checkbox is gone. Business Premium covers HIPAA-eligible services by default. + +## Problems Encountered + +- **CA endpoints 403 after CA Admin role assigned** — root cause not directory role missing (already fixed) but Microsoft tightening the OAuth scope. Fixed by patching `Policy.Read.All` into Tenant Admin manifest and re-consenting in Cascades. +- **Intune Manager app single-tenant** — couldn't be consented in Cascades; got AADSTS700016. Fixed by PATCH on `signInAudience` to `AzureADMultipleOrgs` in ACG home. +- **Direct Graph add of Managed Play apps not supported** — POST to `/v1.0/deviceAppManagement/mobileApps` with `androidManagedStoreApp` returns 400 because Managed Play apps must be approved through Google's Play console iframe. Howard added apps via UI; assignments + config policies done via Graph after. +- **Graph v1.0 mobileApps endpoint hides Managed Play apps** — initially saw only ALIS in the inventory. Beta endpoint returned all 8 apps. Switched all subsequent app queries to beta. +- **Kiosk app schema discovery iteration** — `kioskModeApps` requires items typed as `microsoft.graph.appListItem` with required `name` field. Two failed attempts (`androidDeviceOwnerKioskModeManagedAppItem`, `androidDeviceOwnerKioskModeAppPositionItem`) before landing on the correct shape. +- **Phones still noncompliant after all policy work** — expected. Compliance requires a real user sign-in flow to set the 6-digit PIN and trigger Authenticator SDM registration. Will resolve when the pilot user signs into one of the test phones. +- **Existing all-users-MFA policy is enforced, not Report-only** — invalidates Gate A7 from Track A. Reshaped CA design described above. +- **Diagnostic Settings page requires Azure subscription** — no Azure subscription on Cascades yet. Audit retention plan deferred until subscription decision (Cascades direct billing vs ACG bundles it into MSP invoice). +- **Sync notification surfaced Mike's note (now superseded)** — Mike approved Path A while we were already doing Path B. Net result is the same; need a follow-up note to Mike to close the loop. + +--- + +## Configuration Changes + +### ComputerGuru ACG home tenant (`ce61461e-81a0-4c84-bb4a-7b354a9a356d`) + +- **`ComputerGuru - Tenant Admin` app manifest:** added `Policy.Read.All` (`246dd0d5-5bd0-4def-940b-0421030a5b68`) to `requiredResourceAccess`. Granted appRoleAssignment in home tenant. +- **`ComputerGuru - Intune Manager` app manifest:** flipped `signInAudience` from `AzureADMyOrg` to `AzureADMultipleOrgs`. + +### Cascades tenant (`207fa277-e9d8-4eb7-ada1-1064d2221498`) + +- **Tenant Admin SP** (`a5fa89a9-b735-4e10-b664-f042e265d137`) now holds **Conditional Access Administrator** directory role (PIM-managed, Active assignment, permanent, no expiration). Status `Provisioned` since 2026-04-28T18:45:50Z. +- **Tenant Admin SP** now also has `Policy.Read.All` appRoleAssignment (added via re-consent). +- **Intune Manager SP** consented + provisioned in Cascades after multi-tenant flip. +- **Cascades named location** (id `061c6b06-b980-40de-bff9-6a50a4071f6f`): + - Was: `72.211.21.217/32` only (secondary WAN) + - Now: `72.211.21.217/32` + `184.191.143.62/32` (both WANs) + - `isTrusted: true` +- **`CSC - Android Shared Phones Restrictions`** (id `070a76c2-a8c3-4f7f-9ba7-1f4ac5084184`) — version bumped 2 → 4: + - `cameraBlocked: true` + - `bluetoothBlockConfiguration: true` + - `dataRoamingBlocked: true` + - `kioskModeUseManagedHomeScreenApp: "multiAppMode"` + - `kioskModeApps: [ALIS, Microsoft Teams]` + - `kioskModeManagedHomeScreenAutoSignout: true` + - `kioskModeManagedHomeScreenInactiveSignOutDelayInSeconds: 600` (10 min) + - `kioskModeManagedHomeScreenInactiveSignOutNoticeInSeconds: 60` (1 min warning) + - `kioskModeManagedHomeScreenPinRequired: false` +- **Microsoft Outlook** added to mobile apps via Intune portal (Managed Play). Pending sync, then assignment + add to MHS app list. + +### CS-SERVER (Cascades on-prem DC, `192.168.2.254`) + +- `\\CS-SERVER\homes` SMB share: `EncryptData` was `False`, Howard ran `Set-SmbShare -Name homes -EncryptData $true -Force` to flip it. + +### Repo + +- **`.claude/skills/remediation-tool/scripts/onboard-tenant.sh`** patched: + - Added `ROLE_CA_ADMIN` constant (`b1be1c3e-b65d-4f19-8427-f6fa0d97feb9`) + - Added Tenant Admin SP → Conditional Access Administrator assignment block (with role activation, idempotent) + - Added `Policy.Read.All` backfill block before the consent_app loop (idempotent — only grants if missing) + - Added Tenant Admin row to the final status table + +--- + +## Commands & Outputs + +### Cascades named location PATCH + +```bash +TOKEN=$(bash .claude/skills/remediation-tool/scripts/get-token.sh \ + 207fa277-e9d8-4eb7-ada1-1064d2221498 tenant-admin) +NL_ID="061c6b06-b980-40de-bff9-6a50a4071f6f" +curl -X PATCH \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations/$NL_ID" \ + -d '{ + "@odata.type": "#microsoft.graph.ipNamedLocation", + "isTrusted": true, + "ipRanges": [ + {"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "72.211.21.217/32"}, + {"@odata.type": "#microsoft.graph.iPv4CidrRange", "cidrAddress": "184.191.143.62/32"} + ] + }' +# HTTP 204 +``` + +### Tenant Admin manifest patch (Policy.Read.All) + +Inline bash (mirrored Mike's `patch-tenant-admin-manifest.sh` pattern): + +```bash +POLICY_READ_ALL_ID="246dd0d5-5bd0-4def-940b-0421030a5b68" +# Patch requiredResourceAccess to include Policy.Read.All +# Then grant appRoleAssignment in ACG home +# Howard re-consented in Cascades via: +# https://login.microsoftonline.com/207fa277-e9d8-4eb7-ada1-1064d2221498/adminconsent?client_id=709e6eed-0711-4875-9c44-2d3518c47063&redirect_uri=https://azcomputerguru.com&prompt=consent +``` + +### Intune Manager multi-tenant flip + +```bash +INTUNE_APP_OBJ_ID="31017446-c01a-4775-864f-aef96ce43797" +curl -X PATCH \ + -H "Authorization: Bearer $MGMT_TOKEN" -H "Content-Type: application/json" \ + "https://graph.microsoft.com/v1.0/applications/$INTUNE_APP_OBJ_ID" \ + -d '{"signInAudience": "AzureADMultipleOrgs"}' +# HTTP 204 (initial verify lagged; re-verified after 8s — confirmed) +``` + +### Device restrictions PATCHes + +```bash +TOKEN=$(bash .claude/skills/remediation-tool/scripts/get-token.sh \ + 207fa277-e9d8-4eb7-ada1-1064d2221498 intune-manager) +RESTRICT_ID="070a76c2-a8c3-4f7f-9ba7-1f4ac5084184" + +# Block camera + bluetooth + roaming +curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/$RESTRICT_ID" \ + -d '{ + "@odata.type": "#microsoft.graph.androidDeviceOwnerGeneralDeviceConfiguration", + "cameraBlocked": true, + "bluetoothBlockConfiguration": true, + "dataRoamingBlocked": true + }' + +# Enable MHS multi-app kiosk +curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/$RESTRICT_ID" \ + -d '{ + "@odata.type": "#microsoft.graph.androidDeviceOwnerGeneralDeviceConfiguration", + "kioskModeUseManagedHomeScreenApp": "multiAppMode", + "kioskModeApps": [ + {"@odata.type": "#microsoft.graph.appListItem", "name": "ALIS", "appId": "fcbf803d-ceb7-4f4e-93ed-2be1b91a05f3"}, + {"@odata.type": "#microsoft.graph.appListItem", "name": "Microsoft Teams", "appId": "0eb81676-299b-4eb2-bfd9-8be914a82f91"} + ], + "kioskModeManagedHomeScreenAutoSignout": true, + "kioskModeManagedHomeScreenInactiveSignOutDelayInSeconds": 600, + "kioskModeManagedHomeScreenInactiveSignOutNoticeInSeconds": 60, + "kioskModeManagedHomeScreenPinRequired": false + }' +``` + +### CS-SERVER SMB encryption + +```powershell +Set-SmbShare -Name homes -EncryptData $true -Force +Get-SmbShare -Name homes | Select Name,EncryptData +# Name EncryptData +# ---- ----------- +# homes True +``` + +--- + +## CA design correction (revised from `user-account-rollout-plan.md` §5) + +Existing all-users MFA policy is `enabled` not Report-only. Original §5 design said "block off-site, require MFA on-site" — that conflicts with Howard's actual goal of bypass on-site + compliant. Corrected design (deferred for build until break-glass exists): + +| # | Name | Users | Conditions | Grant | +|---|---|---|---|---| +| — | (DELETE) `Require multifactor authentication for all users` | — | — | — | +| 1 | `CSC - Require MFA off Cascades network` | All users (admins still hit by separate admin policy) | Locations: include All, exclude Cascades named location | Require MFA | +| 2 | `CSC - Require MFA on non-compliant device` | All users | Filter for devices: include `device.isCompliant -eq False` | Require MFA | +| 3 | `CSC - Block off-network unless allow-listed` | All users **except** `SG-External-Signin-Allowed` and break-glass | Locations: include All, exclude Cascades named location | Block | +| 4 | `CSC - Sign-in frequency 8h` (session control) | `SG-Caregivers-Pilot` (then `SG-Caregivers`) | (none — applies to all sessions for the group) | Session control: re-auth every 8 hours | + +**Logic verification:** caregiver on Cascades + compliant phone → no policy applies → password only. Caregiver off-network → policy 3 blocks. Caregiver on Cascades + non-compliant → policy 2 → MFA (which they can't do → effectively blocked, by design). External-allowed user off-site + compliant device → policy 1 → MFA only. Office user not allow-listed off-site → policies 1 + 3 → block wins. + +Keep existing risk-based + admin-targeted policies as defense in depth (they'll continue to add MFA on top in suspect scenarios, even when the bypass would otherwise apply — which is correct for HIPAA). + +--- + +## Pending / Incomplete Tasks + +### Immediate (resume here) +- [ ] Howard finishes Outlook Managed Play sync, tells me, I assign + add to MHS app list +- [ ] Howard adds LinkRx + Helpany web apps when URLs available +- [ ] Pick a phone and run real user sign-in flow → verify compliance flips to compliant + +### Path B pilot +- [ ] Decide: create new `pilot.test@cascadestucson.com` cloud user, or use an existing cloud user +- [ ] Create cloud-only Entra group `SG-Caregivers-Pilot`, add pilot user +- [ ] Assign Business Premium license to pilot user + +### Wave 0 HIPAA pre-flight (before any CA change goes live) +- [ ] Break-glass admin: design + create. Cloud-only Global Admin, FIDO2 (1 YubiKey on hand), excluded from all CA, vaulted password, sign-in alerts to Howard + Mike +- [ ] Audit retention 6yr: decide Cascades-billed vs ACG-billed, then create Azure pay-as-you-go subscription, Log Analytics workspace, Diagnostic Settings export +- [ ] ALIS BAA: ask Meredith (Howard sends email) +- [ ] Risk Analysis doc: draft via Ollama, Mike reviews + +### CA bypass build +- [ ] Stage 4 new policies in **Report-only**, assigned to `SG-Caregivers-Pilot` initially +- [ ] Run pilot validation tests (the 8 from the earlier write-up) +- [ ] After 24-48h Report-only and break-glass verified working, flip to On + +### Production rollout (later) +- [ ] AD prereq cleanup (G1 from §7) — renames, UPN suffix add, former-employee deletes, etc. +- [ ] Exit Entra Connect staging mode (G3-G5) +- [ ] Replace `SG-Caregivers-Pilot` with synced `SG-Caregivers` in CA policy targets +- [ ] Roll Phase 3 (all users at Cascades on Cascades net + compliant device → bypass) +- [ ] Run patched `onboard-tenant.sh` against all other already-onboarded ACG customer tenants to backfill CA Admin role + Policy.Read.All + +### Side-track from 4/25 (still in limbo) +- [ ] 7-mailbox shared conversion + Jodi Ramstack license removal (blocked on Exchange RBAC propagation lag) + +--- + +## Reference Information + +### Cascades tenant +- Tenant ID: `207fa277-e9d8-4eb7-ada1-1064d2221498` +- Tenant Admin SP appId: `709e6eed-0711-4875-9c44-2d3518c47063` +- Tenant Admin SP objectId in Cascades: `a5fa89a9-b735-4e10-b664-f042e265d137` +- Intune Manager SP appId: `46986910-aa47-4e5e-b596-f65c6b485abb` +- Cascades named location id: `061c6b06-b980-40de-bff9-6a50a4071f6f` +- Trusted IPs (both WANs): `72.211.21.217/32` (secondary), `184.191.143.62/32` (primary) +- Vault: `clients/cascades-tucson/wifi-cscnet.sops.yaml`, `clients/cascades-tucson/mdm-service-account.sops.yaml` + +### Cascades Intune resources +- Compliance policy: `CSC - Android Compliance` id `27eeaeda-8390-462e-a514-7d2a558f412c` +- Wi-Fi config: `CSC - CSCNet Wi-Fi (WPA2-Personal)` id `b572ba54-1a04-4c40-9ac5-dd5aec444611` +- Device restrictions: `CSC - Android Shared Phones Restrictions` id `070a76c2-a8c3-4f7f-9ba7-1f4ac5084184` +- SDM Authenticator config: id `a1bfbda0-a36c-45e5-8844-8470f80ecc8d` +- SDM Teams config: id `3c6a354c-1616-434b-ac81-4dad7795e67b` +- Enrollment profile: `CSC - Android Shared Phones`, token `MVDVVDMPSHYJAGDAJOCN` (expires 2026-06-22) +- Target group: `Cascades - Shared Phones` (cloud, dynamic, security) id `ea96f4b7-3000-45da-ab1f-ddb28f509526`, rule: `device.enrollmentProfileName -eq "CSC - Android Shared Phones"` + +### Microsoft directory role template IDs +- Conditional Access Administrator: `b1be1c3e-b65d-4f19-8427-f6fa0d97feb9` +- Security Administrator: `194ae4cb-b126-40b2-bd5b-6091b380977d` +- Global Reader: `f2ef992c-3afb-46b9-b7cf-a126ee74c451` + +### Microsoft Graph appRole IDs +- `Policy.Read.All`: `246dd0d5-5bd0-4def-940b-0421030a5b68` +- `Policy.ReadWrite.ConditionalAccess`: `01c0a623-fc9b-48e9-b794-0756f8e8f067` +- `RoleManagement.ReadWrite.Directory`: `9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8` + +### Currently-enrolled test phones (both noncompliant pending real sign-in flow) +- Device 1: SM-A146U, enrolled 2026-04-20, last sync 2026-04-23, isEncrypted=true, jailBroken=Unknown +- Device 2: SM-A146U, enrolled 2026-04-23, last sync 2026-04-27, isEncrypted=true, jailBroken=Unknown + +--- + +## Note for Mike + +Closing the loop on your 2026-04-28 note re: Cascades CA fix. + +**Path A vs Path B:** I executed Path B (PIM portal click as `sysadmin@cascadestucson.com`, Active assignment, permanent) before seeing your note approving Path A. Both produce the same end state — the directory role is in place on the Tenant Admin SP since 2026-04-28T18:45:50Z. No follow-up needed on the Cascades-specific assignment; it's done. + +**The actual blocker turned out to be different:** even with the directory role, CA endpoints kept returning 403 with "required scopes are missing in the token." Microsoft tightened `LIST /identity/conditionalAccess/namedLocations` and `/policies` to require `Policy.Read.All` as the application permission — `Policy.ReadWrite.ConditionalAccess` (which the SP carried) is no longer accepted for reads. Patched the Tenant Admin app manifest to add `Policy.Read.All`, granted in ACG home, Howard re-consented in Cascades. CA reads then worked. + +**`onboard-tenant.sh` patch — done.** Per the new tool-vs-project approval workflow, Howard approved this and I patched. Two changes: +1. Tenant Admin SP now gets `Conditional Access Administrator` directory role assigned at onboard time (idempotent — handles already-assigned via the existing Conflict-fallback in `assign_role`). +2. New backfill block: detects whether Tenant Admin SP has `Policy.Read.All` and grants it programmatically if missing. Required for tenants where Tenant Admin was consented before the manifest update on 2026-04-29. + +Validated with `bash onboard-tenant.sh 207fa277-e9d8-4eb7-ada1-1064d2221498 --dry-run` — clean. + +**Recommended backfill against other onboarded tenants:** re-run `bash onboard-tenant.sh ` against every customer where Tenant Admin was consented before 2026-04-29 today's manifest update. Idempotent — safe to re-run. If a tenant has a pre-existing PIM-managed CA Admin assignment (like Cascades does after Howard's PIM click), the role_assigned helper might mistakenly report MISSING because PIM-managed assignments may not appear in the legacy `roleAssignments` query. The actual `assign_role` call will hit `Conflict` and the helper treats that as success — so the script is still correct, just slightly noisy in its reporting. Worth a follow-up to teach `role_assigned` about `roleAssignmentSchedules` too, but not blocking. + +**Phase B Intune state at Cascades:** done in full (compliance, Wi-Fi, restrictions w/ MHS multi-app kiosk, both SDM app configs, all 8 apps assigned except Microsoft Launcher). Pilot phone enrollment will validate the full flow once Outlook landed (Howard added it tonight). + +**Wave 0 HIPAA pre-flight status:** BAA confirmed automatic via MOST. Break-glass admin not yet created — that's the next pre-flight before we touch CA policies. Audit retention deferred until Cascades-billed vs ACG-billed decision (your call when you're back). The CA bypass design needed reshaping — original §5 plan didn't actually achieve the bypass goal; corrected design is in this log under "CA design correction." + +If anything in the corrected CA design or the onboard-tenant patch isn't to your liking, ping back and I'll adjust. + +--- + +**Session duration:** ~7 hours continuous (2026-04-28 ~16:00 → 2026-04-29 ~07:30 PT, with breaks for portal tasks) +**Commits during session:** 1 sync auto-commit (`ff4577c`) before this log; this log + onboard-tenant patch will land in a fresh commit after `/save` +**Files changed in this log's commit:** 2 (`.claude/skills/remediation-tool/scripts/onboard-tenant.sh`, this session log)