Files
pxe-server/playbook/shopfloor-setup/gea-shopfloor-collections/04-SetControllerNicIP.ps1
cproudlock 00d4105956 04-SetControllerNicIP: broaden adapter enum + store-mismatch fix + netsh fallback
- Drop -Physical + MediaType filter from Get-NetAdapter; some OEM driver
  stacks report HardwareInterface=False or localize MediaType, hiding the
  Realtek controller NIC from the previous query.
- Refine corp-vs-controller classification: skip only if a gateway is set
  AND it's not 192.168.1.x, OR if the IP looks corp (10.x / 172.16-31.x).
  Keep candidates that are unconfigured, link-local, or 192.168.x.
- Disable DHCP in both PersistentStore and ActiveStore before New-NetIPAddress
  to avoid "Inconsistent parameters PolicyStore PersistentStore and Dhcp
  enabled" failures.
- Fall back to netsh interface ip set address when the PS cmdlets still
  fight each other; netsh writes both stores cleanly.
2026-05-24 07:04:02 -04:00

189 lines
8.2 KiB
PowerShell

# 04-SetControllerNicIP.ps1 - Auto-configure the controller-facing Realtek NIC
# at imaging time on gea-shopfloor-collections bays.
#
# Standard config (per post-deploy-debug-flowchart.md, section 2B):
# IP: 192.168.1.2
# Mask: 255.255.255.0 (/24)
# Gateway: (blank)
# DNS: (none - cleared)
#
# Logic:
# 1. Find physical Ethernet adapters whose vendor description contains
# 'Realtek'. (Shopfloor PCs ship with a Realtek PCIe GbE add-in card
# for the controller; the corp LAN NIC is typically Intel.)
# 2. Skip any candidate that already has a DHCP-assigned default gateway
# (that's the corp LAN NIC, not the controller).
# 3. Skip any candidate that already has 192.168.1.2 - already configured.
# 4. For the single remaining candidate, set manual IP / mask, clear DNS.
# 5. If multiple Realtek NICs remain after filtering, log + bail (manual
# investigation needed; the script will NOT guess which is controller).
#
# Imaging-time only - run once during Run-ShopfloorSetup for collections
# bays. Not registered as a GE-Enforce drift-catcher because once set,
# manual changes by the tech are intentional and should not be reverted.
#
# Log: C:\Logs\Shopfloor\controller-nic.log
$ErrorActionPreference = 'Continue'
$logDir = 'C:\Logs\Shopfloor'
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
$logFile = Join-Path $logDir 'controller-nic.log'
function Write-NicLog {
param([string]$Message, [string]$Level = 'INFO')
$line = '[{0}] [{1}] {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
Write-Host $line
}
$targetIP = '192.168.1.2'
$prefixLen = 24
Write-NicLog "=== 04-SetControllerNicIP start ==="
# Enumerate ALL adapters first, then filter. Don't use -Physical: on some
# OEM driver stacks the Realtek NIC reports HardwareInterface=False and
# would be excluded. Don't filter on MediaType either: some drivers report
# '802.3' as a localized string or as the numeric '6'.
$allAdapters = Get-NetAdapter -ErrorAction SilentlyContinue
Write-NicLog "All adapters on this machine:"
foreach ($a in $allAdapters) {
Write-NicLog " $($a.Name) ifIndex=$($a.ifIndex) desc='$($a.InterfaceDescription)' status=$($a.Status) mediatype='$($a.MediaType)' physical='$($a.HardwareInterface)'"
}
$candidates = @($allAdapters | Where-Object { $_.InterfaceDescription -match 'Realtek' })
if (-not $candidates -or $candidates.Count -eq 0) {
Write-NicLog "No Realtek adapters found - nothing to configure (PC may have no controller NIC)."
exit 0
}
Write-NicLog "Found $($candidates.Count) Realtek adapter(s):"
foreach ($c in $candidates) {
Write-NicLog " $($c.Name) ($($c.InterfaceDescription)) [status=$($c.Status)]"
}
# Filter out the corp-LAN Realtek (if any): has a DHCP-assigned default
# gateway (corp DHCP always hands one out) OR has an IP in a corp subnet
# (10.x / non-192.168.1.x). Keep candidates that are unconfigured (link-local
# / no IP / 192.168.x).
$filtered = @()
foreach ($c in $candidates) {
$ipCfg = Get-NetIPConfiguration -InterfaceIndex $c.ifIndex -ErrorAction SilentlyContinue
$gw = $null
if ($ipCfg -and $ipCfg.IPv4DefaultGateway) {
$gw = ($ipCfg.IPv4DefaultGateway | Select-Object -First 1).NextHop
}
$v4 = @()
if ($ipCfg -and $ipCfg.IPv4Address) {
$v4 = @($ipCfg.IPv4Address | ForEach-Object { $_.IPAddress })
}
Write-NicLog " $($c.Name): ips=$($v4 -join ',') gw=$gw"
# Already has the target IP -> done.
if ($v4 -contains $targetIP) {
Write-NicLog " Skipping $($c.Name) - already has $targetIP/$prefixLen (configured previously)"
continue
}
# Has a default gateway AND that gateway is NOT 192.168.1.x -> corp LAN, skip.
if ($gw -and ($gw -notmatch '^192\.168\.1\.')) {
Write-NicLog " Skipping $($c.Name) - has non-controller IPv4 default gateway $gw (likely corp LAN)"
continue
}
# Has a corp-style IP (10.x or 172.16-31.x) -> corp LAN, skip.
$isCorp = $false
foreach ($ip in $v4) {
if ($ip -match '^10\.' -or $ip -match '^172\.(1[6-9]|2[0-9]|3[01])\.') { $isCorp = $true; break }
}
if ($isCorp) {
Write-NicLog " Skipping $($c.Name) - has corp-subnet IP $($v4 -join ',') (not the controller NIC)"
continue
}
$filtered += $c
}
if ($filtered.Count -eq 0) {
Write-NicLog "After filtering, no candidate remains - either already configured or all Realtek adapters look like corp LAN. Nothing to do."
exit 0
}
if ($filtered.Count -gt 1) {
Write-NicLog "Multiple unconfigured Realtek adapters found ($($filtered.Count)). Refusing to guess which is the controller NIC. Manual configuration required." 'WARN'
foreach ($f in $filtered) { Write-NicLog " Ambiguous: $($f.Name) ($($f.InterfaceDescription))" 'WARN' }
exit 0
}
$target = $filtered[0]
Write-NicLog "Selected controller NIC: $($target.Name) ($($target.InterfaceDescription)) [ifIndex=$($target.ifIndex)]"
# Wipe any existing IPv4 addresses + gateways on this interface so the new
# static doesn't conflict with stale DHCP leases or APIPA addresses.
try {
Get-NetIPAddress -InterfaceIndex $target.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Remove-NetIPAddress -Confirm:$false -ErrorAction SilentlyContinue
Get-NetRoute -InterfaceIndex $target.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Where-Object { $_.DestinationPrefix -eq '0.0.0.0/0' } |
Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue
Write-NicLog "Cleared existing IPv4 addresses + default route on $($target.Name)"
} catch {
Write-NicLog "Failed to clear existing IPv4 state on $($target.Name): $_" 'WARN'
}
# Switch the interface to manual IP assignment + set the static.
#
# Windows store-mismatch trap: Set-NetIPInterface defaults to ActiveStore,
# New-NetIPAddress defaults to PersistentStore. If we only disable DHCP in
# ActiveStore, New-NetIPAddress fails with:
# "Inconsistent parameters PolicyStore PersistentStore and Dhcp enabled"
# Disable DHCP in BOTH stores first, then add the static IP. If the
# cmdlets still fight us, fall back to netsh (writes both stores cleanly).
$staticSet = $false
try {
Set-NetIPInterface -InterfaceIndex $target.ifIndex -Dhcp Disabled -PolicyStore PersistentStore -ErrorAction SilentlyContinue
Set-NetIPInterface -InterfaceIndex $target.ifIndex -Dhcp Disabled -PolicyStore ActiveStore -ErrorAction SilentlyContinue
New-NetIPAddress -InterfaceIndex $target.ifIndex -IPAddress $targetIP `
-PrefixLength $prefixLen -ErrorAction Stop | Out-Null
Write-NicLog "Set $targetIP/$prefixLen on $($target.Name) (no gateway)"
$staticSet = $true
} catch {
Write-NicLog "PowerShell static IP set failed: $_ -- falling back to netsh" 'WARN'
}
if (-not $staticSet) {
$mask = '255.255.255.0'
$netshOut = & netsh interface ip set address name="$($target.Name)" static $targetIP $mask 2>&1
if ($LASTEXITCODE -eq 0) {
Write-NicLog "netsh set $targetIP/$mask on $($target.Name)"
$staticSet = $true
} else {
Write-NicLog "netsh failed too: $netshOut" 'ERROR'
exit 1
}
}
# Clear DNS so the corp resolver doesn't get queried for controller-side hostnames.
try {
Set-DnsClientServerAddress -InterfaceIndex $target.ifIndex -ResetServerAddresses -ErrorAction Stop
Write-NicLog "Cleared DNS servers on $($target.Name)"
} catch {
Write-NicLog "Failed to clear DNS on $($target.Name): $_" 'WARN'
}
# Verify what landed.
try {
$finalCfg = Get-NetIPConfiguration -InterfaceIndex $target.ifIndex -ErrorAction SilentlyContinue
$finalIPs = ($finalCfg.IPv4Address | ForEach-Object { "$($_.IPAddress)/$($_.PrefixLength)" }) -join ', '
$finalGw = if ($finalCfg.IPv4DefaultGateway) { ($finalCfg.IPv4DefaultGateway | Select-Object -First 1).NextHop } else { '(none)' }
$finalDns = ($finalCfg.DNSServer | Where-Object AddressFamily -eq 2 | Select-Object -ExpandProperty ServerAddresses) -join ', '
if (-not $finalDns) { $finalDns = '(none)' }
Write-NicLog "Verify: $($target.Name) IPs=$finalIPs Gateway=$finalGw DNS=$finalDns"
} catch {
Write-NicLog "Verification readback failed: $_" 'WARN'
}
Write-NicLog "=== 04-SetControllerNicIP end ==="
exit 0