From b8328171eb6d0903a70777978e7382cbe2443831 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Wed, 13 May 2026 17:22:41 -0400 Subject: [PATCH] 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) --- .../shopfloor-setup/Run-ShopfloorSetup.ps1 | 17 +++++++ .../Shopfloor/lib/Disable-WiredNics.ps1 | 45 +++++++++++++++++ .../Shopfloor/lib/Monitor-IntuneProgress.ps1 | 49 +++++++++++++++---- 3 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 playbook/shopfloor-setup/Shopfloor/lib/Disable-WiredNics.ps1 diff --git a/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 b/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 index 9f96fcb..5ae7a38 100644 --- a/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 +++ b/playbook/shopfloor-setup/Run-ShopfloorSetup.ps1 @@ -41,6 +41,23 @@ function Report-Stage { } 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 # Windows decrements it per-logon and at 0 clears AutoAdminLogon -- which # nukes the lockdown-configured ShopFloor autologon later in the chain. diff --git a/playbook/shopfloor-setup/Shopfloor/lib/Disable-WiredNics.ps1 b/playbook/shopfloor-setup/Shopfloor/lib/Disable-WiredNics.ps1 new file mode 100644 index 0000000..78d1ddc --- /dev/null +++ b/playbook/shopfloor-setup/Shopfloor/lib/Disable-WiredNics.ps1 @@ -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: $_" +} diff --git a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 index dd42577..2268e05 100644 --- a/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 +++ b/playbook/shopfloor-setup/Shopfloor/lib/Monitor-IntuneProgress.ps1 @@ -223,16 +223,47 @@ function Get-Phase1 { } # 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) { - Ensure-SendPxeStatus - if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) { - try { - Send-PxeStatus -Stage 'Monitor-IntuneProgress: Intune Device ID captured' ` - -StageIndex 7 -StageTotal 8 ` - -IntuneDeviceId $script:cache.DeviceId - $script:cache.DeviceIdReported = $true - } catch { } + $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 + if (Get-Command Send-PxeStatus -ErrorAction SilentlyContinue) { + try { + Send-PxeStatus -Stage 'Monitor-IntuneProgress: Intune Device ID captured' ` + -StageIndex 7 -StageTotal 8 ` + -IntuneDeviceId $script:cache.DeviceId + $script:cache.DeviceIdReported = $true + } catch { } + } } }