Link to home
Start Free TrialLog in
Avatar of Varshini S
Varshini S

asked on

How to use Powershell script to find CPU utilization above threshold limit?

I am using following script to find CPU utilization is above the threshold limit.
Looks like the script is wrong. Let me know whats wrong in this script?

$cputhreshold=50

 #Collect the data from the list of servers and only include it if the CPU Utilization is above the threshold .
 $tableFragment= Get-WMIObject  -ComputerName $computers win32_processor | select __Server, @{name="CPUUtilization" ;expression ={“{0:N2}” -f (get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |  select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average).average}}| Where-Object {[decimal]$_.CPUUtilization -gt [decimal]$cputhreshold} | ConvertTo-HTML -fragment
Avatar of K B
K B
Flag of United States of America image

what is the full error?
$tableFragment | Get-Member

Open in new window


...reveals that $_.CPUUtilization is not a property
Avatar of Varshini S
Varshini S

ASKER

i do not have any error but this script does not return value as expected. I need the  CPU utilization percentage.
I am still trying to figure out how to cast to decimal in the where clause.  It still is evaluating as a string

[decimal]$cputhreshold = 50

$tableFragment = Get-WMIObject -ComputerName $computers win32_processor | 
    select __Server, @{name = "CPUUtilization" ; expression = ({"{0:N2}" -f (get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
                    select -ExpandProperty countersamples | 
                    select -ExpandProperty cookedvalue | 
                    Measure-Object -Average).average})
} | Where {($tableFragment.CPUUtilization -as [decimal]) -gt $cputhreshold}

$tableFragment  | ConvertTo-HTML -Fragment

Open in new window

Avatar of Qlemo
Why would you want to use the decimal data type? Use float or double. Whatsoever, the format operator -f in the Select expression is the culprit for having a string. Such formatting does not make sense if wanting to process data further.
I can't seem to cast it to any type it seems
K B, you need to use $_ as in the original code in where-object.
Meaning $_.CPUutilization?
Since the expression is complex, I would prefer to use a foreach loop here.

There is a big flaw in the original code - the CPU load is measured at the local machine, not remote.
Agreed. With invoke-command
I'm confused. The WMI result is not used at all but for looping thru the machines and extract __Server, which is misleading because the CPU load is local :D.
I guess we have put everything into the trash bin to restart from scratch.
Frankly I was testing with localhost thinking it was so sort of an agent or something. I lost sight of big picture.
No worries, K B, it took me several posts to see that ;-).


This is "correct" code:
$cputhreshold = 50

$computers |
% {
  $computer = $_
  Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
  Select -Expand CounterSamples |
  Select -Expand CookedValue |
  Measure-Object -Average |
  ? { $_.Average -gt $cputhreshold } |
  Select @{n='ComputerName'  ; e={$computer}},
         @{n='CPUUtilization'; e={'{0:N2}' -f $_.Average}}
}

Open in new window

Test with a $cputhreshold of 0 to get all machines. If the result is ok, add your | ConvertTo-HTML -Fragment etc. to the very end.
QLEMO: I have tested the code you provided. But i got an error when i execute this below code.

error:

Get-Counter : Unable to connect to the specified computer or the computer is offline.
At C:\WINDOWS\system32\NEW.PS1:7 char:3
+   Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -Sampl ...
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidResult: (:) [Get-Counter], Exception
    + FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.GetCounterCommand


-------------------------code---------------------------------------------------------
$computer="mypc01"
$cputhreshold = 50

$computers |
% {
  $computer = $_
  Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
  Select -Expand CounterSamples |
  Select -Expand CookedValue |
  Measure-Object -Average |
  ? { $_.Average -gt $cputhreshold } |
  Select @{n='ComputerName'  ; e={$computer}},
         @{n='CPUUtilization'; e={'{0:N2}' -f $_.Average}}
Are you able to connect to 'mypc01' via get-wmiobject?
I've tested this script prior to posting against my (local) home and (remote) work PC, which do not have the same credentials and are separated via VPN, so I'm certain it works ;-).
offline? firewall?
mypc01 is my local machine.
Give this a try


$cputhreshold = 0
$computers = "mypc01"
foreach ($computer in $computers) {
  Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
  Select -Expand CounterSamples |
  Select -Expand CookedValue |
  Measure-Object -Average |
  ? { $_.Average -gt $cputhreshold } |
  Select @{n='ComputerName'  ; e={$computer}},
         @{n='CPUUtilization'; e={'{0:N2}' -f $_.Average}}
}

Open in new window

Of course. You need to set $computers, not $computer, for testing. $computer is used as loop variable, and overwritten.
K B's code does not allow for piping the output, because the foreach statement does not, btw.
....and that is what he needs..  Qlemo how would you modify that to allow to be piped?
There are two common ways, and I showed one already - using foreach-object alias % instead.
The other approach is to encase the foreach statement in a subexpression, like $(foreach ...) | ....
Of course I prefer my way, #a42104119. For test with a single machine or a hardcoded list of machines (and a threshold of zero):
$cputhreshold = 0

"mypc01", "notmypc02" |
% {
  $computer = $_
  Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
  Select -Expand CounterSamples |
  Select -Expand CookedValue |
  Measure-Object -Average |
  ? { $_.Average -gt $cputhreshold } |
  Select @{n='ComputerName'  ; e={$computer}},
         @{n='CPUUtilization'; e={'{0:N2}' -f $_.Average}}
} | ConvertTo-HTML -fragment

Open in new window

Of course lines 1-3 could also be
$computers = "mypc01", "notmypc02"
$cputhreshold = 0

$computers |

Open in new window

Trying to wrap my head around that.. and it works perfectly.. why does one let you pipe to output and my example not (which it clearly does not)
I guess this is why?

but why lol?
localhost won't work but i just obfuscated my machine names

User generated image
Well, foreach-object is thought to process the pipeline, foreach is not. That simple it is :D.

Indeed, it is everything else than simple. PowerShell helps you a lot under the sheets, by hiding things and acting as it thinks you want to use it.
Rule of thumb: A statement never feeds anything into the pipe. You see that with if. The only reason you can see output with the foreach statement is because the output is pushed into the default output queue for the console (that is different from the pipeline!). This behaviour does not help to understand what is going on, of course ...
so is mine is using foreach-object even though I wrote foreach?  

Does it know this because I used ($computer in $computers)
Ah, I see your confusion. That is one reason why I try to never use foreach as abbreviation of foreach-object. You should use foreach for the statement only, and foreach-object or % for the "filter" cmdlet acting on a pipe.

You can't use the statement in a pipe, so "something" | foreach (...) { ... } does not use the statement but the cmdlet.
If I may ask... where is filter?
OH! so your example is the foreach-object, correct? mine is foreach?

https://blogs.technet.microsoft.com/heyscriptingguy/2014/04/29/powershell-looping-using-the-foreach-object-cmdlet/

got it .. Thank you Qlemo!  Sorry to hijack thread.
I assume this is an interesting and important exercise for many PS beginners, and not really off-topic, so it should be fine.
Along the same lines I posted this question.  I am attempting to get a better handle on ForEach-Object.

https://www.experts-exchange.com/questions/29017968/PowerShell-ForEach-Object-Export-to-CSV.html
Thank You, K B and Qlemo for your effort to provide me a good solution for my questions.
KB - I think footech clarified you regarding better handle on ForEach-Object.  Based on that should I need to modify anything in your solution? Sorry, I am a beginner in PS.

Based on your solution:
$cputhreshold = 0

"mypc01", "notmypc02" |
% {
  $computer = $_
  Get-Counter "\\$computer\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 |
  Select -Expand CounterSamples |
  Select -Expand CookedValue |
  Measure-Object -Average |
  ? { $_.Average -gt $cputhreshold } |
  Select @{n='ComputerName'  ; e={$computer}},
         @{n='CPUUtilization'; e={'{0:N2}' -f $_.Average}}
} | ConvertTo-HTML -fragment

Open in new window


i am going to take the CPU utilization value from your PS script. And if the value is more than 50% (threshold), i am going to execute below script

Get-Process | Sort CPU -descending | Select -first 10 -Property ID,ProcessName,CPU, *mem* | export-csv -NoType C:\Temp\result.csv

Open in new window

and save the value in csv file and send email with this csv file attachement.
ASKER CERTIFIED SOLUTION
Avatar of Qlemo
Qlemo
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you K B &  QLEMO  for your efforts to given good solution. This is awesome.
Qlemo should get all the points in my opinion.