PowerShell Hyper-V VM Health Check and Auto-Report Script

Manual Hyper-V Checks Do Not Scale
Opening Hyper-V Manager to check VM states on three hosts is manageable. Doing it across fifteen hosts covering two hundred virtual machines is not — especially when you need consistent data about CPU pressure, memory demand, replication health, and checkpoint age. A PowerShell health check script queries every VM across all hosts in seconds, applies consistent rules, and produces an HTML dashboard that can be emailed or published to a shared drive. Run it on a schedule and you have continuous visibility without daily manual effort.
Quick Answer
Use Get-VM -ComputerName to collect VM objects from multiple hosts, check State, CPUUsage, MemoryDemand, and Get-VMCheckpoint for snapshot age, then pipe the results into a ConvertTo-Html report with color-coded rows.
Getting All VMs with Get-VM Across Multiple Hosts
The -ComputerName parameter of Hyper-V cmdlets accepts an array of host names. You get a flat list of VM objects, each stamped with a ComputerName property identifying which host it came from. The Hyper-V PowerShell module must be installed on the management machine, not just on the hosts.
# Hyper-V module required — install if missing
if (-not (Get-Module -ListAvailable -Name Hyper-V)) {
Install-WindowsFeature -Name RSAT-Hyper-V-Tools -IncludeManagementTools
}
$hyperVHosts = @('HV01', 'HV02', 'HV03')
$allVMs = Get-VM -ComputerName $hyperVHosts -ErrorAction Stop
Write-Host "Found $($allVMs.Count) VMs across $($hyperVHosts.Count) hosts"
$allVMs | Group-Object ComputerName | Select-Object Name, Count
Found 47 VMs across 3 hosts
Name Count
---- -----
HV01 18
HV02 16
HV03 13
Checking CPU and Memory Demand vs Assigned
Hyper-V VM objects expose CPUUsage (current percentage) and MemoryDemand vs MemoryAssigned when Dynamic Memory is enabled. Calculating demand-to-assigned ratio highlights VMs that are consistently pressured and need more memory or migration.
$vmMetrics = $allVMs | ForEach-Object {
$memPct = if ($_.MemoryAssigned -gt 0) {
[math]::Round(($_.MemoryDemand / $_.MemoryAssigned) * 100, 1)
} else { 0 }
[PSCustomObject]@{
Host = $_.ComputerName
Name = $_.Name
State = $_.State
CPUPct = $_.CPUUsage
MemDemandGB = [math]::Round($_.MemoryDemand / 1GB, 2)
MemAssignedGB = [math]::Round($_.MemoryAssigned / 1GB, 2)
MemPressurePct= $memPct
}
}
# Flag high-pressure VMs
$vmMetrics | Where-Object { $_.MemPressurePct -gt 90 -or $_.CPUPct -gt 85 } |
Format-Table -AutoSize
Detecting VMs with Old Checkpoints
Checkpoints (snapshots) consume disk space and can degrade VM storage performance over time. Flag any VM with a checkpoint older than 7 days as a warning, and older than 30 days as critical.
$checkpointWarnings = foreach ($vm in $allVMs) {
$checkpoints = Get-VMCheckpoint -VMName $vm.Name -ComputerName $vm.ComputerName `
-ErrorAction SilentlyContinue
foreach ($cp in $checkpoints) {
$ageDays = ((Get-Date) - $cp.CreationTime).Days
if ($ageDays -gt 7) {
[PSCustomObject]@{
Host = $vm.ComputerName
VM = $vm.Name
Checkpoint = $cp.Name
AgeDays = $ageDays
Severity = if ($ageDays -gt 30) { 'Critical' } else { 'Warning' }
}
}
}
}
$checkpointWarnings | Sort-Object AgeDays -Descending | Format-Table -AutoSize
Hyper-V Replication Health with Measure-VMReplication
Measure-VMReplication returns replication statistics for each VM with replication configured. Check the Health property — anything other than Normal warrants investigation.
$replStatus = foreach ($host in $hyperVHosts) {
Measure-VMReplication -ComputerName $host -ErrorAction SilentlyContinue |
Select-Object @{N='Host';E={$host}}, VMName, Health,
ReplicationState, LastReplicationTime,
AverageReplicationSize
}
$replStatus | Where-Object { $_.Health -ne 'Normal' } | Format-Table -AutoSize
Flagging VMs in Critical or Off States
VMs in Critical state indicate a resource problem or configuration error requiring immediate attention. VMs that are Off unexpectedly (i.e., not intentionally shut down) should also be surfaced. Cross-reference against an expected-state list if you maintain one; otherwise flag all non-Running, non-Saved VMs.
Generating an HTML Health Dashboard
Combine all the data collected above into a single HTML report with color-coded rows. This report can be emailed as an attachment or dropped to a web share for the operations team.
$reportRows = $vmMetrics | ForEach-Object {
$severity = 'OK'
if ($_.State -ne 'Running') { $severity = 'Critical' }
elseif ($_.CPUPct -gt 85) { $severity = 'Warning' }
elseif ($_.MemPressurePct -gt 90) { $severity = 'Warning' }
$_ | Add-Member -NotePropertyName Severity -NotePropertyValue $severity -PassThru
}
$css = '<style>
body{font-family:Arial;font-size:13px}
table{border-collapse:collapse;width:100%}
th,td{border:1px solid #ccc;padding:5px 8px}
tr.Critical{background:#ff4c4c;color:#fff}
tr.Warning{background:#fff3cd}
tr.OK{background:#d4edda}
</style>'
$reportRows |
ConvertTo-Html -Head $css -Title 'Hyper-V VM Health Report' |
ForEach-Object {
$_ -replace '<tr><td>(Critical|Warning|OK)', '<tr class="$1"><td>$1'
} |
Out-File -FilePath 'C:\Reports\HyperV-Health.html' -Encoding UTF8
Write-Host "Report written to C:\Reports\HyperV-Health.html"
Common Errors
- Hyper-V PowerShell module not installed on the management machine. The cmdlets are part of the RSAT Hyper-V Tools feature (
RSAT-Hyper-V-Tools) on Windows Server and Hyper-V Module for Windows PowerShell on Windows 10/11. UseInstall-WindowsFeatureorEnable-WindowsOptionalFeatureto add them. - Get-VM on a remote host requires CredSSP or Kerberos constrained delegation. Hyper-V cmdlets with
-ComputerNameuse implicit remoting. If the management machine is not the Hyper-V host itself, double-hop authentication must be configured via CredSSP delegation or Kerberos constrained delegation in Active Directory.
Related Cmdlets / See Also
Wrapping Up
A scripted Hyper-V health check built on Get-VM, Get-VMCheckpoint, and Measure-VMReplication replaces hours of manual console work with a scheduled job that runs in minutes. Add the HTML report to a daily email digest and your operations team has consistent, objective visibility across the entire VM fleet.


