Monitoring with PowerShell: Monitoring Windows Performance Index

First things are first: I just got awarded the new and prestigious Azure Hero award. Im an Azure Content Hero, of which only 250 are awarded. I am super thankful and very excited for this as its a great recognition for my work on this blog. I really want to thank the people that nominated me.

So now that we have that out of the way, lets get to scripting. This time we’re doing some workstation experience monitoring, I like monitoring the actual user experience on machines. This gives us a change to actively improve the lives of users that are working on slow machines by preventively replacing them, or by notifying them we’re seeing performance problems.

The Windows Experience Index was introduced in Windows 7 as a method to gauge how well your computer is running both hardware and software wise. I like to run this script every couple of hours to grab the most recent result of ‘winsat’ test. This shows if a machine is slowing down etc. We also decided that any machine that scores lower than a 6.5 is going to be replaced, because be honest; who would want to work on a slow machine right?

The script

So this one is pretty straight forward, set your “failing score” and let it run. The script prints everything to the console as well, in case you want to run it on demand instead of via your RMM system.

$FailingThreshold = 6.5

$WinSatResults = Get-CimInstance Win32_WinSAT | Select-Object CPUScore, DiskScore, GraphicsScore, MemoryScore, WinSPRLevel

$WinSatHealth = foreach ($Result in $WinSatResults) {
    if ($Result.CPUScore -lt $FailingThreshold) { "CPU Score is $($result.CPUScore). This is less than $FailingThreshold" }
    if ($Result.DiskScore -lt $FailingThreshold) { "Disk Score is $($result.Diskscore). This is less than $FailingThreshold" }
    if ($Result.GraphicsScore -lt $FailingThreshold) { "Graphics Score is $($result.GraphicsScore). This is less than $FailingThreshold" }
    if ($Result.MemoryScore -lt $FailingThreshold) { "RAM Score is $($result.MemoryScore). This is less than $FailingThreshold" }
    if ($Result.WinSPRLevel -lt $FailingThreshold) { "Average WinSPR Score is $($result.winsprlevel). This is less than $FailingThreshold" }
if (!$WinSatHealth) {
    $AllResults = ($Winsatresults | out-string)
    $WinSatHealth = "Healthy. $AllResults"

And that’s it! short and sweet, and it allows for a bit more user experience monitoring. As always, Happy PowerShelling!


  1. Michael McCool May 5, 2020 at 11:56 pm

    I ran into a machine that had an invalid WinSAT state and added a small addition to the script to check for any systems which doesn’t show a good state for the WinSAT data. I put this check near the top of the script. I set it to exit the script with a failure errorlevel.
    # Check to make sure there is a vald WinSat assessment available.
    # “1” indicates a valid assesment is available, any other value shows issues with WinSAT on the system.
    $WinSatStatus= (Get-CimInstance Win32_WinSAT).WinSATAssessmentState
    if ($WinSatStatus -ne “1”) {
    Write-Output “WinSAT has not run or contains invalid data. No score is available for this device.”
    exit 1

  2. Pingback: Monitoring with PowerShell: user experience issues & Unifi EOL Monitoring - CyberDrain

  3. Sam November 4, 2020 at 11:59 am

    Hey Kelvin

    Weird but when I run this via RMM (syncro) the -LT comparison is providing me the opposite of what it should (eg 9 -lt 6 is coming back true)

    When I test locally on my machine its ok. Is there anything to try such as forcing/converting to int or similar?


    1. Sam November 6, 2020 at 6:01 pm

      Ignore! I did a brain fart ty

  4. Ted April 2, 2021 at 7:47 pm

    Thanks, Kelvin! I’ve found your scripts extremely useful and have started incorporating some of them as CW Automate remote monitors. To make some of your more lengthy scripts one-liners I’ve been base64 encoding them and paste it in a remote EXE process monitor like so:

    “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe” -NoProfile -EncodedCommand base64encodedstringhere

    I usually have to re-arrange/add some things like $ProgressPreference = “SilentlyContinue” at the start of the script and $WinSatHealth in the last If statement to get a “Healthy” output. I set my monitor result field to: CONTAINS Healthy. At least for this example 🙂

    Not sure if this is the easiest way to go about it, but scheduling scripts to run against groups is so lame to me. Hopefully this helps someone else that uses Automate!

Leave a comment

Your email address will not be published.

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