Testing Network Bandwidth with PowerShell and iPerf

One of the first steps to diagnose network issues, especially nowadays when so many people are working from home, is to run a speed test. Testing speed by going visiting one of the many speed tests sites is useful, but what if you want to automate the process of testing speed? What if you have an RMM that enables you to run scripts directly from the RMM console, like Atera?

Enter iPerf, a super handy throughput testing tool to run bandwidth tests from command line. I like iPerf over some other tools, because it is entirely run from a single EXE. No need for Python or anything else installed to be able to run it.

Once you download and unpack the iPerf binary, it's easy to run a test. Just open command prompt, navigate to where the iPerf binary is and run it against a iPerf server. Since it can be tricky to get access to the public servers listed on the iPerf website, I spun up my own server in Azure to test against:

> iperf3.exe --client iperf.cageops.com --port 5210
Connecting to host iperf.cageops.com, port 5210
[  4] local 192.168.86.115 port 31659 connected to 20.51.231.242 port 5210
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.01   sec   512 KBytes  4.17 Mbits/sec
[  4]   1.01-2.02   sec   256 KBytes  2.08 Mbits/sec
[  4]   2.02-3.00   sec   512 KBytes  4.25 Mbits/sec
[  4]   3.00-4.02   sec   640 KBytes  5.17 Mbits/sec
[  4]   4.02-5.02   sec   768 KBytes  6.26 Mbits/sec
[  4]   5.02-6.01   sec   896 KBytes  7.44 Mbits/sec
[  4]   6.01-7.01   sec   512 KBytes  4.21 Mbits/sec
[  4]   7.01-8.01   sec   512 KBytes  4.19 Mbits/sec
[  4]   8.01-9.00   sec   768 KBytes  6.32 Mbits/sec
[  4]   9.00-10.01  sec   768 KBytes  6.22 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.01  sec  6.00 MBytes  5.03 Mbits/sec                  sender
[  4]   0.00-10.01  sec  5.88 MBytes  4.93 Mbits/sec                  receiver

iperf Done.

The output of iPerf is pretty easy top understand. The default options run for 10 seconds and gives you the running average every second. At the end, you can see the final result. The confusing part that I ran into initially was that I expected it to give me both my download and upload speed. Running the default options effectively shows you your upload speed.

To get an accurate download speed, you just need to add the --reverse option, which sends data from the server to your PC:

> iperf3.exe --client iperf.cageops.com --port 5210 --reverse
Connecting to host iperf.cageops.com, port 5210
Reverse mode, remote host iperf.cageops.com is sending
[  4] local 192.168.86.115 port 31689 connected to 20.51.231.242 port 5210
[ ID] Interval           Transfer     Bandwidth
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00   sec  14.6 MBytes  61.1 Mbits/sec    0             sender
[  4]   0.00-10.00   sec  12.8 MBytes  53.5 Mbits/sec                  receiver

This is closer to what I expect, but still not quite right. The last thing I figured out with iPerf is that unlike most web browsers, iPerf defaults to run single threaded, meaning that it is not utilizing all the benefits of our modern fancy CPUs.

Lucky for us, all we have to do is add the --parallel N option with however many threads we want to run. I've found 10 threads to be a nice amount that doesn't overwhelm the CPU:

iperf3 --client iperf.cageops.com --port 5210 --reverse --parallel 10
...
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  41.1 MBytes  34.4 Mbits/sec    1             sender
[  4]   0.00-10.00  sec  38.8 MBytes  32.5 Mbits/sec                  receiver
[  6]   0.00-10.00  sec  35.5 MBytes  29.8 Mbits/sec    0             sender
[  6]   0.00-10.00  sec  33.4 MBytes  28.0 Mbits/sec                  receiver
[  8]   0.00-10.00  sec  37.0 MBytes  31.0 Mbits/sec   19             sender
[  8]   0.00-10.00  sec  34.8 MBytes  29.2 Mbits/sec                  receiver
[ 10]   0.00-10.00  sec  36.6 MBytes  30.7 Mbits/sec   23             sender
[ 10]   0.00-10.00  sec  34.4 MBytes  28.8 Mbits/sec                  receiver
[ 12]   0.00-10.00  sec  39.8 MBytes  33.4 Mbits/sec    1             sender
[ 12]   0.00-10.00  sec  37.5 MBytes  31.5 Mbits/sec                  receiver
[ 14]   0.00-10.00  sec  36.7 MBytes  30.8 Mbits/sec   25             sender
[ 14]   0.00-10.00  sec  34.5 MBytes  29.0 Mbits/sec                  receiver
[ 16]   0.00-10.00  sec  40.5 MBytes  33.9 Mbits/sec    0             sender
[ 16]   0.00-10.00  sec  38.2 MBytes  32.0 Mbits/sec                  receiver
[ 18]   0.00-10.00  sec  35.3 MBytes  29.6 Mbits/sec    0             sender
[ 18]   0.00-10.00  sec  34.3 MBytes  28.8 Mbits/sec                  receiver
[ 20]   0.00-10.00  sec  37.6 MBytes  31.5 Mbits/sec    0             sender
[ 20]   0.00-10.00  sec  35.4 MBytes  29.7 Mbits/sec                  receiver
[ 22]   0.00-10.00  sec  39.7 MBytes  33.3 Mbits/sec    0             sender
[ 22]   0.00-10.00  sec  37.7 MBytes  31.6 Mbits/sec                  receiver
[SUM]   0.00-10.00  sec   380 MBytes   318 Mbits/sec   69             sender
[SUM]   0.00-10.00  sec   359 MBytes   301 Mbits/sec                  receiver

Whoa! That's a lot more output. The output now shows the total of each thread and then the sum of all of the threads at the bottom. ~300Mbps is what I expect to see with my ISP.

So now for a bonus script that you can run from your RMM tool of choice to get your download and upload speed without ever having to actually remote into the end PC.

###
# Author: Dave Long <dlong@cagedata.com>
# Gets an bandwidth test using iPerf3. Sum is the download speed of computer.
###

$iPerfDownload = "https://iperf.fr/download/windows/iperf-3.1.3-win64.zip"
$DownloadLocation = Join-Path $env:TEMP "iperf.zip"
$iPerfPath = Join-Path $env:TEMP "iperf"

if (!(Test-Path $iPerfPath)) {
  Invoke-WebRequest -Uri $iPerfDownload -OutFile $DownloadLocation
  Expand-Archive -Path $DownloadLocation -DestinationPath $iPerfPath
}
Set-Location (Join-Path $iPerfPath "iperf-3.1.3-win64")

$Download = & .\iperf3.exe --client iperf.cageops.com --port 5210 --parallel 10 --reverse
if (($Download | Select-Object -Last 1) -eq "iperf Done.") {
  Write-Host "Download Speed"
  $Download | Select-Object -Last 4 | Select-Object -First 2 | Write-Host
} else {
  Write-Host "iPerf failed to get download speed."
}
$Upload = & .\iperf3.exe --client iperf.cageops.com --port 5210 --parallel 10
if (($Upload | Select-Object -Last 1) -eq "iperf Done.") {
  Write-Host "Upload Speed"
  $Upload | Select-Object -Last 4 | Select-Object -First 2 | Write-Host
} else {
  Write-Host "iPerf failed to get upload speed."
}

Stay tuned for my next post on how to test network speeds across a local network by running the iPerf server locally.