Monitor: gate WiFi swap on SCEP cert + Phase 1 done on AESFMA connected
Two related fixes for the WiFi handoff timing: 1. WiFi swap (delete INTERNETACCESS + connect AESFMA) was firing on Phase 1 essentials being green (AAD + Intune + EmTask + baseline policies >=5). That signal flips ~minutes BEFORE the Intune SCEP machine cert actually lands in LocalMachine\My. Without the cert, AESFMA EAP-TLS auth fails and the bay has no path at all (we just deleted INTERNETACCESS). Stuck. New gate: walk Cert:\LocalMachine\My for any cert with Client Authentication EKU (1.3.6.1.5.5.7.3.2). When that's present, SCEP has delivered, AESFMA EAP-TLS will succeed. Swap then fires safely. 2. Phase 1 row on the on-bay Monitor display now ALSO requires AESFMA to be actively connected (parsed from netsh wlan show interfaces: SSID=AESFMA + State=connected). Phase 1 stays IN PROGRESS until the bay is operationally on corp WLAN, not just data-side enrolled. Matches user request "not complete phase 1 until AESFMA is ready". idx=7 dashboard push still fires on the original Phase 1 essentials gate so the QR appears as soon as Intune registers the device, independent of AESFMA join timing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -309,29 +309,41 @@ function Get-Phase1 {
|
|||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
# Once Intune registration is fully landed (AAD-joined + Intune-enrolled
|
# Once Intune registration is fully landed (AAD-joined + Intune-enrolled
|
||||||
# + EnterpriseMgmt task present + baseline policies arrived), three
|
# + EnterpriseMgmt task present + baseline policies arrived):
|
||||||
# things must happen together:
|
# - Push idx=7 to PXE dashboard with the DeviceId / QR.
|
||||||
# 1. Delete INTERNETACCESS WiFi profile (gets bay off 172.16.x)
|
# The INTERNETACCESS -> AESFMA WiFi swap is GATED SEPARATELY on the
|
||||||
# 2. Connect AESFMA (gets bay onto corp 10.x via EAP-TLS - cert is
|
# actual SCEP-provisioned machine cert landing in LocalMachine\My
|
||||||
# already in LocalMachine\My thanks to Intune SCEP)
|
# with Client Authentication EKU. Phase 1 essentials flip earlier
|
||||||
# 3. Push idx=7 to the PXE dashboard with the captured DeviceId so
|
# than the cert delivery, and tearing INTERNETACCESS without the
|
||||||
# the dashboard card shows the QR for the Intune device id.
|
# cert present leaves the bay with no path (AESFMA EAP-TLS would
|
||||||
# All three fire in one shot per Monitor lifetime via cache flags.
|
# fail). Wait for the cert before swapping.
|
||||||
$phase1Essential = ($script:cache.AzureAdJoined -and
|
$phase1Essential = ($script:cache.AzureAdJoined -and
|
||||||
$script:cache.IntuneEnrolled -and
|
$script:cache.IntuneEnrolled -and
|
||||||
$script:cache.EmTaskExists -and
|
$script:cache.EmTaskExists -and
|
||||||
$policiesBaselineReady)
|
$policiesBaselineReady)
|
||||||
if ($phase1Essential -and -not $script:cache.InternetAccessDeleted) {
|
if (-not $script:cache.InternetAccessDeleted) {
|
||||||
|
# Look for any LocalMachine\My cert with Client Auth EKU
|
||||||
|
# (1.3.6.1.5.5.7.3.2). That's what AESFMA EAP-TLS uses.
|
||||||
|
$hasMachineClientAuthCert = $false
|
||||||
try {
|
try {
|
||||||
Write-Host "Intune registration complete - deleting INTERNETACCESS profile + reconnecting to AESFMA..."
|
$clientAuthEku = '1.3.6.1.5.5.7.3.2'
|
||||||
$delOut = netsh wlan delete profile name="INTERNETACCESS" 2>&1 | Out-String
|
$hasMachineClientAuthCert = [bool](Get-ChildItem 'Cert:\LocalMachine\My' -ErrorAction SilentlyContinue |
|
||||||
Write-Host $delOut
|
Where-Object {
|
||||||
Start-Sleep -Seconds 2
|
$_.EnhancedKeyUsageList.ObjectId -contains $clientAuthEku
|
||||||
$conOut = netsh wlan connect name="AESFMA" ssid="AESFMA" 2>&1 | Out-String
|
} | Select-Object -First 1)
|
||||||
Write-Host $conOut
|
} catch {}
|
||||||
$script:cache.InternetAccessDeleted = $true
|
if ($hasMachineClientAuthCert) {
|
||||||
} catch {
|
try {
|
||||||
Write-Warning "WiFi swap (INTERNETACCESS -> AESFMA) failed: $_"
|
Write-Host "SCEP machine cert detected - swapping WiFi: delete INTERNETACCESS, connect AESFMA..."
|
||||||
|
$delOut = netsh wlan delete profile name="INTERNETACCESS" 2>&1 | Out-String
|
||||||
|
Write-Host $delOut
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
$conOut = netsh wlan connect name="AESFMA" ssid="AESFMA" 2>&1 | Out-String
|
||||||
|
Write-Host $conOut
|
||||||
|
$script:cache.InternetAccessDeleted = $true
|
||||||
|
} catch {
|
||||||
|
Write-Warning "WiFi swap (INTERNETACCESS -> AESFMA) failed: $_"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($phase1Essential -and $script:cache.DeviceId -and -not $script:cache.DeviceIdReported) {
|
if ($phase1Essential -and $script:cache.DeviceId -and -not $script:cache.DeviceIdReported) {
|
||||||
@@ -814,13 +826,27 @@ function Format-Snapshot {
|
|||||||
# not just "arriving". Stops the category prompt firing pre-first-reboot
|
# not just "arriving". Stops the category prompt firing pre-first-reboot
|
||||||
# when only ~4 subkeys are present (we tested this empirically; clicking
|
# when only ~4 subkeys are present (we tested this empirically; clicking
|
||||||
# "assign category" at 4 subkeys = imaging stalls + re-image required).
|
# "assign category" at 4 subkeys = imaging stalls + re-image required).
|
||||||
|
# AESFMA connected = bay has a live corp WLAN association on the
|
||||||
|
# machine-auth profile. That's the natural ground-truth that Phase 1
|
||||||
|
# is operationally done (not just the data-side flags). Check the
|
||||||
|
# WLAN state via netsh wlan show interfaces - look for SSID=AESFMA
|
||||||
|
# with state=connected.
|
||||||
|
$aesfmaConnected = $false
|
||||||
|
try {
|
||||||
|
$wlanOut = netsh wlan show interfaces 2>$null
|
||||||
|
if ($wlanOut -match '(?ms)SSID\s*:\s*AESFMA.*?State\s*:\s*connected') {
|
||||||
|
$aesfmaConnected = $true
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
$p1Done = ($Snap.Phase1.AzureAdJoined -and $Snap.Phase1.IntuneEnrolled -and
|
$p1Done = ($Snap.Phase1.AzureAdJoined -and $Snap.Phase1.IntuneEnrolled -and
|
||||||
$Snap.Phase1.EmTaskExists -and $Snap.Phase1.PoliciesBaselineReady)
|
$Snap.Phase1.EmTaskExists -and $Snap.Phase1.PoliciesBaselineReady -and
|
||||||
|
$aesfmaConnected)
|
||||||
$p1Status = Get-PhaseStatus @(
|
$p1Status = Get-PhaseStatus @(
|
||||||
@{ Ok = $Snap.Phase1.AzureAdJoined; Failed = $false },
|
@{ Ok = $Snap.Phase1.AzureAdJoined; Failed = $false },
|
||||||
@{ Ok = $Snap.Phase1.IntuneEnrolled; Failed = $false },
|
@{ Ok = $Snap.Phase1.IntuneEnrolled; Failed = $false },
|
||||||
@{ Ok = $Snap.Phase1.EmTaskExists; Failed = $false },
|
@{ Ok = $Snap.Phase1.EmTaskExists; Failed = $false },
|
||||||
@{ Ok = $Snap.Phase1.PoliciesBaselineReady; Failed = $false }
|
@{ Ok = $Snap.Phase1.PoliciesBaselineReady; Failed = $false },
|
||||||
|
@{ Ok = $aesfmaConnected; Failed = $false }
|
||||||
)
|
)
|
||||||
|
|
||||||
# Phase 6 / Lockdown (shared by both flows, rendered last).
|
# Phase 6 / Lockdown (shared by both flows, rendered last).
|
||||||
|
|||||||
Reference in New Issue
Block a user