Skip to content

How to Delete a Hyper-V VM Snapshot in Batch Script

Learn how to delete Hyper-V VM checkpoints (snapshots) in bulk using Batch Scripts and PowerShell, including single deletion, retention policies, and safe cleanup methods.

Hyper-V checkpoints (snapshots) are powerful safety nets, but they accumulate cost over time. Every checkpoint creates differencing disk files (.avhdx) that grow as the VM writes data, degrading I/O performance and consuming significant storage. Deleting old checkpoints that are no longer needed reclaims disk space and improves VM performance by merging the differencing disks back into the base VHDX.

In this guide, we will explore how to delete Hyper-V VM checkpoints from a Batch Script using PowerShell, including single deletion, bulk cleanup, and retention policy automation.

Understanding What Happens When You Delete a Checkpoint

When you delete a checkpoint:

  1. The checkpoint metadata is removed from Hyper-V Manager.
  2. The associated differencing disk (.avhdx) is merged into the parent disk.
  3. The merge happens in the background. For running VMs, there is no downtime.
  4. The merge may take significant time for large differencing disks (minutes to hours for multi-GB files).
  5. The VM continues operating normally during the merge.

Method 1: Deleting a Specific Checkpoint by Name

@echo off
setlocal

set "vm_name=WebServer-01"
set "cp_name=Pre-Update [2024-01-15 09:30]"

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

echo Deleting checkpoint "%cp_name%" from "%vm_name%"...

powershell -noprofile -command ^
  "$cp = Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue | ^
         Where-Object { $_.Name -eq '%cp_name%' }; ^
   if (-not $cp) { exit 1 }; ^
   $cp | Remove-VMCheckpoint -Confirm:$false"

if %errorlevel%==0 (
    echo [SUCCESS] Checkpoint deleted. Differencing disk merge is in progress.
) else (
    echo [ERROR] Checkpoint not found or deletion failed.
)

pause

Method 2: Deleting All Checkpoints for a VM

To clean up all checkpoints at once:

@echo off
setlocal

set "vm_name=DatabaseVM"

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

:: Count existing checkpoints
set "count=0"
for /f "delims=" %%C in ('powershell -noprofile -command ^
  "@(Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue).Count"') do set "count=%%C"

echo VM: %vm_name%
echo Checkpoints found: %count%

if "%count%"=="0" (
    echo [INFO] No checkpoints to delete.
    pause
    exit /b 0
)

echo.
set /p "confirm=Delete ALL %count% checkpoints? Type YES: "
if /i not "%confirm%"=="YES" (
    echo [CANCELLED]
    pause
    exit /b 0
)

echo Deleting all checkpoints...
powershell -noprofile -command ^
  "Get-VMCheckpoint -VMName '%vm_name%' | Remove-VMCheckpoint -Confirm:$false"

echo [OK] All checkpoints deleted. Merging in background.
pause

Method 3: Interactive Checkpoint Deletion

@echo off
title Checkpoint Cleanup Tool
setlocal enabledelayedexpansion

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

set /p "vm_name=Enter VM name: "

:list
cls
echo =============================================
echo   CHECKPOINT CLEANUP: %vm_name%
echo =============================================
echo.

set "count=0"
for /f "delims=" %%L in ('powershell -noprofile -command ^
  "Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue | Sort-Object CreationTime | ForEach-Object { Write-Host ($_.Name + ' | Created: ' + $_.CreationTime) }"') do (
    set /a count+=1
    echo   !count!. %%L
)

if "!count!"=="0" (
    echo   No checkpoints found.
    pause
    exit /b 0
)

echo.
echo Options:
echo   [1-!count!] Delete specific checkpoint
echo   [A]     Delete ALL checkpoints
echo   [Q]     Quit
echo.
set /p "choice=Select: "

if /i "!choice!"=="Q" exit /b 0
if /i "!choice!"=="A" (
    set /p "c2=Delete ALL? Type YES: "
    if /i "!c2!"=="YES" (
        powershell -noprofile -command "Get-VMCheckpoint -VMName '%vm_name%' | Remove-VMCheckpoint -Confirm:$false"
        echo [OK] All deleted.
        pause
    )
    goto list
)

:: Validate numeric input
set /a "sel=!choice!" 2>nul
if !sel! lss 1 (
    echo [ERROR] Invalid selection.
    pause
    goto list
)
if !sel! gtr !count! (
    echo [ERROR] Invalid selection.
    pause
    goto list
)

:: Delete specific checkpoint by index
powershell -noprofile -command ^
  "$cps = @(Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue | Sort-Object CreationTime); ^
   $i = !sel! - 1; ^
   if ($i -ge 0 -and $i -lt $cps.Count) { ^
       Write-Host ('Deleting: ' + $cps[$i].Name); ^
       $cps[$i] | Remove-VMCheckpoint -Confirm:$false; ^
       Write-Host '[OK] Deleted.' ^
   } else { ^
       Write-Host '[ERROR] Checkpoint no longer exists.' ^
   }"

pause
goto list

Method 4: Age-Based Cleanup (Retention Policy)

Automatically delete checkpoints older than a specified number of days:

@echo off
setlocal

set "vm_name=AppServer-01"
set "max_age_days=7"

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

echo Cleaning up checkpoints older than %max_age_days% days for "%vm_name%"...
echo.

powershell -noprofile -command ^
  "$cutoff = (Get-Date).AddDays(-%max_age_days%); ^
   $old = @(Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue | ^
            Where-Object { $_.CreationTime -lt $cutoff }); ^
   if ($old.Count -eq 0) { ^
       Write-Host 'No checkpoints older than %max_age_days% days found.' ^
   } else { ^
       Write-Host ('Found ' + $old.Count + ' old checkpoint(s):'); ^
       $old | ForEach-Object { ^
           Write-Host ('  Deleting: ' + $_.Name + ' (Created: ' + $_.CreationTime + ')'); ^
           $_ | Remove-VMCheckpoint -Confirm:$false ^
       }; ^
       Write-Host '[OK] Cleanup complete.' ^
   }"

pause

Method 5: Cleanup All VMs on the Host

For server-wide checkpoint management:

@echo off
setlocal

set "max_age_days=14"

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

echo =============================================
echo   HOST-WIDE CHECKPOINT CLEANUP
echo   Removing checkpoints older than %max_age_days% days
echo =============================================
echo.

powershell -noprofile -command ^
  "$cutoff = (Get-Date).AddDays(-%max_age_days%); ^
   $total = 0; ^
   Get-VM | ForEach-Object { ^
       $vm = $_.Name; ^
       $old = @(Get-VMCheckpoint -VMName $vm -ErrorAction SilentlyContinue | ^
                Where-Object { $_.CreationTime -lt $cutoff }); ^
       if ($old.Count -gt 0) { ^
           Write-Host ('--- ' + $vm + ': ' + $old.Count + ' old checkpoint(s) ---'); ^
           $old | ForEach-Object { ^
               Write-Host ('  Deleting: ' + $_.Name); ^
               $_ | Remove-VMCheckpoint -Confirm:$false; ^
               $total++ ^
           } ^
       } ^
   }; ^
   Write-Host ''; ^
   Write-Host ('[TOTAL] Deleted ' + $total + ' checkpoints.')"

pause

Deleting a Checkpoint Subtree

A checkpoint may have child checkpoints beneath it. To delete a checkpoint and all its descendants:

@echo off
set "vm_name=TestVM"
set "cp_name=Root Checkpoint"

powershell -noprofile -command ^
  "Get-VMCheckpoint -VMName '%vm_name%' -ErrorAction SilentlyContinue | ^
   Where-Object { $_.Name -eq '%cp_name%' } | ^
   Remove-VMCheckpoint -IncludeAllChildCheckpoints -Confirm:$false"

echo [OK] Checkpoint and all children deleted.
pause

Common Mistakes

The Wrong Way: Manually Deleting AVHDX Files

:: WRONG - Never delete differencing disk files directly
del "D:\Hyper-V\VHDs\WebServer-01_GUID.avhdx"

Output Concern: Manually deleting .avhdx files bypasses the Hyper-V merge process and corrupts the VM's disk chain. The VM will fail to start with an error about missing parent disks. Always use Remove-VMCheckpoint to properly merge and clean up differencing disks.

The Wrong Way: Confusing Delete with Restore

:: WRONG mental model:
:: "Delete checkpoint" does NOT revert the VM to before the checkpoint
:: It KEEPS the current state and removes the ability to go back

Deleting a checkpoint merges its changes into the base disk, making them permanent. It does not undo those changes. To revert, use Restore-VMCheckpoint before deleting.

Best Practices

  1. Implement a retention policy: Automatically delete checkpoints older than 7-14 days.
  2. Never delete .avhdx files manually: Always use Remove-VMCheckpoint for proper disk merging.
  3. Monitor merge progress: Large merges can take hours. Monitor disk I/O during the merge.
  4. Delete from oldest to newest: While Hyper-V handles the merge order, cleaning from oldest first is logically cleaner.
  5. Schedule cleanup during low activity: The merge process consumes disk I/O. Run cleanup during off-peak hours.

Conclusion

Deleting Hyper-V VM checkpoints from a Batch Script is handled by PowerShell's Remove-VMCheckpoint cmdlet, which removes the checkpoint metadata and triggers a background merge of the differencing disk into its parent. Implementing age-based retention policies ensures checkpoints do not accumulate indefinitely, while interactive tools give administrators granular control over which checkpoints to preserve or remove. The critical takeaway is that deletion means making changes permanent, not reverting them, and that disk files must never be deleted manually.

Navigation