Home > Automation, Hyper-V, Powershell, Virtualization, Win2012 > Clone VM on Win 2012 Hyper-V v3 when it is running using Powershell

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.. :-)

  1. September 19th, 2012 at 18:38 | #1

    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!

  2. Sebastian Haensch
    March 1st, 2013 at 10:15 | #2

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

    • Niklas
      March 1st, 2013 at 13:15 | #3

      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 :-)

  1. No trackbacks yet.