Filtered Report IP shim - POSTs only WJ corp ranges to GE webhook

GE's Intune-deployed ReportIPAddresses_v2.ps1 filters Get-NetIPAddress
with $_.StartsWith("10.") - too broad for WJ where PXE LAN is 10.9.x.
Can't modify the GE script (signed). Workaround: run our own POST to
the same Tines webhook with a tight subnet filter, beating GE's
script to the punch.

New Invoke-FilteredReportIP.ps1 (lib/):
 - Walks Get-NetIPAddress -AddressFamily IPv4
 - Filters strictly to 10.134.48.0/23 OR 10.48.249.0/26 (WJ corp)
 - POSTs to https://tines.apps.geaerospace.com/webhook/.../... with
   {host, fqdn, IP, force_update} body matching GE's payload shape
 - Local dedup via C:\ProgramData\GEA\FilteredReportIP\last-ip.txt
 - 6 retries with 10s backoff on transient HTTP error
 - Logs to C:\Logs\FilteredReportIP.log

Monitor-IntuneProgress main loop calls it each tick until it
succeeds once. After success, $filteredReportIpSucceeded flag short-
circuits further attempts.

If WJ later moves to a different VLAN, edit the $allowedRanges array
in Invoke-FilteredReportIP.ps1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-14 16:16:39 -04:00
parent 4dd300e7ab
commit a80bdd6923
2 changed files with 115 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
# Invoke-FilteredReportIP.ps1
# Reports this bay's GE corp IP to the GE Tines webhook with a tight
# subnet filter so PXE LAN (10.9.100.x) or any other 10.x stray address
# never leaks. Mirrors the payload shape of GE's Intune-deployed
# ReportIPAddresses_v2.ps1 so the receiving Tines workflow accepts it.
#
# WHY:
# GE's signed Report IP script filters Get-NetIPAddress with
# $_.StartsWith("10.") - too broad for WJ where PXE LAN is also 10.x.
# Calling our own POST with a narrow filter guarantees the webhook
# only ever sees the bay's AESFMA corp 10.x address.
#
# ALLOWED RANGES (WJ-specific - update if site moves to a new VLAN):
# 10.134.48.0/23 (10.134.48.0 - 10.134.49.255)
# 10.48.249.0/26 (10.48.249.0 - 10.48.249.63)
param(
[switch]$Force,
[int]$MaxAttempts = 6,
[int]$RetrySeconds = 10
)
$ErrorActionPreference = 'Continue'
$logFile = 'C:\Logs\FilteredReportIP.log'
New-Item -ItemType Directory -Path 'C:\Logs' -Force -ErrorAction SilentlyContinue | Out-Null
function Log([string]$msg) {
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
"$ts $msg" | Tee-Object -FilePath $logFile -Append
}
# Allowed ranges (CIDR -> first IP int + prefix bits)
$allowedRanges = @(
@{ Network = '10.134.48.0'; PrefixLen = 23 },
@{ Network = '10.48.249.0'; PrefixLen = 26 }
)
function ConvertTo-Uint32($ip) {
$bytes = ([System.Net.IPAddress]::Parse($ip)).GetAddressBytes()
[Array]::Reverse($bytes)
return [BitConverter]::ToUInt32($bytes, 0)
}
function Test-InAllowedRange([string]$ip) {
try {
$ipInt = ConvertTo-Uint32 $ip
foreach ($r in $allowedRanges) {
$netInt = ConvertTo-Uint32 $r.Network
$mask = [uint32]([math]::Pow(2, 32) - [math]::Pow(2, 32 - $r.PrefixLen))
if (($ipInt -band $mask) -eq ($netInt -band $mask)) { return $true }
}
} catch {}
return $false
}
Log "=== Filtered Report IP fire ==="
$allIPs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue |
Where-Object { $_.IPAddress -notmatch '^169\.254' -and $_.IPAddress -ne '127.0.0.1' } |
Select-Object -ExpandProperty IPAddress
Log "All IPv4 addresses on this bay: $($allIPs -join ', ')"
$match = @($allIPs | Where-Object { Test-InAllowedRange $_ })
if ($match.Count -eq 0) {
Log "No IP in allowed ranges (10.134.48.0/23 or 10.48.249.0/26). Not POSTing."
exit 2
}
$reportIP = $match[0]
Log "Reporting IP: $reportIP"
$statusFile = "$env:ProgramData\GEA\FilteredReportIP\last-ip.txt"
$lastIP = ''
if (Test-Path $statusFile) { $lastIP = (Get-Content $statusFile -ErrorAction SilentlyContinue) -join '' }
if (-not $Force -and $lastIP -eq $reportIP) {
Log "IP unchanged from last POST ($lastIP). Skipping."
exit 0
}
$webhookUrl = 'https://tines.apps.geaerospace.com/webhook/aa891efac08bab3d077d9956ab6614d0/c687048cb0ee35c33760ad14cb9257eb'
$body = @{
host = $env:COMPUTERNAME
fqdn = "$env:COMPUTERNAME.device.geaerospace.net"
IP = $reportIP
force_update = if ($Force) { 'True' } else { 'False' }
}
for ($i = 1; $i -le $MaxAttempts; $i++) {
try {
Log "POST attempt $i/$MaxAttempts ..."
$resp = Invoke-WebRequest -Uri $webhookUrl -Method POST -Body $body -TimeoutSec 60 -UseBasicParsing -ErrorAction Stop
Log "POST succeeded. StatusCode=$($resp.StatusCode) Content=$($resp.Content)"
New-Item -ItemType Directory -Path (Split-Path $statusFile) -Force -ErrorAction SilentlyContinue | Out-Null
Set-Content -Path $statusFile -Value $reportIP -Force
exit 0
} catch {
Log "POST attempt $i failed: $_"
if ($i -lt $MaxAttempts) { Start-Sleep -Seconds $RetrySeconds }
}
}
Log "All $MaxAttempts attempts failed - giving up."
exit 1

View File

@@ -1190,6 +1190,7 @@ try {
$lastSync = Get-Date $lastSync = Get-Date
$nextRetrigger = $lastSync.AddMinutes($RetriggerMinutes) $nextRetrigger = $lastSync.AddMinutes($RetriggerMinutes)
$filteredReportIpSucceeded = $false
while ($true) { while ($true) {
$snap = Get-Snapshot $snap = Get-Snapshot
@@ -1201,6 +1202,20 @@ try {
$qrRefreshed = [bool]($qrText -notmatch 'not yet Azure AD joined') $qrRefreshed = [bool]($qrText -notmatch 'not yet Azure AD joined')
} }
# Filtered Report IP shim - POSTs this bay's GE corp IP to the
# Tines webhook directly with a tight subnet filter. Fires each
# tick until it succeeds once (script's own status-file dedup
# prevents duplicate POSTs). Bypasses the timing window where
# GE's Intune-deployed Report IP misses (it may run before the
# SCEP cert + AESFMA join land).
if (-not $filteredReportIpSucceeded) {
$filteredScript = Join-Path $PSScriptRoot 'Invoke-FilteredReportIP.ps1'
if (Test-Path $filteredScript) {
$rc = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $filteredScript 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) { $filteredReportIpSucceeded = $true }
}
}
Clear-Host Clear-Host
Format-Snapshot -Snap $snap -LastSync $lastSync -NextRetrigger $nextRetrigger Format-Snapshot -Snap $snap -LastSync $lastSync -NextRetrigger $nextRetrigger
Write-Host "" Write-Host ""