How to Set Up Windows Server 2019 Network Monitoring

Comprehensive network monitoring on Windows Server 2019 provides visibility into bandwidth utilization, connectivity health, latency, packet loss, and network service availability. Windows Server 2019 includes built-in tools including Performance Monitor, Network Monitor (via download), and the Network Diagnostics Framework, which can be extended with third-party agents. This guide covers building a network monitoring solution using native Windows tools, PowerShell, and open-source integrations.

Using Performance Monitor for Network Metrics

Performance Monitor (perfmon) provides real-time and logged metrics for all network interfaces. Key counters for network monitoring include bytes per second, packets per second, and error rates:

# Create a performance counter data collector set via PowerShell
$datacollector = New-Object -ComObject Pla.DataCollectorSet
$datacollector.DisplayName = "Network Performance Baseline"
$datacollector.Duration = 3600  # 1 hour

# Add performance counters
$counters = @(
    "Network Interface(*)Bytes Total/sec",
    "Network Interface(*)Bytes Received/sec",
    "Network Interface(*)Bytes Sent/sec",
    "Network Interface(*)Packets Received Errors",
    "Network Interface(*)Packets Outbound Errors",
    "Network Interface(*)Output Queue Length",
    "Network Interface(*)Current Bandwidth",
    "TCPv4Connections Established",
    "TCPv4Connection Failures",
    "TCPv4Connections Reset"
)

$pdc = $datacollector.DataCollectors.CreateDataCollector(0)  # 0 = PerformanceCounter
$pdc.PerformanceCounters = $counters
$pdc.SampleInterval = 30  # seconds
$pdc.LogAppend = $true
$pdc.FileName = "NetworkPerf"
$pdc.FileNameFormat = 1  # yyyyMMdd

$datacollector.DataCollectors.Add($pdc)
$datacollector.Commit("NetworkMonitor", $null, 3)
$datacollector.Start()

Monitoring Network Interfaces with PowerShell

# Get all network adapter statistics
Get-NetAdapterStatistics | Select-Object Name, ReceivedBytes, SentBytes, ReceivedUnicastPackets, SentUnicastPackets

# Monitor bandwidth utilization in real-time
while ($true) {
    $adapters = Get-NetAdapterStatistics
    $timestamp = Get-Date -Format "HH:mm:ss"
    foreach ($adapter in $adapters) {
        if ($adapter.ReceivedBytes -gt 0) {
            Write-Host "$timestamp | $($adapter.Name) | RX: $([math]::Round($adapter.ReceivedBytes/1MB,2)) MB | TX: $([math]::Round($adapter.SentBytes/1MB,2)) MB"
        }
    }
    Start-Sleep -Seconds 5
}

Configuring SNMP for Network Monitoring Integration

SNMP allows external monitoring systems to poll Windows Server 2019 for network metrics. Install and configure the SNMP service:

# Install SNMP feature
Install-WindowsFeature -Name SNMP-Service, SNMP-WMI-Provider -IncludeManagementTools

# Configure SNMP community string and security via registry
$snmpPath = "HKLM:SYSTEMCurrentControlSetServicesSNMPParameters"

# Set accepted community names (read-only)
$communityPath = "$snmpPathValidCommunities"
New-Item -Path $communityPath -Force
New-ItemProperty -Path $communityPath -Name "monitoring-community" -Value 4 -PropertyType DWord -Force
# Value 4 = READ ONLY

# Set permitted managers (restrict which IPs can query SNMP)
$managersPath = "$snmpPathPermittedManagers"
New-Item -Path $managersPath -Force
New-ItemProperty -Path $managersPath -Name "1" -Value "192.168.1.50" -PropertyType String -Force
New-ItemProperty -Path $managersPath -Name "2" -Value "192.168.1.51" -PropertyType String -Force

# Configure contact and location
Set-ItemProperty -Path $snmpPath -Name "RFC1156AgentsysContact" -Value "[email protected]"
Set-ItemProperty -Path $snmpPath -Name "RFC1156AgentsysLocation" -Value "DataCenter-Rack-A1"

# Restart SNMP service
Restart-Service -Name SNMP

Setting Up Network Connection Monitoring with WMI

# Monitor active TCP connections
Get-NetTCPConnection | Where-Object { $_.State -eq "Established" } |
  Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, State, OwningProcess |
  Sort-Object OwningProcess | Format-Table -AutoSize

# Monitor DNS resolution performance
function Test-DNSResolution {
    param([string[]]$Hostnames, [string]$DNSServer = "8.8.8.8")
    foreach ($host in $Hostnames) {
        $start = Get-Date
        try {
            $result = Resolve-DnsName -Name $host -Server $DNSServer -ErrorAction Stop
            $elapsed = ((Get-Date) - $start).TotalMilliseconds
            [PSCustomObject]@{
                Hostname = $host
                Resolved = $result.IPAddress -join ", "
                LatencyMs = [math]::Round($elapsed, 2)
                Status = "OK"
            }
        } catch {
            [PSCustomObject]@{
                Hostname = $host
                Resolved = "FAILED"
                LatencyMs = -1
                Status = $_.Exception.Message
            }
        }
    }
}

Test-DNSResolution -Hostnames @("dc01.domain.local","google.com","microsoft.com")

Configuring Windows Firewall Logging for Network Monitoring

# Enable Windows Firewall logging
Set-NetFirewallProfile -Profile Domain,Private,Public `
  -LogFileName "C:WindowsSystem32LogFilesFirewallpfirewall.log" `
  -LogMaxSizeKilobytes 32768 `
  -LogAllowed True `
  -LogBlocked True

# Parse firewall log for blocked connections
$logPath = "C:WindowsSystem32LogFilesFirewallpfirewall.log"
Get-Content $logPath | Where-Object { $_ -match "^[^#]" -and $_ -match "DROP" } |
  ForEach-Object {
    $fields = $_ -split " "
    [PSCustomObject]@{
        Date = $fields[0]
        Time = $fields[1]
        Action = $fields[2]
        Protocol = $fields[3]
        SrcIP = $fields[4]
        DstIP = $fields[5]
        SrcPort = $fields[6]
        DstPort = $fields[7]
    }
  } | Select-Object -Last 50 | Format-Table -AutoSize

Automated Network Health Check Script

# Comprehensive network health check
$gateways = Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Select-Object -ExpandProperty NextHop
$dnsServers = (Get-DnsClientServerAddress -AddressFamily IPv4).ServerAddresses | Select-Object -Unique

Write-Output "=== Network Health Check: $(Get-Date) ==="

# Test gateway connectivity
foreach ($gw in $gateways) {
    $ping = Test-Connection -ComputerName $gw -Count 3 -ErrorAction SilentlyContinue
    $avg = ($ping.ResponseTime | Measure-Object -Average).Average
    Write-Output "Gateway $gw : Avg RTT $([math]::Round($avg,1))ms"
}

# Test DNS servers
foreach ($dns in $dnsServers) {
    $test = Test-Connection -ComputerName $dns -Count 2 -ErrorAction SilentlyContinue
    Write-Output "DNS $dns : $(if($test){'Reachable'}else{'UNREACHABLE'})"
}

# Interface utilization
Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | ForEach-Object {
    $stats = $_ | Get-NetAdapterStatistics
    Write-Output "Adapter: $($_.Name) | Speed: $($_.LinkSpeed) | RX Err: $($stats.ReceivedDiscardedPackets) | TX Err: $($stats.OutboundDiscardedPackets)"
}

Schedule this health check script to run every 15 minutes via Task Scheduler and email results to the operations team when thresholds are breached. Combine with Event Log monitoring — specifically Event ID 4227 (TCP connection table exhaustion) and 4231 (port exhaustion) — to detect network performance degradation before it becomes a user-impacting incident.