Public/Get-WinClatInstance.ps1

Function Get-WinClatInstance {

    <#

    .SYNOPSIS
        Returns per-interface Windows CLAT (WinCLAT) state.

    .DESCRIPTION
        Wraps 'netsh.exe interface clat show instance' for each up network adapter with IPv6 enabled and returns WinClat.Instance objects. Runs locally or against one or more remote computers in parallel. Requires elevation on the target computer.

        Adapters without IPv6 bound, such as a physical NIC attached to a Hyper-V virtual switch, are skipped because CLAT can never run on them. Virtual adapters with IPv6 bound, such as a Hyper-V vEthernet adapter shared with the host or a VPN adapter, are included.

        Every computer produces at least one object. Computers that cannot be reached return Status Unreachable, computers without the WinCLAT feature return FeatureNotAvailable (NotSupported on Windows Server, which Microsoft does not deliver WinCLAT to), and sessions without elevation return ElevationRequired. WinCLAT is generally available on Windows 11 version 25H2 build 26200.9550 and later. A computer with no up adapter with IPv6 enabled matching InterfaceAlias returns one Success object with no interface data and an explanatory Message, without an error.

        TriggerSource shows how an enabled instance was started, read from event 1106 in the Microsoft-Windows-IPxlatCfg/Operational log on the target: Pref64 RA, No IPv4, Manual, or Unknown (<code>) for a code not yet documented. It is empty when the instance is disabled or the event is not found, for example after the log was cleared. Mode is Manual when TriggerSource is Manual and Automatic for any other TriggerSource. Windows keeps a manually enabled instance running after AutoEnable is enabled again, so Mode can be Manual while AutoEnable is enabled. When TriggerSource is empty, Mode is Automatic when the active AutoEnable setting is enabled and Manual when it is disabled. RemotePrefixRemainingLifetimeRaw holds the netsh text only when the lifetime could not be converted to a TimeSpan, and is empty otherwise, so every object has the same columns for Export-Csv. It is left out of the list shown for a single result; use Format-List -Property * to see it. CollectedAtUtc records when netsh ran on the target, in UTC.

        The CLAT IPv6 address is regenerated each time the instance is re-created. Do not treat it as a stable identifier.

        Remote computers must have WinRM enabled, typically through Group Policy. Roaming devices may be offline at run time. Each remote computer is first checked on TCP 5985 (WinRM over HTTP); one that does not answer within 2 seconds, or whose name does not resolve, is returned as Unreachable at once instead of after a WinRM timeout. Failures are not written as errors: every computer returns its result with a Status and a Message that explains it, and when a run for several computers has any computer that did not complete, one warning at the end gives the count. To list them, filter the results, for example with Where-Object -FilterScript { $_.Status -ne 'Success' }.

    .PARAMETER InterfaceAlias
        Limits results to up adapters with IPv6 enabled whose name matches any of these values. Wildcards are supported.

    .PARAMETER ComputerName
        One or more computers to query. Accepts pipeline input by value and by property name, including objects from Get-ADComputer and Get-ADObject; DNSHostName is preferred over Name. Blank values and values beginning with '#' are ignored, so Get-Content output can be piped directly. Defaults to the local computer.

    .PARAMETER Credential
        Credentials used to connect to remote computers. Not used for the local computer.

    .PARAMETER ThrottleLimit
        Maximum number of concurrent remote connections. The default is 32.

    .INPUTS
        System.String. Computer names.

        System.Management.Automation.PSObject. Objects with a DNSHostName, ComputerName, Name, or CN property, such as those from Get-ADComputer and Get-ADObject.

    .OUTPUTS
        WinClat.Instance. When a run returns exactly one result, it also carries the type name WinClat.Instance#Single, which selects a list view instead of the default table.

    .EXAMPLE
        Get-WinClatInstance

        Returns the WinCLAT state of every up adapter with IPv6 enabled on the local computer. Run from an elevated session.

    .EXAMPLE
        Get-WinClatInstance -ComputerName CLIENT4 -InterfaceAlias 'Wi-Fi'

        Returns the WinCLAT state of the Wi-Fi adapter on CLIENT4.

    .EXAMPLE
        Get-Content -Path .\clients.txt | Get-WinClatInstance | Where-Object -FilterScript { $_.IsTranslating } | Export-Csv -Path .\translating.csv -NoTypeInformation

        Exports every interface that is actively translating across the computers in clients.txt.

    .EXAMPLE
        Get-Content -Path .\clients.txt | Get-WinClatInstance | Where-Object -FilterScript { $_.Status -ne 'Unreachable' }

        Returns the interfaces of the computers in clients.txt that could be reached, leaving out the Unreachable rows. The warning at the end still counts the computers that were unreachable.

    .LINK
        https://learn.microsoft.com/windows-server/administration/windows-commands/netsh

    .NOTES
        Version: 1.0.0
        Creation Date: October 28, 2026
        Last Updated: October 28, 2026
        Author: Richard Hicks
        Organization: Richard M. Hicks Consulting, Inc.
        Contact: rich@richardhicks.com
        Website: https://www.richardhicks.com/

        See docs/DESIGN.md, section 7.2.

    #>


    [CmdletBinding(PositionalBinding = $false)]
    [OutputType('WinClat.Instance')]

    Param (

        [SupportsWildcards()]
        [string[]]$InterfaceAlias,
        [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('DNSHostName', 'CN', 'Name')]
        [string[]]$ComputerName = $env:COMPUTERNAME,

        [PSCredential]$Credential,

        [ValidateRange(1, 1024)]
        [int]$ThrottleLimit = 32

    )

    Begin {

        $Targets = New-Object -TypeName 'System.Collections.Generic.List[string]'

    }

    Process {

        # Read $_ only for pipeline input; otherwise it may be an outer ForEach-Object variable.
        $PipelineObject = $null

        If ($MyInvocation.ExpectingInput) {

            $PipelineObject = $_

        }

        ForEach ($Name in @(Get-WinClatTargetName -InputObject $PipelineObject -ComputerName $ComputerName -Cmdlet $PSCmdlet)) {

            $Targets.Add($Name)

        }

    }

    End {

        $NewFailure = {

            Param ($Name, $Status, $Message)

            ConvertTo-WinClatInstance -ComputerName $Name -Status $Status -Message $Message -CollectedAtUtc (Get-Date).ToUniversalTime()

        }

        # The alias list travels as one argument, so wrap it to stop it being spread across parameters.
        $Aliases = [string[]]@($InterfaceAlias | Where-Object -FilterScript { $_ })

        $RemoteParameters = @{

            ComputerName     = $Targets.ToArray()
            ScriptBlock      = Get-WinClatInstanceScript
            ArgumentList     = @(, $Aliases)
            ThrottleLimit    = $ThrottleLimit
            NewFailureObject = $NewFailure
            Cmdlet           = $PSCmdlet

        }

        If ($Credential) {

            $RemoteParameters['Credential'] = $Credential

        }

        # One summary warning at the end instead of an error per computer (DESIGN.md, section 4.6). For a single computer the
        # results are held, so exactly one is shown as a list and several as a table (section 6); fleet results still stream.
        $Tally = New-WinClatTally -ComputerName $Targets.ToArray()

        Invoke-WinClatRemote @RemoteParameters | Measure-WinClatResult -Tally $Tally
        Complete-WinClatResult -Tally $Tally

        Write-WinClatFailureSummary -Tally $Tally -Action 'queried' -Cmdlet $PSCmdlet

    }

}