Now, I am not going to take any credit for this little and brilliant piece of PowerShell. All the credit goes to Taylor Brown.
Back many moon ago Taylor was publishing a bunch of PowerShell scripts that use the Hyper-V WMI interface to do things. (In more recent times Ben Armstrong has taken up this task)
Taylor’s original post is here: http://blogs.msdn.com/b/taylorb/archive/2008/06/07/hyper-v-wmi-cloning-virtual-machines-using-import-export.aspx
Did you notice that date? The year was 2008 on that original post.
This is actually a highly useful bit of code if you want to make bunches of VMs that are identical in settings. You create the first “MasterVM” – you could also call a template. Then copy it.
I did tweak Taylor’s PowerShell a bit, but only to remove an error that annoyed me with Write-Progress and to pre-pend and post-pend the VM names, the count to start at and the number to create.
I will add more to this later as this is just the beginning. After all of this creation of VMs there are other settings that can be applied and considerations that you must have in regards to the system as a whole.
What this script does is use the Hyper-V Export and Import feature. The Export creates a copy of the VM (and all of its associated physical objects) in a nice tidy folder in the path that is specified.
This target can be local storage or if your target Hyper-V server is a node of a cluster it can be a CSV. And the plus of using Export to copy is that the VM is all set to be Highly Available.
The Import takes advantage of Hyper-V doing all the work to generate the new virtual objects, apply security settings, modify any snapshots files, handle differencing disks, etc. Let the system do all that work so I don’t have to. After all, someone already went to all the trouble to build the method, and it gets tested.
Without further delay, here is my version of the script.
param
(
[string]$masterVm = $(Throw "MasterVM required"),
[string]$exportPath = $(Throw "Path required to the Hyper-V local storage where VMs will be created"),
[string]$namePrePend = $(Throw "A string to add to the front of the new vm name"),
[string]$namePostPend = $(Throw "A string to add to the end of the new vm name"),
[string]$hypervHost = $(Throw "The Hyper-V host where the template VM is registered and where VMs will be created"),
[int] $startAt = $(Throw "The number to begin the VM creation with (ie. 2 = start with VM 2)"),
[int]$numCopies = $(Throw "The number of VMs to create")
)
function ProcessWMIJob
{
# Is there a way to loop through this and have these process and manage multiple jobs asynchronously? The problem might be in overloading the storage layer.
param (
[System.Management.ManagementBaseObject]$Result
)
if ($Result.ReturnValue -eq 4096) {
$Job = [WMI]$Result.Job
while ($Job.JobState -eq 4) {
Write-Progress -Id 2 -ParentId 1 $Job.Caption -Status "Executing" -PercentComplete $Job.PercentComplete
Start-Sleep 1
$Job.PSBase.Get()
}
if ($Job.JobState -ne 7) {
Write-Error $Job.ErrorDescription
Throw $Job.ErrorDescription
}
}
elseif ($Result.ReturnValue -ne 0) {
Throw $Result.ReturnValue
}
Write-Progress -Id 2 -Status "Complete" -Completed $TRUE
}
#Main Script Body
"Creation script is starting at: "+(Get-Date -uFormat "%j, %T")
$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService -ComputerName $hypervHost
$SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='$masterVm'" -ComputerName $hypervHost
$a = $startAt
$arrNewVms = @()
write-progress -Id 1 "Cloning Vm's" -Status "Executing"
while ($a -lt ($startAt + $numCopies)) {
$tempVMName = ($namePrePend + $a.ToString("00000") + $namePostPend)
$tempVMName
# a simple test to see if the VM already exists and try the next
if ((Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='$tempVMName'" -ComputerName $hypervHost) -eq $NULL) {
$VMSettingData = Get-WmiObject -Namespace root\virtualization -Query "Associators of {$SourceVm} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState" -ComputerName $hypervHost
$VMSettingData.ElementName = $tempVMName
$Result = $VMManagementService.ModifyVirtualSystem($SourceVm, $VMSettingData.PSBase.GetText(1))
ProcessWMIJob $Result
$Result = $VMManagementService.ExportVirtualSystem($SourceVm, $TRUE, "$exportPath")
ProcessWMIJob $Result
$Result = $VMManagementService.ImportVirtualSystem("$exportPath\$tempVMName", $TRUE)
ProcessWMIJob $Result
$arrNewVms += $tempVMName
}
$a ++
}
write-progress -Id 1 "Cloning Vm's" -Completed $TRUE
$VMSettingData = Get-WmiObject -Namespace root\virtualization -Query "Associators of {$SourceVm} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState" -ComputerName $hypervHost
$VMSettingData.ElementName = $masterVm
$Result = $VMManagementService.ModifyVirtualSystem($SourceVm, $VMSettingData.PSBase.GetText(1))
ProcessWMIJob $Result
"The following were created:"
$arrNewVms