Clone VM on Win 2012 Hyper-V v3 when it is running using Powershell

I wanted to create a function that could help an IT Pro Admin with the task to create a clone of a running VM, Yes you can use the Export-VM cmdlet but then your VM must be turned off and in some cases, for example when you want to test a new release or patch on a production system but first test it in a safe environment and you are not allowed to stop the original VM.

The function exists in System Center Virtual Machine Manager but there it also must be turned off

So how do I do? I have made a PowerShell function that take a snapshot, copies the vhd files and creates a new VM, connect the vhd´s and network cards, configures the VM with number of processors, dynamic memory etc.

As the Snapshot merges the vhdx files when the VM is running in this new Hyper-V version I thought it was an sufficient way to solve that the VM actually was not writing to the .vhdx files when I copy them but into the .avhdx diff file.

This is a version 0.1 and yes it need some development but right now I do not have the time to make it supernice,

Here you can see it in action

And when It is finished It looks like this in the gui

And here is the PowerShell function

<#
.Synopsis
   Function to clone a running VM
.DESCRIPTION
   This function can be used to clone a running vm and connect the copied vhd´s and network
.EXAMPLE
   Clone-VM -VMName vmdisktest -VMCloneName vmdisktest-clone -Path c:\VMs
.EXAMPLE
   Clone-VM -VMName vmdisktest -VMCloneName vmdisktest-clone -Path c:\VMs -Switch Private
.Notes
Author: Niklas Akerlund
Date: 2012-06-19
#>
function Clone-VM
{
    [CmdletBinding()]
    [OutputType([int])]
    Param
    (
        # Name of the VM to be cloned
        [Parameter(Mandatory=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        $VMName,
        [string] $VMCloneName,
        # Where to store the VM-clone 
        [string] $Path,
        # Set to another network switch
        [string] $Switch = " "    
    )

    # Get VM
    $VM = Get-VM $VMName
    $VHDs = Get-VMHardDiskDrive -VM $VM
    #$VHDPath = (Get-VMHardDiskDrive -VM $VM).Path
    $VHDClonePath = "$Path\$VMCloneName\Virtual Hard Disks\"
    
    # Create a new array of VHD info
    $VHDOrg = @()
    foreach ($VHD in $VHDs){
        $data = New-Object PSObject -property @{
            VHDName = (Get-Item $VHD.Path).Name
            VHDPath = $VHD.Path
            ControllerType = $VHD.ControllerType
            ControllerNumber = $VHD.ControllerNumber
            ControllerLocation = $VHD.ControllerLocation

        }
        $VHDOrg +=$data
    }

    $VMNics = Get-VMNetworkAdapter -VM $VM

    # Take snapshot 
    $VM | Checkpoint-VM 

    New-Item -ItemType directory -Path $VHDClonePath

    New-VM -Name $VMCloneName -Path $Path -NoVHD -MemoryStartupBytes $VM.MemoryStartup -BootDevice IDE
    $VMClone = Get-VM -Name $VMCloneName
    Get-VMNetworkAdapter -VMName $VMCloneName | Remove-VMNetworkAdapter
   
    # Configure VM-Clon
    if ($VM.DynamicMemoryEnabled){
        Set-VM -VMName $VMCloneName -ProcessorCount $VM.ProcessorCount -DynamicMemory -MemoryMinimumBytes $VM.MemoryMinimum -MemoryMaximumBytes $VM.MemoryMaximum
    }else{
        Set-VM -VMName $VMCloneName -ProcessorCount $VM.ProcessorCount -StaticMemory
    }

    # Add all network cards
    foreach ($VMNic in $VMNics){
        if ($Switch -eq " "){
            Add-VMNetworkAdapter -VMName $VMCloneName -SwitchName $VMNic.SwitchName -IsLegacy $VMNic.IsLegacy
        }else{
            Add-VMNetworkAdapter -VMName $VMCloneName -SwitchName $Switch -IsLegacy $VMNic.IsLegacy
        }
    }

    # Copy all VHDs
    foreach ($VHDcopy in $VHDOrg){
        
        $Dest = $VHDClonePath + $VHDcopy.VHDName
        Copy-Item -Path $VHDcopy.VHDPath -Destination $Dest
        Add-VMHardDiskDrive -VMName $VMCloneName -ControllerType $VHDCopy.ControllerType -ControllerLocation $VHDCopy.ControllerLocation  -ControllerNumber $VHDCopy.ControllerNumber -Path $Dest   
    }

    # Remove snapshot 
    $VM | Remove-VMSnapshot

}

Good luck in testing, but do take in consideration that this is an copy of the running VM so do not start it at the same time on the same network or you will get IP collision etc, as you can see above I have added an -Switch parameter that you can use to set the network cards of the VM to be connected on another switch to avoid any problems.. 🙂

Comments

Christian Saborío
Reply

Great stuff, thanks for sharing! We’ll try and test the script when we get a chance and will post back any bugs or improvements we find, cheers!

Sebastian Haensch
Reply

you article doesn’t mention sysprep – don’t you sysprep your machines before cloning?

Niklas
Reply

When I do a clone it is the exact copy of a running machine so no sysprep as you want it to be the same 🙂

Stefano
Reply

Great job, thank you very much!

Steffen
Reply

Hello Niklas,
very cool script , but if I clone a GEN2 VM the script creates a GEN1 VM 🙂 . Thanks for your support, regards Steffen

Thom Schumacher
Reply

Nice script

Thom Schumacher
Reply

Niklas,

After using your function with hyper-v on my desktop with version 5.0 powershell. Discovered the following requires lines fixed issues I had when using your script again.

#requires -version 3.0
#requires -module hyper-v

the Hyper-v module being the most important.

Leave a comment

name*

email* (not published)

website