Use PowerShell to Monitor CPU and Memory Usage

This blog discusses how we can use PowerShell to monitor CPU and memory usage.

I was having a few issues recently on a web server whereby the server was becoming unresponsive due to either high CPU or memory usage. It was difficult to debug since I could no longer remote desktop (RDP) onto it, or even VNC on to it.

When i restarted the server and checked the event logs, I couldn’t find anything of interest. So I decided to knock up a quick PowerShell script to monitor for high CPU or memory usage. If it detects this, it will dump all the current processes out to a local log file with their CPU/memory utilisation. This way I could check the log file when I regained access, and check what was hogging all the resources.

I configured it to run every day using a scheduled task with the action of:

powershell.exe -ExecutionPolicy Bypass -File [PathToFile]

All you have to do is change $logFile to write to a log file of your choice, and $availablePercentageThreshold to a threshold value of your choice. I’ve set it to 20, since I want to receive an alert (and process dump) if CPU or memory availability drops below 20% utilisation.

There is a section where you can add your own email notification code if required. For this, you can read how to send emails using PowerShell.

cls
$logFile = "C:\Alkane\CPUMemory.log"
#if only this percentage of CPU/Memory OR LESS is available, dump process information to log and do something
$availablePercentageThreshold = 20
function Write-Log {
[CmdletBinding()]
param(
[string]$message
)
try {
"$((Get-Date).tostring('dd/MM/yyyy HH:mm:ss')) - $message" | Out-File $logFile -Append
} catch {}
}
#CPU availability
$cpuTime = (Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples.CookedValue
$availCPUPercentage = (100-$cpuTime)
$availCPUPercentageOutput = $availCPUPercentage.ToString("#,0.000") + "%"
#Mem availability
$totalRam = (Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).Sum
$availMem = (Get-Counter '\Memory\Available MBytes').CounterSamples.CookedValue
$availMemMB = $availMem.ToString('N0') + "MB"
$availMemPercentage = (104857600 * ($availMem/$totalRam))
$availMemPercentageOutput = $availMemPercentage.ToString('#,0.0') + "%"
Write-Log "Available CPU: $availCPUPercentageOutput" 
Write-Log "Available Memory: $availMemMB ($availMemPercentageOutput)"
if ($availCPUPercentage -lt $availablePercentageThreshold -or $availMemPercentage -lt $availablePercentageThreshold) {
Write-Log "Dumping Processes..."
get-process -IncludeUserName | Sort-Object -Property WorkingSet | foreach { 
try {  
Write-Log "************************************"
Write-Log "ID: $($_.ID)"
Write-Log "Process Name: $($_.processname)"
Write-Log "Path: $($_.path)"
Write-Log "UserName: $($_.username)"
Write-Log "CPU: $($_.CPU)"
Write-Log "Start Time: $($_.StartTime.tostring('dd/MM/yyyy HH:mm:ss'))"
Write-Log "Handles: $($_.Handles)"
Write-Log "Working Set Memory: $($_.WorkingSet)"
Write-Log "PeakPaged Memory Size: $($_.PeakPagedMemorySize)"
Write-Log "Private Memory Size: $($_.PrivateMemorySize)"
Write-Log "Virtual Memory Size: $($_.VirtualMemorySize)"
} catch {}
}
#Maybe send an email alert
}
Write-Log "Finished."