Kill wired NICs post-stage-2 until Report IP log appears

Recurring Phase 2 "Device Configuration" stuck: GE Intune Proactive
Remediation "Report IP" script enumerates Get-NetIPAddress and POSTs
all IPs to a GE webhook. Bays cabled to air-gapped PXE LAN have
10.9.100.x leak into that report. GE backend tags bays "not on corp
net" -> dynamic-group assignment-filter at GE excludes them from the
SFLD ConfigurationProfile (Function + SasToken OMA-URI) ->
HKLM:\SOFTWARE\GE\SFLD\DSC never populates -> Monitor Phase 2 gate
never closes. Confirmed via mdm-diag-F907T5X3 dump: every Microsoft
policy delivered fine, zero SFLD/GE-namespace OMA-URI present.

Fix flow:
1. Run-ShopfloorSetup line 43: disable every Up wired NIC right after
   stage 2 push. NIC names persisted to
   C:\Enrollment\disabled-wired-nics.txt for later re-enable.
2. Stages 3-6 status pushes fail silently while wired is down (PXE
   server lives on the air-gapped 10.9.100.0/24 LAN, unreachable from
   WiFi). Dashboard goes dark in that window.
3. PPKG installs, immediate reboot, AAD/Intune enroll over WiFi only.
4. IME boots, Report IP script fires with corp-WiFi IP only, writes
   C:\Logs\GE_Report_IP_Address*.txt. Webhook records clean IP. GE
   dynamic group eligibility flips. SFLD policy delivers next sync.
5. Monitor-IntuneProgress detects the log file, re-enables every NIC
   in the persisted list, sleeps 1s for link, then pushes idx=7 with
   DeviceId so the dashboard card flips to QR before the Intune-
   triggered LAPS-prompt reboot lands.

Phase 1 remains "in progress" on the dashboard until Report IP fires
- correct, the bay isn't actually registration-clean until then.

Files:
- Disable-WiredNics.ps1 (new) - persists names + disables
- Run-ShopfloorSetup.ps1 - call after stage 2 Report-Stage
- Monitor-IntuneProgress.ps1 - gate idx=7 push + re-enable

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-13 17:22:41 -04:00
parent b5a067bd48
commit b8328171eb
3 changed files with 102 additions and 9 deletions

View File

@@ -41,6 +41,23 @@ function Report-Stage {
} }
Report-Stage -Stage 'Run-ShopfloorSetup: starting' -Index 2 Report-Stage -Stage 'Run-ShopfloorSetup: starting' -Index 2
# Kill wired NICs immediately after stage 2 push. Goal: GE Intune Report
# IP webhook only ever sees this bay's corp-WiFi IP, never the PXE LAN
# (10.9.100.x) IP. Otherwise GE backend tags the bay "not on corp net"
# and dynamic-group assignment filters exclude it from the SFLD
# ConfigurationProfile -> Phase 2 stuck forever.
# Monitor-IntuneProgress re-enables wired once
# C:\Logs\GE_Report_IP_Address*.txt appears (proof the webhook fire saw
# the corp IP it needed). Side effect during disabled window:
# Send-PxeStatus pushes from stages 3-6 fail silently (PXE server lives
# on the air-gapped 10.9.100.0/24 LAN). Dashboard catches up at idx=7.
$disableWiredScript = Join-Path $PSScriptRoot 'shopfloor-setup\Shopfloor\lib\Disable-WiredNics.ps1'
if (Test-Path -LiteralPath $disableWiredScript) {
try { & $disableWiredScript } catch { Write-Warning "Disable-WiredNics threw: $_" }
} else {
Write-Warning "Disable-WiredNics.ps1 not found at $disableWiredScript - wired stays up (Report IP leak risk)"
}
# AutoLogonCount is NOT set here. Previously we bumped it to 99/4, but # AutoLogonCount is NOT set here. Previously we bumped it to 99/4, but
# Windows decrements it per-logon and at 0 clears AutoAdminLogon -- which # Windows decrements it per-logon and at 0 clears AutoAdminLogon -- which
# nukes the lockdown-configured ShopFloor autologon later in the chain. # nukes the lockdown-configured ShopFloor autologon later in the chain.

View File

@@ -0,0 +1,45 @@
# Disable-WiredNics.ps1
# Disables every Up wired (MediaType 802.3) NIC and records their names to
# C:\Enrollment\disabled-wired-nics.txt so Monitor-IntuneProgress can
# re-enable them once Report IP has run on WiFi-only.
#
# Reason: GE's Intune Proactive-Remediation "Report IP" script enumerates
# Get-NetIPAddress and POSTs every IP it finds to a GE webhook. When a
# shopfloor bay is still cabled to the air-gapped PXE LAN (10.9.100.0/24),
# the webhook sees 10.9.100.x as one of the device's IPs and tags the bay
# "not on corp net". A dynamic group / assignment-filter at GE then excludes
# the bay from receiving the SFLD ConfigurationProfile (Function + SasToken
# OMA-URI) -> Phase 2 "Device Configuration" never closes.
#
# Killing the wired NIC after stage 2 reports + before AAD-join makes the
# bay's first Report IP fire see corp-WiFi IP only. The bay is tagged
# clean, dynamic group eligibility flips, SFLD policy delivers normally.
# Monitor-IntuneProgress re-enables the NIC once Report IP's log file
# appears at C:\Logs\GE_Report_IP_Address*.txt.
$ErrorActionPreference = 'Continue'
$stateFile = 'C:\Enrollment\disabled-wired-nics.txt'
try {
$wired = Get-NetAdapter -ErrorAction Stop |
Where-Object {
$_.Status -eq 'Up' -and
$_.MediaType -eq '802.3' -and
$_.HardwareInterface -eq $true
}
if (-not $wired) {
Write-Host "Disable-WiredNics: no Up wired NICs found - nothing to disable."
return
}
$names = $wired | ForEach-Object { $_.Name }
$names | Out-File -FilePath $stateFile -Encoding ASCII -Force
Write-Host ("Disable-WiredNics: persisted {0} NIC name(s) -> {1}" -f $names.Count, $stateFile)
foreach ($n in $names) { Write-Host " - $n" }
$wired | Disable-NetAdapter -Confirm:$false -ErrorAction Continue
Write-Host "Disable-WiredNics: NICs disabled. Re-enable triggered by Monitor when GE_Report_IP_Address log appears."
} catch {
Write-Warning "Disable-WiredNics: failed: $_"
}

View File

@@ -223,8 +223,38 @@ function Get-Phase1 {
} }
# Push DeviceId to the PXE dashboard exactly once (the imaging.html card # Push DeviceId to the PXE dashboard exactly once (the imaging.html card
# renders a QR of it). Best-effort. # renders a QR of it). Gated on Report IP log existing: Run-ShopfloorSetup
# disabled wired NICs after stage 2 push so GE's Report IP webhook only
# sees the corp-WiFi IP. We wait for the log file to appear (proof Report
# IP fired clean), re-enable the NICs we recorded, sleep a tick so the
# interface comes back up, then push idx=7. Until the log exists, Phase 1
# stays "in progress" on the dashboard - which is correct, the bay is
# not actually "done with registration" until the Report IP step has
# cleared.
if ($script:cache.DeviceId -and -not $script:cache.DeviceIdReported) { if ($script:cache.DeviceId -and -not $script:cache.DeviceIdReported) {
$reportIpLog = Get-ChildItem -Path 'C:\Logs\GE_Report_IP_Address*.txt' -ErrorAction SilentlyContinue |
Select-Object -First 1
if ($reportIpLog) {
# Re-enable any wired NICs Run-ShopfloorSetup disabled. Quick
# before the eventual Intune-driven reboot fires so the bay
# gets a clean wired path back to the PXE dashboard for idx=7
# + idx=8 pushes.
$nicListFile = 'C:\Enrollment\disabled-wired-nics.txt'
if (Test-Path $nicListFile) {
try {
$nicNames = Get-Content $nicListFile -ErrorAction Stop
foreach ($n in $nicNames) {
if ([string]::IsNullOrWhiteSpace($n)) { continue }
try { Enable-NetAdapter -Name $n -Confirm:$false -ErrorAction Stop }
catch { Write-Warning "Enable-NetAdapter '$n' failed: $_" }
}
Start-Sleep -Seconds 1
Remove-Item $nicListFile -Force -ErrorAction SilentlyContinue
} catch {
Write-Warning "Re-enable wired NICs failed: $_"
}
}
Ensure-SendPxeStatus Ensure-SendPxeStatus
if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) { if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) {
try { try {
@@ -235,6 +265,7 @@ function Get-Phase1 {
} catch { } } catch { }
} }
} }
}
# Lockdown-applied auto-completion. Fleet-wide reality: bays use a LOCAL # Lockdown-applied auto-completion. Fleet-wide reality: bays use a LOCAL
# ShopFloor account, so AzureAdPrt stays NO and user-scoped Intune policies # ShopFloor account, so AzureAdPrt stays NO and user-scoped Intune policies