When a VM is pinned at 100% CPU, RDP and SSH often time out. Run Command goes through the VM agent instead, so it still works.
Windows: top processes
az vm run-command invoke -g rg-prod -n vm-app-01 --command-id RunPowerShellScript --scripts '
$cores = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors
Get-Counter "\Process(*)\% Processor Time" -SampleInterval 2 -MaxSamples 2 |
Select-Object -ExpandProperty CounterSamples |
Where-Object { $_.InstanceName -notin "_total","idle" } |
Sort-Object CookedValue -Descending | Select-Object -First 10 InstanceName,
@{n="CPU%";e={[math]::Round($_.CookedValue / $cores, 1)}}
' --query "value[0].message" -o tsv
To find which service a svchost or w3wp is running:
Get-CimInstance Win32_Process -Filter "Name='w3wp.exe'" | Select-Object ProcessId, CommandLine # shows the app pool
tasklist /svc /fi "imagename eq svchost.exe"
Linux: top processes
az vm run-command invoke -g rg-prod -n vm-app-02 --command-id RunShellScript \
--scripts "top -b -n 1 -o %CPU | head -20; echo; uptime" --query "value[0].message" -o tsv
For per-thread detail, or for processes in containers: ps -eLo pid,tid,pcpu,comm --sort=-pcpu | head.
Usual suspects
| Process | Common cause |
|---|---|
MsMpEng.exe / AV agent | Scanning a busy data or log folder. Add exclusions as approved by security. |
TiWorker.exe, TrustedInstaller | Windows Update servicing. Usually temporary. |
w3wp.exe, java, dotnet | An application hot path, or a runaway loop. Take a dump or profile. |
sqlservr.exe | Expensive queries. Use Query Store or DMVs. |
kworker, high %wa | Not really CPU: the system is waiting on disk IO |
Check the history
Was this a spike or a trend? With VM insights:
InsightsMetrics
| where TimeGenerated > ago(7d) and Computer == "vm-app-01"
| where Namespace == "Processor" and Name == "UtilizationPercentage"
| summarize avg(Val), max(Val) by bin(TimeGenerated, 1h)
| render timechart
Fix options
- Kill or restart the offending process or service, after capturing evidence (a dump with
procdump -ma <pid>on Windows,gcoreorjstackon Linux). - Resize the VM if the load is legitimate:
az vm resize -g rg-prod -n vm-app-01 --size Standard_D8s_v5 # restarts the VM - On B-series VMs, check
CPU Credits Remaining. When credits run out, the VM is capped at its baseline and looks "pinned".
Tip
Add a CPU alert with dynamic thresholds so it learns the normal daily pattern and only fires on unusual load.