Monitoring with PowerShell: Monitoring internet speeds

It seems like I’m having a week of requests. This one was requested by my friends at Datto. One of their clients wanted to have the ability to run speed-tests and have their RMM system generate alerts whenever the speed drops. I’ve made the following PowerShell script that uses the CLI utility from speedtest.net. This utility gives us some nice feedback to work with;

  • It returns the external IP & the internal IP for the interface used.
  • The current ISP.
  • The download and upload speed.
  • The Jitter,Latency, and packet loss of the connection.
  • and the server it uses, plus the actual speedtest.net URL so you can compare the results.

So, I’ve made the script use two different monitoring methods; one is absolute and based and the values you’ve entered. The other is a percentage based monitor that alerts if the difference between the current speedtest and the previous one is more than 20%.

The script

The script downloads the Speedtest utility from the speedtest website. You can always replace this URL with your own host if you’d like.

######### Absolute monitoring values ########## 
$maxpacketloss = 2 #how much % packetloss until we alert. 
$MinimumDownloadSpeed = 100 #What is the minimum expected download speed in Mbit/ps
$MinimumUploadSpeed = 20 #What is the minimum expected upload speed in Mbit/ps
######### End absolute monitoring values ######

#Replace the Download URL to where you've uploaded the ZIP file yourself. We will only download this file once. 
#Latest version can be found at: https://www.speedtest.net/nl/apps/cli
$DownloadURL = "https://bintray.com/ookla/download/download_file?file_path=ookla-speedtest-1.0.0-win64.zip"
$DownloadLocation = "$($Env:ProgramData)\SpeedtestCLI"
try {
    $TestDownloadLocation = Test-Path $DownloadLocation
    if (!$TestDownloadLocation) {
        new-item $DownloadLocation -ItemType Directory -force
        Invoke-WebRequest -Uri $DownloadURL -OutFile "$($DownloadLocation)\speedtest.zip"
        Expand-Archive "$($DownloadLocation)\speedtest.zip" -DestinationPath $DownloadLocation -Force
    } 
}
catch {  
    write-host "The download and extraction of SpeedtestCLI failed. Error: $($_.Exception.Message)"
    exit 1
}
$PreviousResults = if (test-path "$($DownloadLocation)\LastResults.txt") { get-content "$($DownloadLocation)\LastResults.txt" | ConvertFrom-Json }
$SpeedtestResults = & "$($DownloadLocation)\speedtest.exe" --format=json --accept-license --accept-gdpr
$SpeedtestResults | Out-File "$($DownloadLocation)\LastResults.txt" -Force
$SpeedtestResults = $SpeedtestResults | ConvertFrom-Json

#creating object
[PSCustomObject]$SpeedtestObj = @{
    downloadspeed = [math]::Round($SpeedtestResults.download.bandwidth / 1000000 * 8, 2)
    uploadspeed   = [math]::Round($SpeedtestResults.upload.bandwidth / 1000000 * 8, 2)
    packetloss    = [math]::Round($SpeedtestResults.packetLoss)
    isp           = $SpeedtestResults.isp
    ExternalIP    = $SpeedtestResults.interface.externalIp
    InternalIP    = $SpeedtestResults.interface.internalIp
    UsedServer    = $SpeedtestResults.server.host
    ResultsURL    = $SpeedtestResults.result.url
    Jitter        = [math]::Round($SpeedtestResults.ping.jitter)
    Latency       = [math]::Round($SpeedtestResults.ping.latency)
}
$SpeedtestHealth = @()
#Comparing against previous result. Alerting is download or upload differs more than 20%.
if ($PreviousResults) {
    if ($PreviousResults.download.bandwidth / $SpeedtestResults.download.bandwidth * 100 -le 80) { $SpeedtestHealth += "Download speed difference is more than 20%" }
    if ($PreviousResults.upload.bandwidth / $SpeedtestResults.upload.bandwidth * 100 -le 80) { $SpeedtestHealth += "Upload speed difference is more than 20%" }
}

#Comparing against preset variables.
if ($SpeedtestObj.downloadspeed -lt $MinimumDownloadSpeed) { $SpeedtestHealth += "Download speed is lower than $MinimumDownloadSpeed Mbit/ps" }
if ($SpeedtestObj.uploadspeed -lt $MinimumUploadSpeed) { $SpeedtestHealth += "Upload speed is lower than $MinimumUploadSpeed Mbit/ps" }
if ($SpeedtestObj.packetloss -gt $MaxPacketLoss) { $SpeedtestHealth += "Packetloss is higher than $maxpacketloss%" }

if (!$SpeedtestHealth) {
    $SpeedtestHealth = "Healthy"
}

And that is it! So this monitoring component will be uploaded to the ComStore soon. As always, Happy PowerShelling!

28 Comments

  1. Justin M Monk February 17, 2020 at 12:28 am

    At line:24 char:66
    + … ts = & “$($DownloadLocation)\speedtest.exe” –format=json –accep …
    + ~~~~~~~~~~~
    Unexpected token ‘format=json’ in expression or statement.
    At line:24 char:27
    + … SpeedtestResults = & “$($DownloadLocation)\speedtest.exe” –forma …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The ‘–‘ operator works only on variables or on properties.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedToken

    1. Kelvin Tegelaar February 17, 2020 at 7:49 am

      It seems my wordpress code plugin messed up some of the code. I’ve fixed it and you can try again. Sorry about that! 🙂

  2. Jeremiah February 17, 2020 at 2:27 am

    So i’m getting

    + … ts = & “$($DownloadLocation)\speedtest.exe” –format=json –accep …
    + ~~~~~~~~~~~
    Unexpected token ‘format=json’ in expression or statement.

    and

    + … SpeedtestResults = & “$($DownloadLocation)\speedtest.exe” –forma …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The ‘–‘ operator works only on variables or on properties.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnexpectedToken

    What am I missing?

    1. Kelvin Tegelaar February 17, 2020 at 7:49 am

      It seems my wordpress code plugin messed up some of the code. I’ve fixed it and you can try again. Sorry about that! 🙂

  3. Alberto Laudadio February 17, 2020 at 3:04 am

    Hi Kelvin
    I am new with scripts in Powershell and I was trying your script, but it failed.
    I suppose that the & text in the line 24 is a typo and I deleted.
    Then I got an error in the same line: The ‘–‘ operator works only on variables or on properties.
    So I change de ” to the end of the line.
    But then the two “ConvertFrom-Json” commands failed: “Invalid JSON primitive: C.”
    Thanks in advance

    1. Kelvin Tegelaar February 17, 2020 at 7:49 am

      It seems my wordpress code plugin messed up some of the code. I’ve fixed it and you can try again. Sorry about that! 🙂

  4. Tony February 17, 2020 at 9:12 pm

    Can you provide the updated script that you fixed for others in your post

    1. Kelvin Tegelaar February 17, 2020 at 9:35 pm

      Hi Tony, The post above has been updated. The previous issue was that wordpress changed line 24. Line 24 should be “$SpeedtestResults = & “$($DownloadLocation)\speedtest.exe” –format=json –accept-license –accept-gdpr”. it seems OK to me now, are you having issues?

  5. deeepeee February 23, 2020 at 11:08 am

    I seem to be missing something. I have copied the code into a ps1 script and run it and it just gives a blank screen?

    1. Kelvin Tegelaar February 23, 2020 at 12:58 pm

      Its meant to be picked up by your RMM system, but you can edit the script and add “$SpeedtestHealth” as the very last line.

  6. DerekinCA February 27, 2020 at 1:28 am

    Hello, this looks great but I am receiving this error when I try to run this script. Can you tell what might be causing this? Thank you.

    speedtest.exe : [2020-02-26 16:17:43.827] [error] Configuration – SSL connect error (UnknownException)
    At line:24 char:21
    + … stResults = & “$($DownloadLocation)\speedtest.exe” –format=json –ac …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: ([2020-02-26 16:…knownException):String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

    [2020-02-26 16:17:43.827] [error] Configuration – Cannot retrieve configuration document (0)
    [2020-02-26 16:17:43.827] [error]
    ConfigurationError – Could not retrieve or read configuration (Configuration)
    [
    2020-02-26 16:17:43.827] [error] ConfigurationError – Could not retrieve or read configuration (Configuration)
    {“type”:”log”,”timestamp”:”2020-02-27T00:17:43Z”,”message”:”Configuration – Could not retrieve or read configuration (ConfigurationError)”,”level”:”error”}

    1. Kelvin Tegelaar February 27, 2020 at 8:00 am

      These errors come from the server you’re trying to connect to. Most likely you are either outbound blocking specific ports or you’re connecting through a proxy. You can try running the script again or selecting your own server.

  7. Bill Shannon April 6, 2020 at 9:31 pm

    I’m wondering if there is a way to have the details of the report pumped into an email at the end of the script? I’ve tried assigning the $SpeedtestObj object to the $body of an email, but the resulting email shows “System.Collections.Hashtable” instead of the desired output that I get when put “$SpeedtestObj” at the end of the script.

    1. Kelvin Tegelaar April 7, 2020 at 8:28 am

      Hi Bill, Try setting your body to: ($SpeedtestObj | out-string) if you want it to send just the text version. You could also convert to it HTML with ($Speedtestobj | convertto-html -frag)

  8. Daniel Curry April 17, 2020 at 11:14 pm

    Thank you for the speed test tool. It is almost what I need. Then again, I’m not sure what I need is possible.

    What I need is a way to capture the test results so I can demonstrate to the ISP that they are NOT delivering the speeds being paid for. I am using Autotask RMM to run this for a client who is having issues with their ISP.

    1. Kelvin Tegelaar April 17, 2020 at 11:42 pm

      If you set the minimum and maximum speeds to what you expect to get, you can show them exactly at which moments you got alerted that they got under that speed.

    2. Daniel Curry June 29, 2020 at 7:11 pm

      Thanks.

      I added:

      “$(Get-Date -Format s): $($SpeedtestObj.downloadspeed) , $($SpeedtestObj.uploadspeed)” >> results.txt

      as the last line of the script. This will give me a simple cummulative log that I can graph and use to demosntrate the performance issues we have at this client location.

  9. Kyle King May 20, 2020 at 7:00 pm

    I got the following results when running through Syncro RMM:

    Directory: C:\ProgramData

    Mode LastWriteTime Length Name
    —- ————- —— —-
    d—– 5/20/2020 11:55 AM SpeedtestCLI
    error> ==============================================================================
    error>
    error> You may only use this Speedtest software and information generated
    error> from it for personal, non-commercial use, through a command line
    error> interface on a personal computer. Your use of this software is subject
    error> to the End User License Agreement, Terms of Use and Privacy Policy at
    error> these URLs:
    error>
    error> https://www.speedtest.net/about/eula
    error> https://www.speedtest.net/about/terms
    error> https://www.speedtest.net/about/privacy
    error>
    error> ==============================================================================
    error>
    error> License acceptance recorded. Continuing.
    error>

    Anyone else getting this? Did they updated the speedtest program maybe?

    1. Kelvin Tegelaar May 20, 2020 at 7:25 pm

      This is expect behavior – The actual content is stored in the variable $speedtestresult and $speedtest health. you’ll have to send those to your RMM alert.

  10. Kyle King May 20, 2020 at 9:03 pm

    Please disregard my last comment – I ran it again and it worked fine

    1. parth kumar February 10, 2021 at 8:08 am

      how did it result as mine error is same as it shown to you

  11. Daniel Curry June 29, 2020 at 5:33 pm

    Hello, again.

    I’m having a slight issue with this program at one particular network.

    This tool is showing their network speed to average between 100 and 200 Mb/s download speed. But they have 1Gb speed from Comcast/XFinity. Manually testing on the website shows an average of 900Mb/s, which is acceptable.

    Is there a switch I need to use?

    Thanks

    Daniel

  12. Peter Kovacs July 22, 2020 at 10:15 pm

    Hi Kelvin!

    Thanks for the code! I was facing a problem where something (most likely utorrent) caused my download speed to drop from time to time (although there were no active connections in utorrent at that time) significantly, but after a reboot, it got back to normal. So each time I experienced this, I restarted my network adapter and the issue was gone for a few days. I was wondering how should I schedule the restart-networkadapter commandlet to not be intrusive, but still effective when I had the idea if there was a ps alternative to speedtest.net. And voila, google got me to your page. So, I’ll schedule the speedtest to each hour and if it detects bandwidth loss, it will simply restart the network adapter. 🙂

  13. Pingback: Powershell – Use Speedtest CLI to Monitor Broadband speeds – IHNI.UK – A Techs notes…

  14. Gian April 27, 2021 at 11:43 pm

    This is what I was looking for! Thanks, Kelvin! Now I will just make some adjustments to have this in an automated job to keep this information saved in a database. Thanks!

  15. Shane September 25, 2021 at 1:38 pm

    Trying script and get the following error too

    & : The term ‘C:\ProgramData\SpeedtestCLI\speedtest.exe’ is not recognized as the name of a cmdlet, function, script file, or operable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:23
    + $SpeedtestResults = & “$($DownloadLocation)\speedtest.exe” –format=j …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (C:\ProgramData\…I\speedtest.exe:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  16. Pete November 10, 2021 at 7:24 pm

    Bummer. The download URL is no longer active.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.