Public/Portproxy/Remove-RouterSshPortProxy.ps1
|
<# .NOTES Do not run this file directly. Dot-sourced by deprovision.ps1. #> # --------------------------------------------------------------------------- # Remove-RouterSshPortProxy # Teardown counterpart to Set-RouterSshPortProxy. Removes every host-side # netsh portproxy rule that forwards to <ConnectAddress>:<ConnectPort> # (the router VM's SSH endpoint), whatever listen address each rule binds. # # Why key on the CONNECT target, not the listen target: # A router's relay can exist under more than one listen address across a # machine's history (0.0.0.0 default vs an operator-pinned 127.0.0.1), # and netsh portproxy state PERSISTS across VM/switch teardown - reboots, # re-provisions, the lot - in HKLM\...\Services\PortProxy. Keying removal # on the router IP sweeps every relay pointing at the router being torn # down, which is the only thing that stops rules accumulating per router # IP across lifecycles. # # Why a leftover rule is not harmless: # The WSL-side relay auto-discovery (the Ansible bridge) keys on the # rule's CONNECT address, so a stale rule for a decommissioned router IP # can shadow the discovery for a different router and silently misroute # - exactly the drift this removal exists to prevent. # # Idempotent: no matching rule -> logs and returns. Best-effort per rule - # a transient netsh delete failure is warned, not thrown, so one stuck # rule cannot derail the rest of a teardown. # --------------------------------------------------------------------------- function Remove-RouterSshPortProxy { [CmdletBinding()] param( # Router VM's reachable IP on the host's Internal vSwitch - the # connect target whose relays are removed. The same value the # provision side passed to Set-RouterSshPortProxy's -ConnectAddress. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $ConnectAddress, [int] $ConnectPort = 22 ) $stale = @(Get-NetshPortProxyRules | Where-Object { $_.ConnectAddress -eq $ConnectAddress -and $_.ConnectPort -eq $ConnectPort }) if ($stale.Count -eq 0) { Write-Host (" [portproxy] no rule forwarding to {0}:{1} - nothing to remove." -f ` $ConnectAddress, $ConnectPort) return } foreach ($rule in $stale) { $listenAddress = $rule.ListenAddress $listenPort = $rule.ListenPort Write-Host (" [portproxy] removing {0}:{1} -> {2}:{3}" -f ` $listenAddress, $listenPort, $rule.ConnectAddress, $rule.ConnectPort) & netsh interface portproxy delete v4tov4 ` listenaddress=$listenAddress listenport=$listenPort | Out-Null # netsh signals failure via exit code, not an exception. A stuck # delete leaves a stale rule (re-runnable on the next teardown) but # must not abort the wider teardown, so warn and continue. if ($LASTEXITCODE -ne 0) { Write-Warning (" [portproxy] netsh delete of {0}:{1} returned exit {2}; rule may persist - re-run deprovision to retry." -f ` $listenAddress, $listenPort, $LASTEXITCODE) } } } |