My Technical Notes

Sunday, 25 December 2016

Deleting a Hyper-V Virtual Machine using PowerShell

Introduction

This article explains how to remove a Hyper-V machine completely using PowerShell which you will run elevated.

Importing `Hyper-V` Module

PowerShell 3.0 and above supports Module autoloading, but if you have turned it off, you will have to explicitly load the `Hyper-V` module:


Import-Module Hyper-V

Get VM Object

First run `Get-VM` to list all the VMs on the machine. You can easily get the $i^{th}$ element using:


$vm = @(Get-VM)[i];

Or just get the vm by name:


$vm = Get-VM -VMName "My VM Name";

Ensure that the VM is off by checking that `$vm.State` returns `Off`.

Delete Checkpoints

"Checkpoint" is Microsoft's name for a "snapshot". To delete all the checkpoints of `$vm` do:


Remove-VMCheckpoint -VM $vm

Delete VM's Hard Drives

You can list all the hard drives of the VM by using:


$vm.HardDrives | % Path

The other method is by using the `Get-VHD` command:


Get-VHD -VMId $vm.Id | % Path

We want to delete all the hard drives of this VM:


Get-VHD -VMId $vm.Id | % Path | Remove-Item

Delete VM

Finally, we delete the VM itself:


Remove-VM -VM $vm -Force

A Script to do the Above

Below is a function `Remove-VM2` which will do the above:


if (!(Get-Module Hyper-V)) {
    Import-Module Hyper-V    
}

Function Remove-VM2
{
    param(
    [Microsoft.HyperV.PowerShell.VirtualMachine]$VM,
    [string]$VMName,
    [Guid]$VMId
    )

    $ErrorActionPreference = 'Stop';

    [Microsoft.HyperV.PowerShell.VirtualMachine]$_vm = & {
        if ($VM) { return $VM; }
        elseif ($VMName) {
            $vms = @(Get-VM -Name $VMName)
            if ($vms.Length -ne 1) {
                throw "Multiple VMs found with name '$VMName'";
            } else {
                return $vms[0];
            }
        } elseif ($VMId) {
            Get-VM -Id $VMId
        } else {
            throw "No parameters specified";
        }
    }

    # stop the vm if ncessary
    if ($_vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::Off) {
        Stop-VM -VM $_vm -TurnOff -Force;

        if ($_vm.State -ne [Microsoft.HyperV.PowerShell.VMState]::Off) {
            throw "VM could not be shutdown";
        }
    }

    # check to see if there are any checkpoints
    if ((Get-VMCheckpoint -VM $_vm | measure).Count -gt 0) {
        # delete checkpoints
        Remove-VMCheckpoint -VM $_vm;
        
        $all = {
            param($arr, $predicate)
            ($arr | where { -not (& $predicate) } | measure).Count -eq 0
        }

        # wait until Hyper-V has processed all checkpoints
        while ($true) {
            try {
                if (& $all (Get-VHD -VMId $_vm.Id) { [string]::IsNullOrWhiteSpace($_.ParentPath) }) {
                    break;
                }
            }
            catch { }

            Start-Sleep -Milliseconds 250
        }
    }

    # delete hard drives
    Get-VHD -VMId $_vm.Id | % Path | Remove-Item
    
    # finally delete the vm
    Remove-VM -VM $_vm -Force
}

No comments: