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:
@@ -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
|
||||||
@@ -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 ""
|
||||||
|
|||||||
Reference in New Issue
Block a user