Public/Ics/Test-IcsDnsProxyReachable.ps1

<#
.NOTES
    Do not run this file directly. Dot-sourced by provision.ps1.
#>


# ---------------------------------------------------------------------------
# Test-IcsDnsProxyReachable
# Preflight check 7: the exact DNS path the VM is about to use -
# libc -> systemd-resolved stub -> ICS DNS proxy (LAN-side
# vEthernet IP) -> host's WiFi DNS. Probes against
# archive.ubuntu.com because the package the seed needs (dnsmasq)
# resolves there; success means cloud-init's apt phase has a
# working name service before we even create the VM.
#
# Auto-repair on probe FAIL is bounded to a SINGLE Reset-IcsSharing
# attempt + one re-probe. If the re-probe also fails it is a
# genuine ICS bug, not a transient state flap; the operator gets
# one clear FAIL with the next steps in Detail, not a retry
# spiral. Returns a finding object; orchestrator routes to
# Add-Finding.
#
# ICS's DNS proxy is known to enter a broken state where it
# answers UDP/53 with TCP RSTs (host-side Resolve-DnsName -Server
# 192.168.137.1 returns "An existing connection was forcibly
# closed"). Restart-Service SharedAccess does NOT recover from
# this; the canonical fix is to toggle the WiFi adapter's Sharing
# checkbox off + on, which Reset-IcsSharing automates via the
# HNetCfg COM API.
# ---------------------------------------------------------------------------

function Test-IcsDnsProxyReachable {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $DnsProbeTarget,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $LanAdapterName,

        # When omitted, auto-repair is skipped even on probe FAIL
        # (Reset-IcsSharing needs to know which connection is
        # actually shared, and we will not guess from the LAN side
        # alone).
        [string] $WanAdapterName,

        [switch] $NoAutoRepair
    )

    if (Test-IcsDnsReachable -Server $DnsProbeTarget) {
        return [PSCustomObject]@{
            Status = 'PASS'
            Label  = "ICS DNS proxy answers at $DnsProbeTarget"
            Detail = "Resolve-DnsName archive.ubuntu.com succeeded against the ICS gateway."
        }
    }

    if ($NoAutoRepair -or -not $WanAdapterName) {
        $hint = if ($NoAutoRepair) {
            "Auto-repair disabled by -NoAutoRepair."
        } else {
            "WanAdapterName not supplied so auto-repair is skipped."
        }
        # Same diagnostics as the terminal FAIL below: the repair was skipped,
        # not attempted-and-failed, so the operator still needs the verdict and
        # its commands - and getting them from one place keeps the two FAIL
        # paths from drifting into differently-worded advice.
        $diag = Get-IcsDnsFailureDiagnostics -DnsProbeTarget $DnsProbeTarget `
                                             -WanAdapterName $WanAdapterName `
                                             -LanAdapterName $LanAdapterName
        return [PSCustomObject]@{
            Status = 'FAIL'
            Label  = "ICS DNS proxy answers at $DnsProbeTarget"
            Detail = "Probe failed. $hint $diag"
        }
    }

    try {
        Reset-IcsSharing -WanInterfaceName $WanAdapterName `
                         -LanInterfaceName $LanAdapterName
    } catch {
        return [PSCustomObject]@{
            Status = 'FAIL'
            Label  = "ICS DNS proxy answers at $DnsProbeTarget"
            # No command list here: the repair itself is what broke, and its
            # own throw carries the cause. The one thing the operator always
            # needs is the adapter-name check that COM lookups fail on.
            Detail = "Resolve-DnsName failed; Reset-IcsSharing also failed: " +
                     "$($_.Exception.Message) Confirm the WAN/LAN names with Get-NetAdapter."
        }
    }

    if (Test-IcsDnsReachable -Server $DnsProbeTarget) {
        return [PSCustomObject]@{
            Status = 'PASS'
            Label  = "ICS DNS proxy answers at $DnsProbeTarget (auto-repaired)"
            Detail = "Initial probe failed; Reset-IcsSharing kicked the proxy; re-probe succeeded."
        }
    }

    # Proxy still dead after the one-shot repair. Rather than hand the
    # operator a "check X and Y" checklist, probe the two distinguishing
    # signals (SharedAccess status + host-side upstream DNS) and report
    # the single fix that applies. See Get-IcsDnsFailureDiagnostics.
    # Adapter names are forwarded so the verdict's Reset-IcsSharing example is
    # the real invocation for this host rather than a placeholder.
    $diag = Get-IcsDnsFailureDiagnostics -DnsProbeTarget $DnsProbeTarget `
                                         -WanAdapterName $WanAdapterName `
                                         -LanAdapterName $LanAdapterName
    return [PSCustomObject]@{
        Status = 'FAIL'
        Label  = "ICS DNS proxy answers at $DnsProbeTarget"
        Detail = "Probe failed and stayed failing after Reset-IcsSharing. $diag"
    }
}