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_exporter binary 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.