I've built this script for a customer that required multiple simultaneous clonings that he could easily configure to back up his production VMs. I've included a mechanism to limit the maximum number of concurrent clonings to maximise performance, so when using this script it will take some experimentation to find the 'magic number' to minimise the time used to do a full clone run.
The attached ZIP file includes three files:
- ScriptClone.cfg - The config file, one VM per line, will be run in order from top to bottom
- ScriptClone.ps1 - The actual script, includes three extra settings for you to configure
- ScriptClone.bat - A batch file to run the script, change the paths to suit your system
The script will power down the target VM if it already exists, then delete it from disk and then clone the source to the target. The power down steps are included three times with a 20 second gap because experience has shown that powerdowns don't always go smoothly. Also after the deletion of the 'old' target VM there is a wait of 2 minutes to give vCenter a chance to clean it all up and update the inventory.
Here's the source of the script:
####################################################### # ScriptClone v1.0 # Scripted simultaneous cloning for VMWare vSphere 4 # by Mark Kathmann # http://www.kathmann.com ####################################################### # # Change these three settings: # # myVCServer = vCenter Server hostname or IP address # CFGFILE = Full path name for config file # LogFolder = Full path name for the log files (no trailing slash) # MaxConc = Max number of concurrent clones # ####################################################### $myVCServer = "127.0.0.1" $CFGFILE = "C:\ScriptClone\ScriptClone.cfg" $LogFolder = "C:\ScriptClone\log" $MaxConc = 3 ####################################################### # # Stop editing, the real work starts here # ####################################################### # Connect to the vCenter Server Connect-VIServer -Server $myVCServer -Protocol https # Read the config file $cfg = Get-Content $CFGFILE # Start an empty task table $taskTab = @{} # Cycle through the lines in the config file foreach ($line in $cfg) { # Ignore lines starting with # if ($line.substring(0,1) -ne "#") { # Split the line's arguments $b = $line.split(",") # Count the no. of arguments, if correct (>=7) then continue if ($b.Length -ge 7) { # Count the active tasks $runningTasks = $taskTab.Count # If no. of running tasks = max then wait and recheck every 15 seconds while ($runningTasks -eq $MaxConc) { # Read the active task statuses Get-Task | % { if($taskTab.ContainsKey($_.Id) -and $_.State -eq "Success") { $SC_Clonelog = $LogFolder+"\SCClone_"+$taskTab[http://$_.Id|http://$_.Id]+".log" "{0} Cloning process finished." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber " " | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber $taskTab.Remove($_.Id) $runningTasks-- } elseif($taskTab.ContainsKey($_.Id) -and $_.State -eq "Error") { $SC_Clonelog = $LogFolder+"\SCClone_"+$taskTab[http://$_.Id|http://$_.Id]+".log" "{0} Cloning process finished." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber " " | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber $taskTab.Remove($_.Id) $runningTasks-- } } # Wait 15 seconds before trying again Start-Sleep -Seconds 15 } # Set the log file Location $SC_Clonelog = $LogFolder+"\SCClone_"+$b[0]+".log" # Log the start of the cloning "{0} Beginning cloning process..." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber # Read the command arguments $sourceVM = $b[0] $destVmName = $b[1] $vCserver = $b[2] $destDatastore = $b[3] $destHost = $b[4] $resourcePool = $b[5] $vmFolder = $b[6] # Log the stopping of the target VM "{0} Stopping target VM..." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber # Stop the target VM (3 attempts + wait) Stop-VM -VM $destVmName -Confirm:$false sleep 20 Stop-VM -VM $destVmName -Confirm:$false sleep 20 Stop-VM -VM $destVmName -Confirm:$false sleep 20 # Log the deletion of the target VM "{0} Deleting target VM..." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber # Delete the target VM (+ wait) Remove-VM -VM $destVmName -DeleteFromDisk -Confirm:$false sleep 120 # Log the actual clone start "{0} Starting clone..." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber # Perform the cloning $taskTab[http://(New-VM -VMHost $destHost -Name $destVmName -ResourcePool (Get-ResourcePool $resourcePool) -Location $vmFolder -Datastore $destDatastore -VM (Get-VM $sourceVM) -RunAsync).Id|http://(New-VM -VMHost $destHost -Name $destVmName -ResourcePool (Get-ResourcePool $resourcePool) -Location $vmFolder -Datastore $destDatastore -VM (Get-VM $sourceVM) -RunAsync).Id] = $b[0] } } } # All lines in the config file are processed, finish up # Count the active tasks $runningTasks = $taskTab.Count # If no. of running tasks > 0 then wait and recheck every 15 seconds while ($runningTasks -gt 0) { # Read the active task statuses Get-Task | % { if($taskTab.ContainsKey($_.Id) -and $_.State -eq "Success") { $SC_Clonelog = $LogFolder+"\SCClone_"+$taskTab[http://$_.Id|http://$_.Id]+".log" "{0} Cloning process finished." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber " " | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber $taskTab.Remove($_.Id) $runningTasks-- } elseif($taskTab.ContainsKey($_.Id) -and $_.State -eq "Error") { $SC_Clonelog = $LogFolder+"\SCClone_"+$taskTab[http://$_.Id|http://$_.Id]+".log" "{0} Cloning process finished." -f [DateTime]::Now | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber " " | out-file $SC_CloneLog -append -encoding "ASCII" -force -NoClobber $taskTab.Remove($_.Id) $runningTasks-- } } # Wait 15 seconds before trying again Start-Sleep -Seconds 15 }
As with all programming this code is based on larger or smaller elements of the work of many others before me, too many to mention all here, but I am of course grateful for everyone's hard work.
I edited the .cfg file and added couple of VM names, one vm per line e.g.
test-vm01
test-vm02
and ran the .ps1 but getting this error, pl suggest :-
Exception calling "Substring" with "2" argument(s): "Index and length must
refer to a location within the string.
Parameter name: length"
At C:\Users\Vinod Sikka\Downloads\ScriptClone\ScriptClone\ScriptClone.ps1:41
char:6
+ if ($line.substring(0,1) -ne "#")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentOutOfRangeException
It looks like you might have an empty line in your cfg file
