powershell: cpu usage, function does not work for value 100 - powershell

I am trying to provide a short script to inform me about the CPU usage over some limet but it works only if CPU < 100% but fails if it works 100% CPU
$trashhold=90
$doit=Get-WmiObject Win32_Processor | Measure-Object -Property LoadPercentage -Average | Select Average | Out-String
if ($doit -match "\d+"){
$doit = $matches[0];
}
if ($doit -gt $trashhold)
{
send email bla bla
}
else
{
Write-Output "less than $limit, do nothing"
}
Basically regex takes number values from Get-WmiObject function and script works if cpu is 99 or less. For 100 it goes for else even it is bigger than trashhold (90). Where is the mistake?
try to catch error but withoout results (regex is returning correct numbers)

There is no need for Out-String and no need for regex either, you're just overcomplicating it:
$threshold = 90
$cpuUsage = (Get-CimInstance Win32_Processor | Measure-Object -Property LoadPercentage -Average).Average
if($cpuUsage -gt $threshold) {
# send email blah blah with `$cpuUsage`
}
else {
"Less than $threshold, do nothing"
}
What you're looking to do is to obtain the Property Value from the LoadPercentage Property, this can be done either via member-access enumeration (as shown in this answer) or using Select-Object -ExpandProperty LoadPercentage. ForEach-Object LoadPercentage would also work (see -MemberName parameter for more info). There are other methods too, though these are the most common ones.

Related

powershell winform searchbox shows results incorrect [duplicate]

I'm using Powershell to set up IIS bindings on a web server, and having a problem with the following code:
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if ($serverIps.length -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
$primaryIp = $serverIps[0]
$secondaryIp = $serverIps[1]
If there's 2+ IPs on the server, fine - Powershell returns an array, and I can query the array length and extract the first and second addresses just fine.
Problem is - if there's only one IP, Powershell doesn't return a one-element array, it returns the IP address (as a string, like "192.168.0.100") - the string has a .length property, it's greater than 1, so the test passes, and I end up with the first two characters in the string, instead of the first two IP addresses in the collection.
How can I either force Powershell to return a one-element collection, or alternatively determine whether the returned "thing" is an object rather than a collection?
Define the variable as an array in one of two ways...
Wrap your piped commands in parentheses with an # at the beginning:
$serverIps = #(gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort)
Specify the data type of the variable as an array:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
Or, check the data type of the variable...
IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
Force the result to an Array so you could have a Count property. Single objects (scalar) do not have a Count property. Strings have a length property so you might get false results, use the Count property:
if (#($serverIps).Count -le 1)...
By the way, instead of using a wildcard that can also match strings, use the -as operator:
[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}
You can either add a comma(,) before return list like return ,$list or cast it [Array] or [YourType[]] at where you tend to use the list.
If you declare the variable as an array ahead of time, you can add elements to it - even if it is just one...
This should work...
$serverIps = #()
gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort | ForEach-Object{$serverIps += $_}
You can use Measure-Object to get the actual object count, without resorting to an object's Count property.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
if (($serverIps | Measure).Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
Return as a referenced object, so it never converted while passing.
return #{ Value = #("single data") }
I had this problem passing an array to an Azure deployment template. If there was one object, PowerShell "converted" it to a string. In the example below, $a is returned from a function that gets VM objected according to the value of a tag. I pass the $a to the New-AzureRmResourceGroupDeployment cmdlet by wrapping it in #(). Like so:
$TemplateParameterObject=#{
VMObject=#($a)
}
New-AzureRmResourceGroupDeployment -ResourceGroupName $RG -Name "TestVmByRole" -Mode Incremental -DeploymentDebugLogLevel All -TemplateFile $templatePath -TemplateParameterObject $TemplateParameterObject -verbose
VMObject is one of the template's parameters.
Might not be the most technical / robust way to do it, but it's enough for Azure.
Update
Well the above did work. I've tried all the above and some, but the only way I have managed to pass $vmObject as an array, compatible with the deployment template, with one element is as follows (I expect MS have been playing again (this was a report and fixed bug in 2015)):
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
foreach($vmObject in $vmObjects)
{
#$vmTemplateObject = $vmObject
$asJson = (ConvertTo-Json -InputObject $vmObject -Depth 10 -Verbose) #-replace '\s',''
$DeserializedJson = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property #{MaxJsonLength=67108864}).DeserializeObject($asJson)
}
$vmObjects is the output of Get-AzureRmVM.
I pass $DeserializedJson to the deployment template' parameter (of type array).
For reference, the lovely error New-AzureRmResourceGroupDeployment throws is
"The template output '{output_name}' is not valid: The language expression property 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Expression.Expressions.JTokenExpression'
can't be evaluated.."
There is a way to deal with your situation. Leave most of you code as-is, just change the way to deal with the $serverIps object. This code can deal with $null, only one item, and many items.
$serverIps = gwmi Win32_NetworkAdapterConfiguration
| Where { $_.IPAddress }
| Select -Expand IPAddress
| Where { $_ -like '*.*.*.*' }
| Sort
# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}
# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
# The $serverIps could be $null. Even $null can loop once.
# So we need to skip the $null condition.
if ($_ -ne $null) {
# Get the index of the array.
# The #($serverIps) make sure it must be an array.
$idx = #($serverIps).IndexOf($item)
if ($idx -eq 0) { $primaryIp = $_ }
if ($idx -eq 1) { $secondaryIp = $_ }
}
}
In PowerShell Core, there is a .Count property exists on every objects. In Windows PowerShell, there are "almost" every object has an .Count property.

How can I use powershell to group process names and show the sum of memory used

I am trying to wrap my head around combining powershell options in order to produce a simple table of the top 10 memory users on my system (server, pc, etc). My PC is Windows 7 with no timeline in site for upgrade to Windows 10 due to Covid 19. I cannot add applications to my work PC that has not gone through a vetting process (read, it takes forever) so most of the time I create my own.
I would like to produce a result that looks something like this:
Count Name Memory Sum in MB
10 Firefox 5000
3 javaw 1000
The order I would like to be able to select by changing a property in the powershell options. So for example, sort by count, name or memory. My sample table is not set in stone.
I have come across the following 2 pieces of powershell and have been trying to adapt them but get errors.
(Get-Process | Measure-Object WorkingSet -sum).sum /1gb
Get-Process | Group-Object -Property Name -NoElement | Where-Object {$_.Count -gt 1}
For sake of learning, I don't mind seeing an "ugly" version and an optimized version.
You can use this:
$proc=ps|select -eXp name;$proc2=#()
$proc|%{
if(!("$($_)" -in $proc2)){$proc2+="$($_)"
$mem=0;ps $_|select -eXp workingSet|%{$mem+=$_/1MB}
[pscustomobject][ordered]#{
'Count'=(ps $_ -ea silentlyContinue).Count
'Name'=$_
'Memory in MB'=$mem
}}}
The PSCustomObject accelerator was introduced in PowerShell v3 so I don't know if the the output looks like a table in Windows 7 however the following pipeline returns desired properties even in PowerShell v2:
Get-Process |
Group-Object -Property Name -NoElement |
Where-Object { $_.Count -gt 1 } |
ForEach-Object {
[PSCustomObject]#{
Count= $_.Count
Name = $_.Name
'Memory Sum in MB' = [math]::Round(( Get-Process -Name $_.Name |
Measure-Object WorkingSet -sum).sum /1Mb, 3)
}
} # | Sort-Object -Property 'Memory Sum in MB'

How to get max CPU percentage from from 5 trials

I am new to Powershell and struggling with syntax.
I want to write a script which gives me max CPU usage by a process out of 5 attempts.
$properties=#(
#{Name="Process Name"; Expression = {$_.name}},
#{Name="CPU (%)"; Expression = {$_.PercentProcessorTime}},
#{Name="Memory (MB)"; Expression = {[Math]::Round(($_.workingSetPrivate / 1mb),2)}}
)
Get-WmiObject -class Win32_PerfFormattedData_PerfProc_Process | Select-Object $properties
I have to run the above process 5 times and pick the top process which has max CPU usage.
This should get you what you want (remember to also include your definition of $properties):
1 .. 5 |
ForEach-Object {
Get-WmiObject -class Win32_PerfFormattedData_PerfProc_Process
} | Where-Object Name -notin '_Total','Idle' |
Sort-Object -Property 'PercentProcessorTime' -Descending |
Select-Object -First 1 -Property $properties
1 .. 5 is the range operator, which generates the set of numbers 1,2,3,4,5. This is just a quick hack to run ForEach-Object 5 times.
Where-Object Name -notin '_Total','Idle' excludes some 'processes' that always have high values but are unlikely to be what you're looking for. Generally it is more efficient to update the call to Get-WmiObject to exclude these at that stage, but for clarity I went with this technique.
Sort-Object -Property 'PercentProcessorTime' -Descending takes all of the readings and sorts them in order from largest CPU value to lowest.
Select-Object -First 1 -Property $properties Selects just the first object in the sorted list (i.e. the one with the highest value). Note that it is better to do this last and not after each call to Get-WmiObject as it creates a new custom object for each WMI one returned, almost all of which we discard further along the line - it is more efficient to do this 'duplication' for only the final object we select.

Powershell Process CPU checking

Have the following which works OK, but with an in issue in PowerShell:
$FileName = "E:\Work\ps\Inventory.htm"
$serverlist = "E:\Work\ps\Monitored_computers.txt"
foreach ($server in Get-Content $serverlist)
{
$servern=$server.split(",")[0]
$ip=$server.split(",")[1]
$cpu = gwmi Win32_PerfFormattedData_PerfProc_Process -Computer $servern -filter "Name <> '_Total' and Name <> 'Idle'" | Sort-Object PercentProcessorTime -Descending | where { $_.PercentProcessorTime -gt 0 }| select -First 1
if ($cpu.PercentProcessorTime -ge "92") {
write-host $servern ' ' $cpu.Name ' ' $cpu.PercentProcessorTime
}
}
I have seen some other code in PowerShell, that takes an Average but almost seems like an "average of an average" - which is meaningless. And, this is for overall CPU Usage
gwmi win32_processor | Measure-Object -property LoadPercentage -Average | Foreach {$_.Average}
Now, if we can take the same logic and apply for our process issue:
gwmi Win32_PerfFormattedData_PerfProc_Process | Sort-Object PercentProcessorTime -Descending | where { $_.PercentProcessorTime -gt 0 } | select -First 1 | Measure-Object -property PercentProcessorTime -Average | Foreach {$_.PercentProcessorTime}
What am trying to ask is: I do get the CPU Percentage, which seems to be a "point in time". How do locate the true CPU Percentage? This is why I am pointing out the average. I really want to get around the "point in time" part of this.
The point being, when we have seen on several occasions, a high CPU per process on a server, we login to the server and the high CPU has subsided. This is not to say, this has been each time, but we know that sometimes a CPU will spike and then quiet down.
Thanks for any insight!
First issue, you are stuck at a Point In Time because when you execute your script it captures a snapshot of what is happening right then and there. What you are looking for is historical data, so you can figure out the average CPU usage of processes over a set amount of time, and pinpoint the process that's bogging down your CPU. Do you have performance monitors in place to track CPU usage for individual processes? You may need to setup performance logging if you want to be able to get the numbers you're looking for after the fact.
Secondly, I think that you misunderstand how Measure-Object works. If you run Get-Help on the cmdlet and check the Output you'll see that it outputs a GenericMeasureInfo object. This object will have a property for the relevant stat that you are looking for, which in your case is the Average property. It is not an average of an average, the most common usage I see for it is to calculate something, like a Sum or Average, and then output the value of that property.
Let's try a simple example...
Find the average size of the files in a folder. First we use Get-ChildItem to get a collection of files, and pipe it to Measure-Object. We will specify the -Average argument to specify that we want the Average calculated, and -Property length, so that it knows what to average:
GCI C:\Temp\* -file | Measure-Object -Average -Property length
This outputs a GenericMeasureInfo object like this:
Count : 30
Average : 55453155
Sum :
Maximum :
Minimum :
Property : Length
That lets me know that it had 30 files piped to it, and it found the Average for the Length property. Now, sometime you want to calculate more than one thing, so you can use more than one argument, such as -Sum and -Maximum, and those values will be populated as well:
Count : 30
Average : 55453155
Sum : 1663594650
Maximum : 965376000
Minimum :
Property : Length
So it looks like my average file is ~55MB, but out of the 1.6GB in the whole folder I've got one file that's 965MB! That file is undoubtedly skewing my numbers. With that output I could find folders that have multiple files, but one file is taking up over half of the space for the folder, and find anomalies... such as the ISO that I have saved to my C:\temp folder for some reason. Looks like I need to do some file maintenance.
Thanks to #TheMadTechnician I have been able to sort this out. I had a wrong component with
$_.Average
where I had
$_.PercentProcessorTime
and that would never work. Here is the correct script:
$serverlist = "D:\Work\ps\Monitored_computers.txt"
foreach ($server in Get-Content $serverlist) {
$servern=$server.split(",")[0]
$ip=$server.split(",")[1]
$cpu = gwmi Win32_PerfFormattedData_PerfProc_Process -Computer $ip | `
Where-Object {$_.Name -like "*tomcat*"} | `
Measure-Object -property PercentProcessorTime -Average | `
Foreach {$_.Average}
if ($cpu -ge "20") {
write-host $servern $cpu ' has a tomcat process greater than 20'
}
}

Change powershell script to output without ellipses (...)

I need some help with the output of the following script so the output doesn't show with the ellipses (...).
I tried to insert | Format-Table -Wrap -AutoSize but I just can't seem to get it right.
clear-host Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue
$services = new-object system.collections.sortedlist
$servers = (get-spfarm).servers
foreach ($server in $servers) {
foreach($service in $server.serviceinstances)
{
if ($service.status = "Online")
{
$s = $service.typename
if ($services.contains($s))
{
$serverlist = $services[$s]
$servername = $server.name
$services[$s] = "$serverlist - $servername"
}
else
{
$services[$s] = $server.name
}
}
} }
$services
output:
Name Value
---- -----
Access Database Service SE5APP - SE5FE - SE7FE - FAQ3
Application Discovery **and L...** SE5APP - SE5FE - SE7FE - FAQ3
Application Registry Service SE5APP - SE5FE - SE7FE - FAQ3
Either Format-List (fl) or Format-Table -auto (ft -auto) should help here.
$services | fl
OR
$services | ft -auto
I came across this post and would like to add some information, as the accepted solution did not resolve my problem and I'm sure others may find the following information useful:
Quick Story: Running commands using Microsoft Online Services Module with Powershell, much of the results were continually be retrieved as truncated with data cutoff and missing as an ellipsis (...).
The fix: As explained in this post by Greig, I inevitably came to the conclusion $FormatEnumerationLimit=-1 is the unlimate solution to the problem. Using any variant of Format-Wide, Format-List, Format-Table, Format-Custom, -AutoSize, Out-String -Width, etc. require a hefty amount of additional considerations/code. In the case where all you want is to see all the data being returned, regardless of columns, arrays, etc., $FormatEnumerationLimit=-1 ensures you will get everything and you don't need to mess around.
Additional information, as credited in Greig's post include:
PowerShell Quick Tip: Creating wide tables with PowerShell, where the author explains:
If you have a specific property that contains a collection of items,
that property may still show an ellipsis in the file produced here if
the number of items in that collection exceeds the number assigned to
the built-in $FormatEnumerationLimit variable.
...and that "passing the results to | Format-Table -Property * [will] show all of the columns." But content from the columns may still be truncated ("PowerShell truncates table output by default"), and that even using | Format-Table -Property * -AutoSize will be limited by your screen buffer
("Auto-sized tables are limited to the width of your screen buffer"). The solution offered, before the absolute $FormatEnumerationLimit=-1, seems to be using | Format-Table -Property * -AutoSize in conjunction with | Out-String -Width 4096 or whatever width you require.
Using Format Commands to Change Output View provides some more delailed documentation on the Format cmdlets: Format-Wide, Format-List, and Format-Table.
What I do in this situation is to create a format description then use that as an argument to my Format-Table command. I've developed a function (Get-MaxLength) to examine the data field with the longest data (helps to have this at the end of the format description) and set the width in the format description with the value it returns. You can see the calculations in the code below. Notice the Number value for the Intel(4) Management Engine Interface. Also notice the use of -Wrap on the Format-Table command. This concept can be modified to calculate all fields widths or just the last one, it's just a little math.
Function Get-MaxLength {
<#
.SYNOPSIS
Finds the length of the longest item in collection.
.DESCRIPTION
Use this Function to get the length of the longest item in a
collection for use in format strings or other places where
needed.
.PARAMETER TestObj
The qualified object to be tested. See example!
.Parameter MinLen
The minimum length of the item (if using for formatting) which
should be the Label (title) length. Note if the object item
being tested does not have a Length property you MUST specify
the label length!
.OUTPUTS
Returns a numerical value
.EXAMPLE
$NameLen = Get-MaxLength -TestObj $DotNet.PSChildName
$VerLen = Get-MaxLength -TestObj $DotNet.Version
$RNLen = Get-MaxLength -TestObj $DotNet.Release -MinLen 11
#--- .Net Information ---
$fmtDotNet =
#{Expression={$_.PSChildName};Label=".Net Type";Width=$NameLen},
#{Expression={$_.Version};Label="Version No:";Width=$VerLen},
#{Expression={$_.Release};Label="Release No:";Width=$RNLen}
$Dotnet | Format-Table $fmtDotNet
#>
Param(
[Parameter(Mandatory=$True)]
[object] $TestObj,
[Parameter(Mandatory=$False)]
[int] $MinLen = 0,
[Parameter(Mandatory=$False)]
[int] $MaxLen = 0
)
$ErrorActionPreference = "SilentlyContinue"
foreach ($x in $TestObj) {
If ($x.Trim().length -gt $MinLen) {
$MinLen = $x.Trim().length
}
}
If ($MaxLen -ne 0) {
If ($MinLen -gt $MaxLen) {
$MinLen = $MaxLen
}
}
$ErrorActionPreference = "Continue"
Return ,$MinLen
} #End Function ----------- Get-MaxLength -------------------
$OstrWidth = 80
$DriverInfo =
Get-CimInstance -ClassName 'Win32_PNPSignedDriver' |
Where-Object -Property DriverProviderName -ne "Microsoft" |
Where-Object -Property DeviceName -ne -Value $Null |
Sort-Object -Property DeviceName -Unique
$DriverCnt = $DriverInfo.Count
$DVLen =
Get-MaxLength -TestObj $DriverInfo.DriverVersion -MinLen 14
$DDLen = $OstrWidth - $DVLen
$fmtDRVR = #{Label="`nDriver Description";Width=$DDLen;
Expression={$_.DeviceName}},
#{Label="Version Number"; Width=$DVLen;
Expression={$_.DriverVersion}}
$DrvTitle = "$($DriverCnt) Non-Windows Unique Drivers and " +
"Version Numbers:" | Out-String
$DriverInfo =
$DriverInfo | Format-Table -Property $fmtDRVR -Wrap |
Out-String -Width $OStrWidth
Sample Output:
Driver Description Number
------------------- -------------
Alcor Micro USB 2.0 Card Reader 2.0.150.10135
ASMedia USB3.1 eXtensible Host Controller 1.16.42.1
...
Intel(R) HD Graphics 630 21.20.16.4550
Intel(R) Management Engine Interface 1914.12.0.125
6
Intel(R) Ready Mode Technology Device 1.2.0.0
...
Realtek Audio 6.0.1.8248
Samsung NVMe Controller 3.0.0.1802