Public/Get-LissWindowsNetworkBaseline.ps1
|
function Get-LissWindowsNetworkBaseline { <# .SYNOPSIS Collects a read-only Windows endpoint network baseline. .DESCRIPTION Collects system, adapter, addressing, routing, driver, offload, performance, connectivity, event, WLAN, and WinHTTP proxy evidence. Each optional source has Status, Error, and Data fields so an unavailable cmdlet, class, or log does not fail the complete baseline. The command makes no configuration changes. .PARAMETER Target One or more configurable ping and TCP connectivity targets. .PARAMETER TcpPort TCP port tested against each target. .PARAMETER PingCount Number of ICMP samples per target. .PARAMETER TimeoutMilliseconds Timeout for each ping or TCP connection attempt. .PARAMETER IncludeRouteDiscovery Includes route discovery through Test-NetConnection when that cmdlet is available. .PARAMETER EventLookbackHours Network event lookback duration. .PARAMETER MaxEvents Maximum number of recent network events. .EXAMPLE Get-LissWindowsNetworkBaseline -Target 1.1.1.1,8.8.8.8 -TcpPort 443 .EXAMPLE Get-LissWindowsNetworkBaseline -Target gateway.example.net -IncludeRouteDiscovery #> [CmdletBinding()] [OutputType('LISSTech.WindowsNetworkDiagnostics.Baseline')] param( [ValidateNotNullOrEmpty()][ValidateCount(1, 16)][string[]]$Target = @('1.1.1.1', '8.8.8.8'), [ValidateRange(1, 65535)][int]$TcpPort = 443, [ValidateRange(1, 20)][int]$PingCount = 4, [ValidateRange(100, 10000)][int]$TimeoutMilliseconds = 1000, [switch]$IncludeRouteDiscovery, [ValidateRange(1, 720)][int]$EventLookbackHours = 24, [ValidateRange(1, 1000)][int]$MaxEvents = 100 ) # Local copies make the intended closure inputs explicit for optional source isolation. $targets = @($Target) $connectivityPort = $TcpPort $connectivityPingCount = $PingCount $connectivityTimeout = $TimeoutMilliseconds $eventHours = $EventLookbackHours $eventLimit = $MaxEvents $time = Get-LissTimeContext $system = Invoke-LissDataSource { $operatingSystem = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop [pscustomobject]@{ ComputerName = $env:COMPUTERNAME Caption = $operatingSystem.Caption Version = $operatingSystem.Version BuildNumber = $operatingSystem.BuildNumber BootTime = $operatingSystem.LastBootUpTime TimeZoneId = $time.TimeZoneId UtcOffsetMinutes = $time.UtcOffsetMinutes } } $adapters = Invoke-LissDataSource { if (-not (Get-Command Get-NetAdapter -ErrorAction SilentlyContinue)) { throw 'Get-NetAdapter is unavailable.' } Get-NetAdapter -IncludeHidden -ErrorAction Stop | Select-Object Name, InterfaceDescription, InterfaceIndex, Status, MediaConnectionState, MacAddress, LinkSpeed, MediaType, PhysicalMediaType, DriverInformation, DriverFileName, DriverVersion, DriverDate } $ipConfiguration = Invoke-LissDataSource { if (-not (Get-Command Get-NetIPConfiguration -ErrorAction SilentlyContinue)) { throw 'Get-NetIPConfiguration is unavailable.' } Get-NetIPConfiguration -All -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ InterfaceAlias = $_.InterfaceAlias InterfaceIndex = $_.InterfaceIndex NetProfileName = $_.NetProfile.Name NetProfileCategory = $_.NetProfile.NetworkCategory IPv4Address = @($_.IPv4Address | Select-Object IPAddress, PrefixLength) IPv6Address = @($_.IPv6Address | Select-Object IPAddress, PrefixLength) IPv4DefaultGateway = @($_.IPv4DefaultGateway | Select-Object NextHop, RouteMetric) IPv6DefaultGateway = @($_.IPv6DefaultGateway | Select-Object NextHop, RouteMetric) DnsServer = @($_.DNSServer.ServerAddresses) } } } $routes = Invoke-LissDataSource { if (-not (Get-Command Get-NetRoute -ErrorAction SilentlyContinue)) { throw 'Get-NetRoute is unavailable.' } Get-NetRoute -AddressFamily IPv4, IPv6 -ErrorAction Stop | Select-Object AddressFamily, DestinationPrefix, NextHop, InterfaceAlias, InterfaceIndex, RouteMetric, ifMetric, Protocol, State, Store | Sort-Object AddressFamily, DestinationPrefix, RouteMetric } $interfaceMetrics = Invoke-LissDataSource { if (-not (Get-Command Get-NetIPInterface -ErrorAction SilentlyContinue)) { throw 'Get-NetIPInterface is unavailable.' } Get-NetIPInterface -AddressFamily IPv4, IPv6 -ErrorAction Stop | Select-Object InterfaceAlias, InterfaceIndex, AddressFamily, ConnectionState, NlMtu, AutomaticMetric, InterfaceMetric, Dhcp, RouterDiscovery } $adapterStatistics = Invoke-LissDataSource { Get-LissAdapterStatisticsSnapshot } $drivers = Invoke-LissDataSource { $networkAdapters = @(Get-CimInstance -ClassName Win32_NetworkAdapter -ErrorAction Stop) $signedDrivers = @(Get-CimInstance -ClassName Win32_PnPSignedDriver -Filter "DeviceClass = 'NET'" -ErrorAction Stop) foreach ($adapter in $networkAdapters) { $driver = $signedDrivers | Where-Object { $_.DeviceID -eq $adapter.PNPDeviceID } | Select-Object -First 1 if ($driver) { [pscustomobject]@{ Name = $adapter.NetConnectionID DeviceName = $driver.DeviceName DeviceId = $driver.DeviceID Manufacturer = $adapter.Manufacturer DriverProvider = $driver.DriverProviderName DriverVersion = $driver.DriverVersion DriverDate = $driver.DriverDate InfName = $driver.InfName } } } } $advancedProperties = Invoke-LissDataSource { if (-not (Get-Command Get-NetAdapterAdvancedProperty -ErrorAction SilentlyContinue)) { throw 'Get-NetAdapterAdvancedProperty is unavailable.' } Get-NetAdapterAdvancedProperty -AllProperties -ErrorAction Stop | Where-Object { $_.RegistryKeyword -match '(?i)(speed|duplex|flow|rss|rsc|lso|checksum)' } | Select-Object Name, InterfaceDescription, RegistryKeyword, RegistryValue, DisplayName, DisplayValue } $offloadState = Invoke-LissDataSource { $result = New-Object 'Collections.Generic.List[object]' foreach ($entry in @( @{ Name = 'RSS'; Command = 'Get-NetAdapterRss' } @{ Name = 'RSC'; Command = 'Get-NetAdapterRsc' } @{ Name = 'LSO'; Command = 'Get-NetAdapterLso' } @{ Name = 'ChecksumOffload'; Command = 'Get-NetAdapterChecksumOffload' } )) { $command = Get-Command $entry.Command -ErrorAction SilentlyContinue if ($command) { try { $data = @(& $entry.Command -ErrorAction Stop) $result.Add([pscustomobject]@{ Feature = $entry.Name; Status = 'Available'; Error = $null; Data = $data }) } catch { $result.Add([pscustomobject]@{ Feature = $entry.Name; Status = 'Unavailable'; Error = $_.Exception.Message; Data = @() }) } } else { $result.Add([pscustomobject]@{ Feature = $entry.Name; Status = 'Unavailable'; Error = "$($entry.Command) is unavailable."; Data = @() }) } } $result } $performance = Invoke-LissDataSource { foreach ($className in @('Win32_PerfFormattedData_Tcpip_TCPv4', 'Win32_PerfFormattedData_Tcpip_TCPv6', 'Win32_PerfFormattedData_Tcpip_NetworkInterface')) { try { [pscustomobject]@{ ClassName = $className Status = 'Available' Error = $null Data = @(Get-CimInstance -ClassName $className -ErrorAction Stop) } } catch { [pscustomobject]@{ ClassName = $className Status = 'Unavailable' Error = $_.Exception.Message Data = @() } } } } $connectivity = Invoke-LissDataSource { foreach ($targetName in $targets) { $samples = for ($index = 0; $index -lt $connectivityPingCount; $index++) { Invoke-LissPingSample -Target $targetName -TimeoutMilliseconds $connectivityTimeout } [pscustomobject]@{ Target = $targetName Ping = Get-LissLatencySummary -Sample @($samples) -Target $targetName PingSamples = @($samples) Tcp = Test-LissTcpPort -Target $targetName -Port $connectivityPort -TimeoutMilliseconds $connectivityTimeout } } } $routeDiscovery = if ($IncludeRouteDiscovery) { Invoke-LissDataSource { if (-not (Get-Command Test-NetConnection -ErrorAction SilentlyContinue)) { throw 'Test-NetConnection is unavailable.' } foreach ($targetName in $targets) { Test-NetConnection -ComputerName $targetName -TraceRoute -Hops 30 -WarningAction SilentlyContinue -ErrorAction Stop | Select-Object ComputerName, RemoteAddress, TraceRoute } } } else { [pscustomobject]@{ Status = 'NotRequested'; Error = $null; Data = @() } } $events = Invoke-LissDataSource { Get-LissWindowsNetworkEvent -LookbackHours $eventHours -MaxEvents $eventLimit } $wlan = Invoke-LissDataSource { if (-not (Get-Command Get-NetAdapter -ErrorAction SilentlyContinue)) { throw 'Get-NetAdapter is unavailable.' } Get-NetAdapter -IncludeHidden -ErrorAction Stop | Where-Object { [string]$_.PhysicalMediaType -match '(?i)(802\.11|wireless)' -or [string]$_.NdisPhysicalMedium -match '(?i)(802\.11|wireless)' } | Select-Object Name, InterfaceDescription, InterfaceIndex, Status, MediaConnectionState, LinkSpeed, MacAddress, MediaType, PhysicalMediaType } $winHttpProxy = Invoke-LissDataSource { $output = @(& "$env:SystemRoot\System32\netsh.exe" winhttp show proxy 2>&1 | ForEach-Object { [string]$_ }) if ($LASTEXITCODE -ne 0) { throw "netsh winhttp show proxy failed with exit code $LASTEXITCODE." } [pscustomobject]@{ CommandOutput = $output } } [pscustomobject]@{ PSTypeName = 'LISSTech.WindowsNetworkDiagnostics.Baseline' ComputerName = $env:COMPUTERNAME CollectedAt = $time.Timestamp CollectedAtUtc = $time.TimestampUtc TimeZoneId = $time.TimeZoneId System = $system Adapters = $adapters IPConfiguration = $ipConfiguration Routes = $routes InterfaceMetrics = $interfaceMetrics AdapterStatistics = $adapterStatistics Drivers = $drivers AdvancedProperties = $advancedProperties OffloadState = $offloadState Performance = $performance Connectivity = $connectivity RouteDiscovery = $routeDiscovery Events = $events Wlan = $wlan WinHttpProxy = $winHttpProxy } } |