You can customize and use the following sample script to clone any number of virtual machines (VMs).

To copy and paste the script content without page breaks, use the HTML version of this topic, available from the Horizon 6 documentation page at https://www.vmware.com/support/pubs/view_pubs.html.

Script Input

This script reads one input file, which is described in Input File for the Sample PowerCLI Scripts to Deploy Linux Desktops. This script also interactively asks for the following information:

  • IP address of the vCenter Server

  • Administrator login name for the vCenter Server

  • Administrator password for the vCenter Server

  • Clone type, which can be linked or full

  • Whether to disable vSphere VM console

Script Content

<#
Create Clones from a Master VM

The Tool supports creation of Full clone and linked clone from Master VM.
The parent VM is required for the linked-clone to work and the parent VMs file cannot be renamed or moved.
#>
#------------------------- Functions -------------------------
function GetInput
{
    Param($prompt, $IsPassword = $false)
    $prompt = $prompt + ": "
    Write-Host $prompt -NoNewLine
    [Console]::ForegroundColor = "Blue"
    if ($IsPassword)
    {
        $input = Read-Host -AsSecureString
        $input = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($input))
    }
    else
    {
        $input = Read-Host
    }
    
    [Console]::ResetColor()
    return $input
}

function IsVMExists ()
{
    Param($VMExists)
	Write-Host "Checking if the VM $VMExists already Exists"
	[bool]$Exists = $false

	#Get all VMS and check if the VMs is already present in VC
	$listvm = Get-vm
	foreach ($lvm in $listvm)
	{
		if($VMExists -eq $lvm.Name )
		{
			$Exists = $true
		}
	}
	return $Exists
}
function Disable_VM_Console()
{
    Param($VMToDisableConsole)
    $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $extra = New-Object VMware.Vim.optionvalue
    $extra.Key="RemoteDisplay.maxConnections"
    $extra.Value="0"
    $vmConfigSpec.extraconfig += $extra
    $vm = Get-VM $VMToDisableConsole | Get-View
    $vm.ReconfigVM($vmConfigSpec)
}


function Delete_VM()
{
    Param($VMToDelete)
	Write-Host "Deleting VM $VMToDelete"
	Get-VM $VMToDelete | where { $_.PowerState –eq "PoweredOn" } | Stop-VM –confirm:$false
	Get-VM $VMToDelete | Remove-VM –DeleteFromDisk –confirm:$false
}

#------------------------- Main Script -------------------------

$vcAddress = GetInput -prompt "Your vCenter address" -IsPassword $false
$vcAdmin = GetInput -prompt "Your vCenter admin user name" -IsPassword $false
$vcPassword = GetInput -prompt "Your vCenter admin user password" -IsPassword $true
$cloneType = GetInput -prompt 'Clone Type ("linked" or "full")' -IsPassword $false
$disableVMConsole = GetInput -prompt 'Disable vSphere VM Console ("yes" or "no", recommend "yes")' -IsPassword $false
"-----------------------------------------------------"
$csvFile = '.\CloneVMs.csv'

# Check that user passed only linked or full clone
if (($CloneType.length >0) -and ($CloneType -ne "linked" -or $CloneType -ne "full"))
{
	write-host  -ForeGroundColor Red "Clone type supports only 'linked' or 'full' (case sensitive)"
	exit
}
if (($disableVMConsole.length >0) -and ($disableVMConsole -ne "yes" -or $disableVMConsole -ne "no"))
{
	write-host  -ForeGroundColor Red "Disable vSphere VM Console supports only 'yes' or 'no' (case sensitive)"
	exit
}

#check if file exists
if (!(Test-Path $csvFile))
{
	write-host  -ForeGroundColor Red "CSV File $CSVFile not found"
	exit
}

# Connect to the VC (Parameterize VC)
#Connect to vCenter
$VC_Conn_State = Connect-VIServer $vcAddress -user $vcAdmin -password $vcPassword
if([string]::IsNullOrEmpty($VC_Conn_State))
{
   Write-Host 'Exit since failed to login vCenter'
   exit
}
else
{
  Write-Host 'vCenter is connected'
}

#Read input CSV file
$csvData = Import-CSV $csvFile 
#$csvData = Import-CSV $csvFile -header("VMName","Parentvm","CustomSpec","Datastore","Host","FromSnapshot","DeleteIfPresent")
foreach ($line in $csvData)
{
    "`n-----------------------------------------------------"
    $VMName = $line.VMName
    write-host -ForeGroundColor Yellow "VM: $VMName`n"

    $destVMName=$line.VMName
    $srcVM = $line.Parentvm
	$cSpec = $line.CustomSpec
	$targetDSName = $line.Datastore
    $destHost = $line.Host
	$srcSnapshot = $line.FromSnapshot
	$deleteExisting = $line.DeleteIfPresent
	if (IsVMExists ($destVMName))
	{
		Write-Host "VM $destVMName Already Exists in VC $vcAddress"
		if($deleteExisting -eq "TRUE")
		{
			Delete_VM ($destVMName)
		}
		else
		{
			Write-Host "Skip clone for $destVMName"
            continue
		}
	}    
    $vm = get-vm $srcvm -ErrorAction Stop | get-view -ErrorAction Stop
	$cloneSpec = new-object VMware.VIM.VirtualMachineCloneSpec
	$cloneSpec.Location = new-object VMware.VIM.VirtualMachineRelocateSpec
	if ($CloneType -eq "linked")
	{
		$cloneSpec.Location.DiskMoveType = [VMware.VIM.VirtualMachineRelocateDiskMoveOptions]::createNewChildDiskBacking
	}    
	Write-Host "Using Datastore $targetDSName"
	$newDS = Get-Datastore $targetDSName | Get-View
	$CloneSpec.Location.Datastore =  $newDS.summary.Datastore
    Set-VM -vm $srcVM -snapshot (Get-Snapshot -vm $srcVM -Name $srcSnapshot) -confirm:$false
    $cloneSpec.Snapshot = $vm.Snapshot.CurrentSnapshot
	$cloneSpec.Location.Host = (get-vmhost -Name $destHost).Extensiondata.MoRef
	$CloneSpec.Location.Pool = (Get-ResourcePool -Name Resources -Location (Get-VMHost -Name $destHost)).Extensiondata.MoRef
    # Start the Clone task using the above parameters
	$task = $vm.CloneVM_Task($vm.parent, $destVMName, $cloneSpec)
    # Get the task object
	$task = Get-Task | where { $_.id -eq $task }
    #Wait for the taks to Complete
    Wait-Task -Task $task
    
    $newvm = Get-vm $destVMName
    $customSpec = Get-OSCustomizationSpec $cSpec
    Set-vm -OSCustomizationSpec $cSpec -vm $newvm -confirm:$false 
	if ($disableVMConsole -eq "yes")
	{
		Disable_VM_Console($destVMName)
	} 
    # Start the VM
	Start-VM $newvm
}
Disconnect-VIServer $vcAddress -Confirm:$false
exit

Script Execution

The following messages are from an execution of the script:

PowerCLI C:\scripts> .\CloneVMs.ps1
Your vCenter address: 10.117.44.17
Your vCenter admin user name: administrator
Your vCenter admin user password: *******
Clone Type<"linked" or "Full"> : linked
Disable vSphere VM Console ("yes" or "no", recommend "yes") : yes

The time that the cloning process takes depends on the number of desktop machines and can range from several minutes to a number of hours. To verify that the process is complete, from vSphere client, make sure that the last desktop virtual machine is powered on, has its own unique host name, and VMware Tools is running.