PowerShell Network Bandwidth Usage Report with WMI

PowerShell Network Bandwidth Usage Report with WMI

PowerShell Tips Editor 4 min read
PowerShell Network Bandwidth Usage Report with WMI

Task Manager shows you a rough bandwidth total, but it cannot tell you which adapter is saturated on a multi-NIC server, or give you per-second averages over a meaningful sample window. Windows Performance Counters expose precise bytes-sent and bytes-received counters for every network interface, and PowerShell’s Get-Counter cmdlet can collect and average those counters across any number of remote servers. This guide builds a reusable bandwidth report you can run on-demand or feed into a scheduled monitoring script.

Quick Answer

Use Get-Counter with the \Network Interface(*)\Bytes Total/sec counter set, sample it multiple times, average the results, and convert to Mbps. Enumerate adapter names with Get-NetAdapter to build the correct counter paths, then export results to CSV.

Reading Network Interface Counters with Get-Counter

Performance counter paths for network interfaces follow the pattern \Network Interface(adapter name)\Bytes Total/sec. Adapter names in counter paths often differ from the friendly names shown in Device Manager — they use the description string from the driver and may include parentheses or extra characters. The safest way to get valid paths is to call Get-Counter -ListSet "Network Interface" and inspect the PathsWithInstances property.

# Discover all valid network counter paths on the local machine
$counterSet = Get-Counter -ListSet "Network Interface"
$counterSet.PathsWithInstances | Where-Object { $_ -like "*Bytes Total*" }
\\server01\Network Interface(Intel[R] Ethernet Connection I217-LM)\Bytes Total/sec
\\server01\Network Interface(Hyper-V Virtual Ethernet Adapter)\Bytes Total/sec

Those exact strings — spaces, brackets, and all — are what you pass to Get-Counter. Constructing them manually from Get-NetAdapter display names will fail for adapters with special characters in their description.

Capturing Bytes Sent and Received per Second

For directional reporting you need the Bytes Sent/sec and Bytes Received/sec counters rather than Bytes Total/sec. Collect both in a single Get-Counter call to keep the timestamps aligned.

$paths = (Get-Counter -ListSet "Network Interface").PathsWithInstances |
    Where-Object { $_ -like "*Bytes Sent*" -or $_ -like "*Bytes Received*" }

# Collect 5 samples, 2 seconds apart
$samples = Get-Counter -Counter $paths -SampleInterval 2 -MaxSamples 5

Each element of $samples.CounterSamples has a CookedValue property representing bytes per second at that instant.

Averaging Over a Sample Window for Accuracy

A single sample can spike due to a burst of traffic. Averaging across a window of 5–10 samples gives a representative utilization figure. Group samples by counter path, then average the CookedValue for each group.

$report = $samples.CounterSamples |
    Group-Object Path |
    ForEach-Object {
        $avg = ($_.Group | Measure-Object CookedValue -Average).Average
        [PSCustomObject]@{
            Adapter  = ($_.Name -replace '.*\((.+)\)\\.*','$1')
            Counter  = ($_.Name -replace '.*\)\\','')
            Avg_Mbps = [Math]::Round($avg * 8 / 1MB, 3)
        }
    }

$report | Sort-Object Adapter, Counter | Format-Table -AutoSize
Adapter                               Counter            Avg_Mbps
-------                               -------            --------
Intel[R] Ethernet Connection I217-LM  Bytes Received/sec    45.21
Intel[R] Ethernet Connection I217-LM  Bytes Sent/sec         3.87
Hyper-V Virtual Ethernet Adapter      Bytes Received/sec     0.12
Hyper-V Virtual Ethernet Adapter      Bytes Sent/sec         0.09

Enumerating All Adapters with Get-NetAdapter

Get-NetAdapter provides the link speed for each adapter, which lets you calculate utilization as a percentage of available bandwidth — far more useful than raw Mbps when comparing a 1 Gbps and a 10 Gbps NIC on the same server. Join the report to adapter data on the adapter description or interface index.

Running on Multiple Remote Servers

Wrap the counter collection in Invoke-Command to gather data from several servers simultaneously. Because Get-Counter runs locally on the target, the samples reflect that machine’s actual counters without WMI overhead.

$servers = "srv-web01", "srv-db01", "srv-app01"

$allResults = Invoke-Command -ComputerName $servers -ScriptBlock {
    $paths = (Get-Counter -ListSet "Network Interface").PathsWithInstances |
        Where-Object { $_ -like "*Bytes Total/sec*" }
    $s = Get-Counter -Counter $paths -SampleInterval 2 -MaxSamples 5
    $s.CounterSamples |
        Group-Object Path |
        ForEach-Object {
            $avg = ($_.Group | Measure-Object CookedValue -Average).Average
            [PSCustomObject]@{
                Server   = $env:COMPUTERNAME
                Adapter  = ($_.Name -replace '.*\((.+)\)\\.*','$1')
                Avg_Mbps = [Math]::Round($avg * 8 / 1MB, 3)
            }
        }
}

Exporting Bandwidth Utilization to CSV

Append a datestamp to the filename so you can build a historical archive of bandwidth reports without overwriting previous runs.

$timestamp = Get-Date -Format "yyyyMMdd-HHmm"
$allResults | Select-Object Server, Adapter, Avg_Mbps |
    Export-Csv "F:\reports\bandwidth-$timestamp.csv" -NoTypeInformation
Write-Output "Saved $($allResults.Count) rows to bandwidth-$timestamp.csv"

Common Errors

  • Counter path has spaces in the adapter name: Constructing counter paths manually by concatenating strings from Get-NetAdapter often produces paths that Get-Counter cannot resolve. Always retrieve paths from (Get-Counter -ListSet "Network Interface").PathsWithInstances to get verbatim-correct strings.
  • Get-Counter returns $null on disabled adapters: Disabled or disconnected adapters are registered in the counter set but have no active counters. Filter by checking Get-NetAdapter | Where-Object { $_.Status -eq "Up" } and cross-reference against discovered paths before sampling.

Related Cmdlets / See Also

Wrapping Up

Windows Performance Counters provide the most accurate, low-overhead source of network utilization data available. By combining Get-Counter with multi-sample averaging and remote execution you can build bandwidth reports across your entire server fleet in under a minute, with results precise enough to guide hardware upgrade decisions.

Send-Item -To