Skip to main content

How to Get the Replication Status of a Hyper-V VM in Batch Script

Hyper-V Replica is a disaster recovery feature that continuously replicates virtual machines from a primary host to a replica host. Monitoring replication status is critical to ensure that your disaster recovery plan is functional and that replicated VMs are up to date. A replication failure that goes unnoticed means your backup copy is stale, and a failover would result in significant data loss.

In this guide, we will explore how to query and monitor Hyper-V replication status from a Batch Script using PowerShell cmdlets.

Understanding Replication States

StateMeaning
Ready to beginReplication is configured but has not started initial replication
ReplicatingActive replication is in progress (normal operating state)
Resynchronization requiredReplicas are out of sync; a full resync is needed
SuspendedReplication is paused (manually or due to error)
CriticalReplication has failed and requires immediate attention
Not applicableVM is not configured for replication

Method 1: Checking Replication Status of a Specific VM

@echo off
setlocal

set "vm_name=ProductionDB"

:: Verify Admin
net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Administrator privileges required.
pause
exit /b 1
)

echo Replication status for "%vm_name%":
echo ====================================
echo.

powershell -NoProfile -Command ^
"$rep = Get-VMReplication -VMName '%vm_name%' -ErrorAction SilentlyContinue;" ^
"if (-not $rep) {" ^
" Write-Host '[INFO] VM is not configured for replication.'; exit 0" ^
"};" ^
"Write-Host (' State: {0}' -f $rep.State);" ^
"Write-Host (' Health: {0}' -f $rep.Health);" ^
"Write-Host (' Mode: {0}' -f $rep.Mode);" ^
"Write-Host (' Primary Server: {0}' -f $rep.PrimaryServer);" ^
"Write-Host (' Replica Server: {0}' -f $rep.ReplicaServer);" ^
"Write-Host (' Frequency (sec): {0}' -f $rep.FrequencySec);" ^
"Write-Host (' Last Replication: {0}' -f $rep.LastReplicationTime);" ^
"if ($rep.LastReplicationTime) {" ^
" $age = (Get-Date) - $rep.LastReplicationTime;" ^
" Write-Host (' Replication Age: {0}d {1}h {2}m ago' -f $age.Days, $age.Hours, $age.Minutes)" ^
"}"

pause

Method 2: Listing All Replicated VMs

@echo off
setlocal

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo =============================================
echo HYPER-V REPLICATION STATUS
echo %date% %time:~0,8%
echo =============================================
echo.

powershell -NoProfile -Command ^
"$reps = @(Get-VMReplication -ErrorAction SilentlyContinue);" ^
"if ($reps.Count -eq 0) {" ^
" Write-Host 'No replicated VMs found on this host.'; exit 0" ^
"};" ^
"foreach ($r in ($reps | Sort-Object Health, VMName)) {" ^
" $icon = switch ($r.Health) {" ^
" 'Normal' {'[OK]'}" ^
" 'Warning' {'[!!]'}" ^
" 'Critical' {'[XX]'}" ^
" default {'[??]'}" ^
" };" ^
" Write-Host ('{0} {1,-25} State: {2,-15} Last: {3}' -f" ^
" $icon, $r.VMName, $r.State, $r.LastReplicationTime)" ^
"};" ^
"Write-Host '';" ^
"$normal = @($reps | Where-Object Health -eq 'Normal').Count;" ^
"$warning = @($reps | Where-Object Health -eq 'Warning').Count;" ^
"$critical = @($reps | Where-Object Health -eq 'Critical').Count;" ^
"Write-Host ('Summary: Normal: {0} Warning: {1} Critical: {2}' -f $normal, $warning, $critical)"

pause

Method 3: Detailed Replication Health Report

@echo off
setlocal

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo Generating replication health report...
echo.

powershell -NoProfile -Command ^
"$reps = @(Get-VMReplication -ErrorAction SilentlyContinue);" ^
"if ($reps.Count -eq 0) {" ^
" Write-Host 'No replicated VMs found.'; exit 0" ^
"};" ^
"foreach ($r in $reps) {" ^
" Write-Host ('=== ' + $r.VMName + ' ===');" ^
" Write-Host (' Mode: {0}' -f $r.Mode);" ^
" Write-Host (' State: {0}' -f $r.State);" ^
" Write-Host (' Health: {0}' -f $r.Health);" ^
" Write-Host (' Frequency: Every {0} seconds' -f $r.FrequencySec);" ^
" Write-Host (' Primary: {0}' -f $r.PrimaryServer);" ^
" Write-Host (' Replica: {0}' -f $r.ReplicaServer);" ^
" Write-Host (' Last Sync: {0}' -f $r.LastReplicationTime);" ^
" $stats = Measure-VMReplication -VMName $r.VMName -ErrorAction SilentlyContinue;" ^
" if ($stats) {" ^
" $avgMB = if ($stats.AverageReplicationSize) { [math]::Round($stats.AverageReplicationSize / 1MB, 2) } else { 'N/A' };" ^
" $pendMB = if ($stats.PendingReplicationSize) { [math]::Round($stats.PendingReplicationSize / 1MB, 2) } else { 'N/A' };" ^
" Write-Host (' Avg Size: {0} MB' -f $avgMB);" ^
" Write-Host (' Pending Size: {0} MB' -f $pendMB);" ^
" Write-Host (' Missed Cycles: {0}' -f $stats.MissedReplicationCount);" ^
" Write-Host (' Successful: {0}' -f $stats.SuccessfulReplicationCount)" ^
" } else {" ^
" Write-Host ' Statistics: Not available'" ^
" };" ^
" Write-Host ''" ^
"}"

pause

Method 4: Replication Health Monitor (Auto-Refresh)

@echo off
title Replication Health Monitor

:loop
cls
echo =============================================
echo REPLICATION MONITOR
echo %date% %time:~0,8%
echo =============================================
echo.

powershell -NoProfile -Command ^
"$reps = @(Get-VMReplication -ErrorAction SilentlyContinue);" ^
"if ($reps.Count -eq 0) { Write-Host 'No replications configured.'; exit 0 };" ^
"foreach ($r in ($reps | Sort-Object Health, VMName)) {" ^
" $color = switch ($r.Health) {" ^
" 'Normal' {'Green'}" ^
" 'Warning' {'Yellow'}" ^
" 'Critical' {'Red'}" ^
" default {'White'}" ^
" };" ^
" $age = if ($r.LastReplicationTime) {" ^
" ((Get-Date) - $r.LastReplicationTime).ToString('hh\:mm\:ss')" ^
" } else { 'Never' };" ^
" Write-Host ('{0,-25} {1,-12} {2,-15} Age: {3}' -f" ^
" $r.VMName, $r.Health, $r.State, $age) -ForegroundColor $color" ^
"};" ^
"Write-Host '';" ^
"$issues = @($reps | Where-Object { $_.Health -ne 'Normal' });" ^
"if ($issues.Count -gt 0) {" ^
" Write-Host ('ALERT: {0} replication(s) need attention!' -f $issues.Count) -ForegroundColor Red" ^
"} else {" ^
" Write-Host 'All replications healthy.' -ForegroundColor Green" ^
"}"

echo.
echo Refreshing in 30 seconds. Press Ctrl+C to stop.
timeout /t 30 /nobreak >nul
goto loop

Method 5: CSV Export for Compliance Reporting

@echo off
setlocal

for /f "tokens=2 delims==" %%T in ('wmic os get LocalDateTime /value') do set "dt=%%T"
set "stamp=%dt:~0,4%%dt:~4,2%%dt:~6,2%"
set "output=%~dp0replication_report_%COMPUTERNAME%_%stamp%.csv"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

echo Exporting replication status to CSV...

powershell -NoProfile -Command ^
"@(Get-VMReplication -ErrorAction SilentlyContinue) |" ^
"Select-Object VMName, State, Health, Mode," ^
" PrimaryServer, ReplicaServer," ^
" FrequencySec, LastReplicationTime," ^
" @{Name='AgeMinutes'; Expression={" ^
" if ($_.LastReplicationTime) {" ^
" [math]::Round(((Get-Date) - $_.LastReplicationTime).TotalMinutes, 1)" ^
" } else { 'N/A' }" ^
" }} |" ^
"Export-Csv -Path '%output%' -NoTypeInformation"

if %errorlevel%==0 (
echo [SUCCESS] Exported to: %output%
echo.
type "%output%"
) else (
echo [ERROR] Export failed.
)

echo.
pause

Resuming Failed or Suspended Replication

@echo off
setlocal

set "vm_name=ProductionDB"

net session >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Admin required.
pause
exit /b 1
)

:: Check current state
set "state="
for /f "delims=" %%S in ('powershell -NoProfile -Command ^
"$r = Get-VMReplication -VMName '%vm_name%' -ErrorAction SilentlyContinue; if ($r) { $r.State }"') do set "state=%%S"

if not defined state (
echo [ERROR] VM "%vm_name%" has no replication configured or was not found.
pause
exit /b 1
)

echo Current replication state: %state%
echo.

if /i "%state%"=="Suspended" (
echo Resuming replication...
powershell -NoProfile -Command ^
"try { Resume-VMReplication -VMName '%vm_name%' -ErrorAction Stop; exit 0 }" ^
"catch { Write-Host ('[ERROR] ' + $_.Exception.Message); exit 1 }"
if !errorlevel!==0 (
echo [OK] Replication resumed.
)
) else if /i "%state%"=="ResynchronizationRequired" (
echo Starting resynchronization...
powershell -NoProfile -Command ^
"try { Resume-VMReplication -VMName '%vm_name%' -Resynchronize -ErrorAction Stop; exit 0 }" ^
"catch { Write-Host ('[ERROR] ' + $_.Exception.Message); exit 1 }"
if !errorlevel!==0 (
echo [OK] Resynchronization started.
)
) else (
echo [INFO] No action needed. State is: %state%
)

pause

Common Mistakes

The Wrong Way: Not Monitoring Replication Age

:: WRONG - Only checking state, not age
powershell -Command "(Get-VMReplication -VMName 'MyVM').State"
:: Returns "Replicating" even if the last sync was 24 hours ago

Output Concern: The replication state may show "Replicating" even when the last successful sync was hours ago. Always check LastReplicationTime and calculate the age to detect stale replicas.

The Wrong Way: Assuming All VMs Are Replicated

:: WRONG - Fails silently if the VM has no replication configured
powershell -Command "Measure-VMReplication -VMName 'NonReplicatedVM'"

Not all VMs on a host may be configured for replication. Always handle the case where Get-VMReplication returns null or no results.

Best Practices

  1. Monitor replication age: Check the time since the last successful replication, not just the state.
  2. Alert on non-Normal health: Any replication in Warning or Critical health requires investigation.
  3. Use Measure-VMReplication for statistics: It provides detailed metrics on replication sizes, missed cycles, and success rates.
  4. Schedule regular reports: Export replication status to CSV daily for compliance and audit trails.
  5. Resume suspended replications promptly: Suspended replications accumulate changes that make resynchronization slower.

Conclusion

Getting the replication status of Hyper-V VMs from a Batch Script is powered by PowerShell's Get-VMReplication and Measure-VMReplication cmdlets. Regular monitoring of replication state, health, and age ensures that disaster recovery copies remain current and functional. By building automated dashboards, alerting on degraded replications, and exporting status reports for compliance, administrators maintain confidence that their disaster recovery infrastructure will perform when needed.