How to Create a New Virtual Machine from a Hyper-V Snapshot Part 2

In the first part of this series I walked through using the Hyper-V Manager to create a new virtual machine from a snapshot. If this is something you only need to do occasionally, there is no reason not to use a graphical tool. But if you find yourself doing this often, or would like a way to automate the process for the sake of efficiency, then you’ll need to turn to Windows PowerShell.

Using Hyper-V 3.0 PowerShell Cmdlets

When you install the Hyper-V role on Windows Server 2012 (or in Windows 8) and include the Management tools you should also get the Windows PowerShell module. You should see something like Figure 1 on Windows 8

Hyper-V PowerShell Module Feature

Figure 1 Windows 8 Hyper-V PowerShell Module Feature

Or on Windows Server 2012 something like Figure 2.

Windows Server 2012 Hyper-V PowerShell Module

Figure 2 Windows Server 2012 Hyper-V PowerShell Module

The PowerShell module includes commands for just about anything you would need to accomplish with Hyper-V. While I’m going to walk through using them interactively, you can just as easily take the commands and turn them into a PowerShell script or function, in essence creating your own Hyper-V PowerShell tools.

In PowerShell 3, modules will be autoloaded the first time you run one of its commands. But I’ll explicitly import the module anyway.

PS C:> Import-Module Hyper-V

I’m going to run the commands locally, but specify a computername so you can see how you could do this for a remote server.

PS C:> $Server = $env:COMPUTERNAME

Creating the snapshot with PowerShell

I’m going to take a snapshot of a virtual machine called Test Rig.

PS C:> $VMName= "Test Rig"

Because I want my imported VM to have a nice name, which is derived from the snapshot name, I’ll define one:

PS C:> $SnapName= "$VMName Clone"

Now to create the snapshot.

PS C:> $snapshot = Checkpoint-VM -Name $VMName -SnapshotName $SnapName -ComputerName $Server –passthru
PS C:> $snapshot

VMName   Name           SnapshotType CreationTime          ParentSnapshotName
------   ----           ------------ ------------          ------------------
Test Rig Test Rig Clone Standard     2/18/2013 12:41:14 PM

Next, I’ll export the snapshot to disk.

PS C:> $export = Export-VMSnapshot -VMSnapshot $snapshot -Path $ExportPath –Passthru

The path is relative to the Hyper-V server and should be created if it doesn’t already exist. The computername is included as a property in the $snapshot object so PowerShell knows what server to use. From here I have some options.

Creating a new virtual machine

I could create a totally new virtual machine using the exported VHD or VHDX files. I’ll create a new virtual machine without any disk drives. I’ll also “copy” some settings from the snapshot.

PS C:> $clone = New-VM -Name $snapshot.Name -NoVHD -MemoryStartupBytes $snapshot.MemoryStartup -switch "Private Data" -ComputerName $Server

You can obtain the switch name from Get-VMSwitch.

PS C:> Get-VMSwitch -ComputerName $server | Select name

Name
----
Private Data
Work Network

So now I have a virtual machine without any drives. The tricky part is that I want to add the drives from the export folder, not the paths specified in the snapshot configuration. So I need to do a little parsing to extract the drive name from the snapshot, e.g. “Test Rig.vhdx” without the path. Then I need to construct a new path using the export folder path and this file name. Finally, I’ll construct a hash table of parameters which I’ll eventually pass to the Add-VMHardDiskDrive cmdlet which will ensure I get the drives hooked back up in their original positions.

PS C:> ForEach ($drive in $Snapshot.HardDrives) {
>>  $VHD = Split-Path -leaf ($drive.path)
>>  $VHDPath = Join-Path (Join-Path (Join-Path $ExportPath $VMName) "Virtual Hard Disks") $VHD
>>  $addDriveHash=@{
>>  VMName=$Clone.Name
>>  Path=$VHDPath
>>  ControllerType=$drive.ControllerType
>>  ControllerNumber=$drive.ControllerNumber
>>  ControllerLocation=$drive.ControllerLocation
>>  Computername=$Server
>>    }
>>    Add-VMHardDiskDrive @addDriveHash
>> }
>>

You can see where eventually this is much easier accomplished in a script. The last step, and it is purely optional, is that I want to configure the new virtual machine with the same memory settings as the original. The Set-VM cmdlet offers a number of settings that I can pass all at once with a hash table.

PS C:> $paramhash=@{
>> MemoryStartupBytes=$snapshot.MemoryStartup
>> ProcessorCount=$snapshot.ProcessorCount
>> Notes="Cloned $(Get-Date)"
>> Name=$($clone.Name)
>> Computername=$Server
>> }
PS C:> if ($snapshot.DynamicMemoryEnabled) {
>> $paramhash.Add("DynamicMemory",$snapshot.DynamicMemoryEnabled)
>> $paramhash.Add("MemoryMinimumBytes",$snapshot.MemoryMinimum)
>> $paramhash.Add("MemoryMaximumBytes",$snapshot.MemoryMaximum)
>> }
>>
PS C:> Set-VM @paramhash

And it is done!

PS C:> Get-VM $clone.name -ComputerName $Server 

Name           State CPUUsage(%) MemoryAssigned(M) Uptime   Status
----           ----- ----------- ----------------- ------   ------
Test Rig Clone Off   0           0                 00:00:00 Operating normally

Importing the snapshot

The other approach is to import the snapshot. When you import the snapshot it will use the snapshot name. Since I defined it at the time I created the snapshot I should be ok. But if you want to modify the name again, you can use PowerShell to adjust the XML configuration file. There should only be one XML file in the Virtual Machines folder.

PS C:> $VMPath = Join-Path (Join-Path $ExportPath $VMName) "Virtual Machines"
PS C:> $VMConfigPath = (dir $vmpath -filter *.xml).FullName
PS C:> [xml]$config = Get-Content -Path $VMConfigPath
PS C:> $config.configuration.properties.name.'#text'="My Cloned VM"
PS C:> $config.Save($VMConfigPath)

Note that if you are doing this remotely, you’ll need to modify this to run via PowerShell Remoting, most likely using Invoke-Command.

Importing is very easy. I can register the import in place.

PS C:> Import-VM -Path $VMConfigPath –Register

This will only work if you are importing to a different server or if the original virtual machine no longer exists. Or you can copy. If you are importing on a different server you can use the default paths. But if you are importing on the same server, and the disks exist from the original virtual machine in the default location, you will need to specify a new path. Because I’m copying the imported virtual machine to the same server, I’m going to use the export path as my disk path.

PS C:> $VHDPath = (Join-Path (Join-Path $ExportPath $VMName) "Virtual Hard Disks")

Finally, the import command.

PS C:> Import-VM -Path $VMConfigPath -Copy -GenerateNewId -VhdDestinationPath $VHDPath -ComputerName $server

Because the original virtual machine still exists I need to generate a new GUID. If you look at help for Import-VM you’ll see that the VhdDestinationPath is marked as obsolete. But it still works and in this case I need it to.

Summary

This may seem like a lot of work, but once I have the PowerShell commands in place it isn’t too difficult to turn them into a tool that automatically converts a snapshot to a virtual machine. If you are new to the PowerShell Hyper-V module, be sure to look at help for all of the commands I’ve shown here. You can also download my demo commands here.

 

Altaro Hyper-V Backup
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

9 thoughts on "How to Create a New Virtual Machine from a Hyper-V Snapshot Part 2"

  • Antek says:

    You can do the same for NICs as for HDs…

    ForEach ($nic in $snapshot.networkadapters) {
    $addNicHash=@{
    VMName=$clone.name
    SwitchName=$nic.SwitchName
    Name=$nic.Name
    }
    Add-VMNetworkAdapter @addNicHash
    }

  • Antek says:

    Actually the snapshot method has a problem in that it ‘mangles’ the hard drive name to be avhd/avhdx drive and not the actual drive. Here is my script:

    $vm = get-VM -Name $VMName -ComputerName $Server

    if (-Not (Test-Path -Path .$snapname) ) {
    New-Item -Path .$snapname -Itemtype directory
    }

    $export = Export-VM -Name $VMName -Path .$snapname –Passthru

    New-VM -Name $snapname -NoVHD -MemoryStartupBytes $vm.MemoryStartup -ComputerName $Server

    sleep 5

    $clone = Get-VM $snapname

    Remove-VMNetworkAdapter -VMName $($clone.name) -Name ‘Network Adapter’

    sleep 5

    ForEach ($nic in $vm.networkadapters) {
    $addNicHash=@{
    VMName=$($clone.name)
    SwitchName=$nic.SwitchName
    Name=$nic.Name
    Computername=$Server
    }
    Add-VMNetworkAdapter @addNicHash
    }

    ForEach ($drive in $vm.HardDrives) {
    $VHD = Split-Path -leaf ($drive.path)
    $VHDPath = Join-Path (Join-Path (Join-Path .$($clone.name) $VMName) “Virtual Hard Disks”) $VHD
    $addDriveHash=@{
    VMName=$($clone.name)
    Path=$VHDPath
    ControllerType=$drive.ControllerType
    ControllerNumber=$drive.ControllerNumber
    ControllerLocation=$drive.ControllerLocation
    Computername=$Server
    }
    Add-VMHardDiskDrive @addDriveHash
    }

    $paramhash=@{
    MemoryStartupBytes=$vm.MemoryStartup
    ProcessorCount=$vm.ProcessorCount
    Notes=”Cloned $(Get-Date)”
    Name=$($clone.Name)
    Computername=$Server
    }

    if ($vm.DynamicMemoryEnabled) {
    $paramhash.Add(“DynamicMemory”,$vm.DynamicMemoryEnabled)
    $paramhash.Add(“MemoryMinimumBytes”,$vm.MemoryMinimum)
    $paramhash.Add(“MemoryMaximumBytes”,$vm.MemoryMaximum)
    }

    Set-VM @paramhash

    NOTE: The ‘sleep 5’ is there to ensure that the VM is actually *fully* registered on the HyperV host, without it creation will fail at random because of what appears to be a race condition.

  • There is a problem that I believe I missed in my original post if the virtual machine uses differencing disks. The exported snapshot merges them into a new parent file but the configuration (which I’m using in the script) points to the differencing disk. Your code is a bit different than mine. I am exporting the snapshot, which is the whole point to the article. You are exporting the virtual machine and cloning that, which is still useful. But you’ve got me working on a revision to my code for exporting a snapshot to a virtual machine which was my goal all along. Thanks for reading and taking the time to comment.

  • Disam says:

    Creating the snapshot with PowerShell____

    Hello Jeffery under this I tried to take a snapshot using the given powershell commands but it saying about an error like this:

    $snapshot = Checkpoint-VM <<<< -Name $VMName -SnapshotName $SnapName -ComputerName $Server –passthru
    CategoryInfo : ObjectNotFound: (Checkpoint-VM:String) [], CommandNotFoundException
    FullyQualifiedErrorId : CommandNotFoundException

  • It seems to me based on the error that you are trying to Checkpoint a VM that doesn’t exist. I don’t know what you’ve put in $VMname. If you put in a valid VM name, you should be able to test it:

    Get-VM $VMName

    If that fails, check the value of $VMName.

    Usually I use Get-VM first to make sure I have the VM and then repeat piping to Checkpoint-VM

    get-vm “demo rig” | checkpoint-vm -SnapshotName “Test snapshot”

  • Disam says:

    Hello Jeffry, thankx for the help 🙂 I think the “Checkpoint-VM” is not working the existing powershell version(powershell v1.0) on my server. Could u please mention the powershell version that u used to run this powershell script or any other script that supports powershell v1.0.

    Thank You.. 🙂

  • Everything I was showing was using PowerShell 3.0 and the version of Hyper-V that shipped with Windows Server 2012/Windows 8. Now, you might be able to run the commands from a Windows 8 client with they Hyper-V feature from RSAT installed. What I don’t know is what version of Hyper-V needs to be running on the server. But if you are stuck running PowerShell 1.0 everywhere, you can’t really do anything.

  • tshk says:

    I want to export an specific snapshot, and create a new vm from that snapshot.
    The first issue is the exportation results in 3 files, one vhdx, and two avhdx.
    the second issue is, i just want to create a New-VM and point to that specific vhdx, but having those other two files (avhdx) i have the feeling that it will not work.

    • Zoran says:

      Hi Tshk,
      The avhdx disk is created by a Hyper-V differencing disk (see this Altaro blog for details: https://www.altaro.com/hyper-v/how-to-verify-storage-before-using-checkpoints-hyper-v/). A differencing disk is uses when you have a base VM (in a VHDX file) with the differences tracked in the AVHDX file.

      When you export the file, it must export both the base and differencing disk. You can go through an additional process of merging them if you desire so that you can work with a single disk file.

      You should be able to create a new VM assuming you have all the (A)VHDX files.

      Thanks,
      Symon Perriman
      Altaro Editor

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published.

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.