The Limitations of Task Manager
When a Windows server is running slowly, the immediate instinct is to open Task Manager and look at the CPU graph.
While Task Manager is great for a quick visual check, it is fundamentally useless for long-term auditing. You cannot use Task Manager to prove that a server’s memory spiked at exactly 3:00 AM on a Sunday. You cannot write a script to automatically restart a service if the disk queue length exceeds a specific threshold.
To programmatically monitor, graph, and alert on deep system performance metrics, Windows relies on Performance Counters. In PowerShell, these counters are accessed via the Get-Counter cmdlet.
1. Auditing the Basics (CPU and RAM)
Performance Counters are organized into specific paths. The most common metrics are Processor Time and Available Memory.
To instantly check the exact percentage of CPU currently being utilized across all cores:
Get-Counter "\Processor(_Total)\% Processor Time"
To check exactly how many Megabytes of RAM are currently available before the server crashes:
Get-Counter "\Memory\Available MBytes"
The output is a structured object, not a graph. This means you can extract the exact numerical value and use it in mathematical equations within your script.
2. Continuous Sampling (The Data Logger)
Checking the CPU once is rarely helpful. If a server is freezing intermittently, you need a baseline. You need to record the CPU every 5 seconds for an entire minute to see the spikes.
You can use the -SampleInterval and -MaxSamples parameters to turn PowerShell into an automated telemetry logger.
Get-Counter "\Processor(_Total)\% Processor Time" -SampleInterval 5 -MaxSamples 12
PowerShell will sit quietly, query the CPU exactly every 5 seconds, and print 12 distinct data points to the screen (totaling 60 seconds of continuous monitoring).
3. Building Automated Alerts
Because Get-Counter outputs math, it is the perfect trigger for automated self-healing scripts.
Suppose you manage a fragile database server. If the Available Memory drops below 500 MB, the database will corrupt. You can write a script that runs constantly in the background, checking the memory, and firing an alert if it crosses the threshold.
$memoryCounter = "\Memory\Available MBytes"
$currentMemory = (Get-Counter $memoryCounter).CounterSamples.CookedValue
if ($currentMemory -lt 500) {
Write-Host "CRITICAL ALERT: Server memory critically low ($currentMemory MB)."
# Trigger an email, or restart a service here.
} else {
Write-Host "Server health normal ($currentMemory MB available)."
}
(Note: We use .CounterSamples.CookedValue to extract the pure, raw integer from the complex output object, allowing the -lt (less than) math to work perfectly).
4. Exporting to Spreadsheets (BLG to CSV)
If you are monitoring a server for an entire week, you don’t want the data printed to the screen. You want it exported to a file so you can graph it in Microsoft Excel for an executive report.
You simply pipe the continuous output of Get-Counter directly into a CSV file.
Get-Counter "\Processor(_Total)\% Processor Time" -SampleInterval 60 -MaxSamples 1440 |
Export-Counter -Path "C:\Logs\CPU_Report.csv" -FileFormat CSV
This command records the CPU every 60 seconds, 1,440 times (exactly 24 hours), and builds a pristine Excel file ready for charting.
Conclusion
The Get-Counter cmdlet is the foundation of Windows infrastructure observability. By replacing graphical monitors with scriptable, continuous data sampling, administrators can build proactive alerts and generate massive forensic reports without ever opening Task Manager.