Completely Remove a Hyper-V Virtual Machine with PowerShell

Posted on October 24, 2014 by Jeff Hicks in PowerShell with 0 Comments

In the PowerShell Hyper-V module there is a cmdlet called Remove-VM that does pretty much what the name says. You give it the name of a virtual machine (VM) on a Hyper-V server and PowerShell will gladly remove it. You can remove 1, 10, or 100 VMs with the same, simple one-line command: Remove-VM MyVM –computername HV01. The cmdlet will also delete the VM from the Hyper-V host just as if you had used the graphical Hyper-V Manager to delete it. Note that the VM must not be running.

However, not everything is completely removed. When you delete a VM, the only thing that is truly deleted is the configuation file and its registration with the Hyper-V host. Any VHD or VHDX files remain, untouched. I suppose the thinking is that you might want to re-use the virtual disk file with another VM.

A VM is an easy thing to create, but a virtual disk is a bit more valuable. Still, what if you truly want to remove a VM including disk files? Here’s one way I have come up with using PowerShell and it can all be accomplished from your desktop, assuming you have the Hyper-V module available on your desktop. Let’s walk through the process.

First, we need a VM.

Using PowerShell, we can see the path for the VM’s hard drive.

Getting the path to the virtual machine's hard drive with Windows PowerShell. (Image Credit: Jeff Hicks)

Getting the path to the virtual machine’s hard drive with Windows PowerShell. (Image Credit: Jeff Hicks)

We can also use Get-VHD.

Using Get-VHD with Windows PowerShell. (Image Credit: Jeff Hicks)

Using Get-VHD with Windows PowerShell. (Image Credit: Jeff Hicks)

Unfortunately, Get-VHD won’t let us pipe an object with the computer name. But here to you can see a path. This path is relative to the Hyper-V host. In my network, that is CHI-HVR2. So to delete this file, I will need to rely on PowerShell remoting.

Using PowerShell remoting to delete file. (Image Credit: Jeff Hicks)

Using PowerShell remoting to delete file. (Image Credit: Jeff Hicks)

I can re-run this without –Whatif and all disk files will be removed. Once the disks are gone I can  go ahead and run Remove-VM.

Using Remove-VM with Windows PowerShell. (Image Credit: Jeff Hicks)

Using Remove-VM with Windows PowerShell. (Image Credit: Jeff Hicks)

However, there is a potential wrinkle if the VM has any snapshots. Let’s look at another VM as an example. My Test2 VM has a single VHDX file D:\Disks\test2.vhdx. But it also has a snapshot so the current file name reflects that as you can see in the following image:

Example virtual machine with snapshots. (Image Credit: Jeff Hicks)

Example virtual machine with snapshots. (Image Credit: Jeff Hicks)

Sponsored

When you run Remove-VM it will also remove any snapshots automatically. This means that the disk configuration will revert back to Test2.vhdx, and it won’t be deleted. I suppose I could try to parse the file name. But because the snapshots are going to go anyway, I might as well delete them myself first.

Now, the VM reflects the correct file path:

Removing the virtual machine's snapshot with Windows PowerShell. (Image Credit: Jeff Hicks)

Removing the virtual machine’s snapshot with Windows PowerShell. (Image Credit: Jeff Hicks)

With this done, I can remove the file and VM as I did before. But now for the fun part.

I’m assuming this is a task you may want to perform more than once, in which case having a PowerShell tool will make life much easier. So here is my function called Remove-MyVM. This will require PowerShell v4 and the Hyper-V PowerShell module. But you should be able to run it on a Windows 8.1 desktop.

This script is mix of proxy function and wrapper. I probably could have written something completely from scratch, but I wanted to take advantage existing cmdlets. The proxy function is actually for Get-VM and during pipeline processing that happens first. But then I have code to automatically handle all of the tasks I just demonstrated.

In a proxy function, the script command must be a single, pipelined expression, so I had to get creative. I’m taking advantage of a new PowerShell v4 feature called pipelinevariable, which has an alias of pv.

The results of the wrapped command, i.e., Get-VM, are saved to the variable $pv so that I can use them later in the pipelined expression. The Get-VM command is going to return a collection of VMs, and I decided that the best way to handle the processing is to run all the commands remotely, so I pipe to a Foreach-Object and before anything is processed, create a PSSession to the Hyper-V host.

I process each VM with Invoke-Command, running a scriptblock to remove snapshots, disk files and VMs. Because I wanted to preserve features like –Whatif and –Confirm, even in the remote scriptblock I had to resort to a little trickery. While a script block can use parameters, they are positional and you really can’t pass something like –Verbose or –Whatif. So the beginning of the script block sets preferences remotely, using local values.

But now I can run the command like this:

Or I can pipe an expression to Remove-MyVM.

Piping an expression to Remove-MyVM with PowerShell. (Image Credit: Jeff Hicks)

Piping an expression to Remove-MyVM with PowerShell. (Image Credit: Jeff Hicks)

When you are piping something to Remove-MyVM, be sure to include the computer name again for the Hyper-V host. Otherwise the command will try to find the VMs locally.

Sponsored

There is one quirk though with Remove-VM. As you can see in my screen shot, I am being prompted for confirmation. It appears the Remove-VM will always prompt you regardless of what you do with –Confirm. In this case, you might as well say yes because the disk files have already been removed. Please, test this in a safe, non-production environment so that you fully understand how it works. Standard disclaimers apply.

I hope you find this useful, and if so please let me know.

Sponsored

Tagged with , , ,