How to Monitor Hyper-V with Performance Counters on Windows Server 2025
Effective Hyper-V monitoring is the foundation of a stable virtualisation environment. Windows Server 2025 exposes a rich set of Hyper-V-specific performance counters through Performance Monitor (perfmon), PowerShell’s Get-Counter cmdlet, WMI/CIM, and the open-source Prometheus exporter windows_exporter. These counters give you deep visibility into hypervisor CPU usage, virtual machine health, virtual disk throughput, and network bus activity — allowing you to detect resource contention, right-size VMs, and establish performance baselines before problems occur. This tutorial covers the most important Hyper-V counter categories, shows how to collect them with PowerShell, explains how to build Data Collector Sets for continuous baseline monitoring, and demonstrates how to integrate Hyper-V metrics with Prometheus and Grafana.
Prerequisites
- Windows Server 2025 with the Hyper-V role installed.
- PowerShell running as Administrator on the Hyper-V host.
- One or more running virtual machines for testing counter output.
- Performance Monitor (perfmon.exe) available (built into Windows Server).
- Optional: Prometheus and Grafana for long-term metric storage and dashboards.
- Optional:
windows_exporterbinary for Prometheus integration.
Step 1: Understand Key Hyper-V Performance Counter Categories
Hyper-V installs several dedicated performance counter categories on the host. The most important for daily operations are:
- Hyper-V Hypervisor Logical Processor — Measures physical CPU consumption by the hypervisor itself and by guest VPs (virtual processors).
- Hyper-V Virtual Machine Health Summary — Reports the count of VMs in a Critical or OK health state.
- Hyper-V Virtual Machine Bus — Measures VMBus channel throughput between the hypervisor and guest VMs.
- Hyper-V Virtual Storage Device — Tracks read/write IOPS and bytes per second for each virtual disk.
- Hyper-V Hypervisor Virtual Processor — Per-VM virtual CPU metrics including guest run time and hypervisor overhead.
- Hyper-V Network Adapter — Per-VM virtual NIC throughput and packet rates.
Step 2: Query Hyper-V Counters with Get-Counter
PowerShell’s Get-Counter cmdlet is the most scriptable way to retrieve performance counter values. The following examples target the most operationally useful Hyper-V counters.
# Hypervisor logical processor total run time (% of time the logical processor is busy)
Get-Counter -Counter "Hyper-V Hypervisor Logical Processor(_Total)% Total Run Time"
# Guest run time per logical processor (time spent executing guest code)
Get-Counter -Counter "Hyper-V Hypervisor Logical Processor(_Total)% Guest Run Time"
# Hypervisor run time (overhead — should be low, typically under 10%)
Get-Counter -Counter "Hyper-V Hypervisor Logical Processor(_Total)% Hypervisor Run Time"
# VM health summary — count of VMs in Critical state (non-zero means investigate)
Get-Counter -Counter "Hyper-V Virtual Machine Health SummaryHealth Critical"
# Count of VMs in OK/healthy state
Get-Counter -Counter "Hyper-V Virtual Machine Health SummaryHealth Ok"
# VMBus bytes per second for all channels (network + storage aggregate)
Get-Counter -Counter "Hyper-V Virtual Machine Bus(*)Bytes/sec"
# Filter to a specific VM by name
Get-Counter -Counter "Hyper-V Virtual Machine Bus(MyVM)Bytes/sec"
# Virtual storage device read and write throughput (all disks)
Get-Counter -Counter "Hyper-V Virtual Storage Device(*)Read Bytes/sec"
Get-Counter -Counter "Hyper-V Virtual Storage Device(*)Write Bytes/sec"
# IOPS counters
Get-Counter -Counter "Hyper-V Virtual Storage Device(*)Read Operations/Sec"
Get-Counter -Counter "Hyper-V Virtual Storage Device(*)Write Operations/Sec"
Step 3: Collect Multiple Counters in a Single Sample
Rather than querying counters individually, collect all key Hyper-V metrics in a single Get-Counter call and export them for analysis.
$HyperVCounters = @(
"Hyper-V Hypervisor Logical Processor(_Total)% Total Run Time",
"Hyper-V Hypervisor Logical Processor(_Total)% Guest Run Time",
"Hyper-V Hypervisor Logical Processor(_Total)% Hypervisor Run Time",
"Hyper-V Virtual Machine Health SummaryHealth Critical",
"Hyper-V Virtual Machine Health SummaryHealth Ok",
"Hyper-V Virtual Machine Bus(*)Bytes/sec",
"Hyper-V Virtual Storage Device(*)Read Bytes/sec",
"Hyper-V Virtual Storage Device(*)Write Bytes/sec",
"Hyper-V Virtual Storage Device(*)Read Operations/Sec",
"Hyper-V Virtual Storage Device(*)Write Operations/Sec",
"Hyper-V Hypervisor Virtual Processor(*)% Total Run Time"
)
# Sample every 5 seconds, collect 12 samples (1 minute of data)
$Samples = Get-Counter -Counter $HyperVCounters -SampleInterval 5 -MaxSamples 12
# Export to CSV for analysis
$Samples | Export-Counter -Path "C:PerfDatahyperv-baseline.csv" -FileFormat CSV
Write-Host "Counter data saved to C:PerfDatahyperv-baseline.csv"
Step 4: Create a Data Collector Set for Ongoing Baselines
A Data Collector Set (DCS) runs continuously in the background and saves counter data to BLG files that can be opened in Performance Monitor. Create one via PowerShell using logman.
# Create a counter list file
$CounterFile = "C:PerfDatahyperv-counters.txt"
New-Item -Path "C:PerfData" -ItemType Directory -Force | Out-Null
@"
Hyper-V Hypervisor Logical Processor(_Total)% Total Run Time
Hyper-V Hypervisor Logical Processor(_Total)% Guest Run Time
Hyper-V Virtual Machine Health SummaryHealth Critical
Hyper-V Virtual Storage Device(*)Read Bytes/sec
Hyper-V Virtual Storage Device(*)Write Bytes/sec
Hyper-V Virtual Machine Bus(*)Bytes/sec
MemoryAvailable MBytes
Processor(_Total)% Processor Time
"@ | Set-Content -Path $CounterFile
# Create the Data Collector Set (30-second intervals, save to BLG)
logman create counter "Hyper-V Baseline" `
--cf $CounterFile `
-f bincirc `
-max 1024 `
-si 30 `
-o "C:PerfDataHyperV-Baseline.blg"
# Start collecting
logman start "Hyper-V Baseline"
# Stop when done (or schedule via Task Scheduler)
# logman stop "Hyper-V Baseline"
Step 5: Query Hyper-V Metrics via WMI (Msvm Classes)
The Hyper-V WMI namespace (rootvirtualizationv2) exposes richer VM state information than perfmon counters alone. Msvm_ComputerSystem represents virtual machines, and Msvm_Processor provides per-VP metrics.
# List all VMs and their operational status via WMI
Get-CimInstance -Namespace "rootvirtualizationv2" -ClassName "Msvm_ComputerSystem" |
Where-Object { $_.Caption -eq "Virtual Machine" } |
Select-Object ElementName, EnabledState, HealthState, @{
Name = "State"
Expression = {
switch ($_.EnabledState) {
2 { "Running" }
3 { "Stopped" }
6 { "Paused" }
32768 { "Saved" }
default { "Unknown ($_)" }
}
}
}
# Get CPU load summary for each VM using Msvm_SummaryInformation
$SummaryInfo = Get-CimInstance -Namespace "rootvirtualizationv2" -ClassName "Msvm_SummaryInformation"
$SummaryInfo | Select-Object ElementName, NumberOfProcessors, ProcessorLoad, MemoryUsage, Uptime |
Sort-Object ProcessorLoad -Descending | Format-Table -AutoSize
Step 6: Integrate Hyper-V Metrics with Prometheus via windows_exporter
The windows_exporter agent exposes Windows performance counters as Prometheus metrics on port 9182. The hyperv collector translates Hyper-V perfmon counters into Prometheus format.
# Download windows_exporter
$ExporterUrl = "https://github.com/prometheus-community/windows_exporter/releases/download/v0.27.0/windows_exporter-0.27.0-amd64.exe"
New-Item -Path "C:windows_exporter" -ItemType Directory -Force | Out-Null
Invoke-WebRequest -Uri $ExporterUrl -OutFile "C:windows_exporterwindows_exporter.exe"
# Install as a Windows service with hyperv collector enabled
$Collectors = "cpu,cs,hyperv,memory,net,os,logical_disk,process"
New-Service -Name "windows_exporter" `
-DisplayName "Windows Exporter (Prometheus)" `
-BinaryPathName "C:windows_exporterwindows_exporter.exe --collectors.enabled=$Collectors --telemetry.addr=:9182" `
-StartupType Automatic
Start-Service -Name "windows_exporter"
# Verify metrics are being served
Invoke-WebRequest -Uri "http://localhost:9182/metrics" -UseBasicParsing |
Select-Object -ExpandProperty Content |
Select-String "windows_hyperv" |
Select-Object -First 20
Add the Hyper-V host to your Prometheus prometheus.yml scrape configuration, then import the Windows Exporter Dashboard (Grafana ID 14694) to visualise hypervisor CPU overhead, VM health state, storage IOPS, and VMBus throughput on a live Grafana dashboard.
Step 7: Alert on Critical Hyper-V Health States
A scheduled PowerShell task can poll the Health Critical counter and send an alert email when any VM enters a critical state.
# Health check script — save as C:ScriptsCheck-HyperVHealth.ps1
$HealthCritical = (Get-Counter "Hyper-V Virtual Machine Health SummaryHealth Critical").CounterSamples[0].CookedValue
if ($HealthCritical -gt 0) {
$CriticalVMs = Get-VM | Where-Object { $_.Status -like "*Critical*" -or $_.HeartBeat -eq "NoContact" }
$Body = "ALERT: $HealthCritical VM(s) in critical state on $env:COMPUTERNAME`n"
$Body += ($CriticalVMs | Select-Object Name, State, Status | Out-String)
Send-MailMessage -SmtpServer "smtp.corp.local" `
-From "[email protected]" `
-To "[email protected]" `
-Subject "Hyper-V Health Alert — $env:COMPUTERNAME" `
-Body $Body
}
# Register as a scheduled task running every 5 minutes
$Action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NonInteractive -File C:ScriptsCheck-HyperVHealth.ps1"
$Trigger = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 5) -Once -At (Get-Date)
Register-ScheduledTask -TaskName "HyperV-HealthCheck" -Action $Action -Trigger $Trigger -RunLevel Highest
Conclusion
Windows Server 2025 provides multiple overlapping tools for Hyper-V monitoring — performance counters via Get-Counter and perfmon, rich VM state data via WMI’s Msvm_ComputerSystem and Msvm_SummaryInformation classes, persistent baselines via Data Collector Sets and logman, and Prometheus-compatible metrics via windows_exporter. The most effective monitoring strategy combines all of these: use Get-Counter for ad hoc investigation, a Data Collector Set for continuous baselines, WMI for health automation scripts, and windows_exporter with Grafana for a long-term, visual monitoring platform. Establishing a performance baseline during normal operations is critical — without it, you cannot distinguish a real anomaly from expected load growth.