Hi guys, after some painful research and testing, I've finally finished a test release of a script that will clone a hot VM's OS disk whilst ignoring the other disks. It seems that defining a VM configuration and discarding the disks, still copies the disks, even in the latest PowerCLI version, so I took a different approach.
Many thanks to Lucd for his examples on other posts and some support feedback from Alan regarding the usage of the CopyVirtualDisk_Task call - which was once experimental.
The script is to be run within our live environment where we have DB boxes assorted into certain clusters and very strict naming conventions, these servers have -DB at the end of their inventory names and the datastores match these names with _OS appended to the end of them, hence the filters within the script that allows you to filter by host, VM, cluster and even datacentre.
Thus far it only throws one error as it tries to connect twice to the same VC, otherwise, no errors if everything is fine.
Note that to use the VDM stuff you need to be connected to the host, hence the many nested loops and strange logic.
Any and all input appreciated. Cheers :smileyplain:
##################################################
#Script will clone VM OS disks into root datastore
##################################################
Clear-Host
$VCIP = "X.X.X.X" #IP OF CENTRAL VC
$VCUID = "VCUID" #USERNAME FOR LOGIN TO VC
$VCKEY = "VCPASS" #PASSWORD FOR ABOVE VC USERNAME
$HUID = "HUID" #USERNAME FOR LOGIN TO HOST
$HKEY = "HPASS" #PASSWORD FOR ABOVE HOST USERNAME
Connect-VIServer -Server $VCIP -User $VCUID -Password $VCKEY
$DC = Get-Datacenter -Name "LIVEPRIMARY"
$CLUSTERS = Get-Cluster | Sort Name
foreach($C in $CLUSTERS)
{
$ClusterName = $C.Name
if ($ClusterName -like "*DBCLUS*")
{
Write-Host "Working on cluster:"
Write-Host $ClusterName
Connect-VIServer -Server $VCIP -User $VCUID -Password $VCKEY
$HOSTS = $C | Get-VMHost | Sort Name
Disconnect-VIServer -Server $VCIP -Force:$true -Confirm:$false
foreach($H in $HOSTS)
{
If ($H -like "*")
{
Write-Host "Connected to host:"
Write-Host $H
Connect-VIServer -Server $H.Name -User $HUID -Password $HKEY
$VMS = Get-VM | Sort Name
foreach ($VM in $VMS)
{
$srcDC = (Get-View (($VM | Get-Datacenter))).moref
$VMname = $VM.Name
If ($VMname -like "*-DB*")
{
$SI = Get-View ServiceInstance
$VDM = Get-View $SI.Content.VirtualDiskManager
$snapName = "TEMPCLONESNAP"
$targetFileName = $VMname + "_CLONE.vmdk"
$DSname = $VMname.Split("-")[0] + "_OS"
$DS = Get-Datastore -Name $DSname
Write-Host "Working on VM:"
Write-Host $VMname
$VM | New-Snapshot -Memory:$false -Quiesce:$false -Confirm:$false -Name $snapName
$snapDisks = (($VM | Get-Snapshot -Name $snapName) | Get-HardDisk)
foreach ($HD in $snapDisks)
{
if ($HD.Name -eq "Hard disk 1")
{
$srcDS = $HD.Filename
$destDS = "[$DSname] $targetFileName"
$destSpec = New-Object VMware.Vim.VirtualDiskSpec
$destSpec.adapterType = "lsiLogic"
$destSpec.diskType = "thin"
$force = $false
$taskMoRef = $VDM.DeleteVirtualDisk_Task($destDS, $srcDC)
$taskMoRef = $VDM.CopyVirtualDisk_Task($srcDS, $srcDC, $destDS, $srcDC, $destSpec, $force)
$task = Get-View $taskMoRef
$info = Get-Task | Where {$_.id -like "*-" + $task.info.key}
while ($task.Info.State -eq "running" -or $task.Info.State -eq "queued")
{
$info = Get-Task | Where {$_.id -like "*-" + $task.info.key}
$task = Get-View $taskMoRef
Write-Host "Clone task" $task.info.state ":" $info.PercentComplete "% complete."
Sleep 15
}
Get-VM $VM | Get-Snapshot -Name $snapName| Remove-Snapshot -Confirm:$false
break
}
}
}
}
}
Disconnect-VIServer -Server $H.Name -Force:$true -Confirm:$false
}
}
}
Write-Host "SCRIPT COMPLETE"
Great script, thanks for sharing.
On the double connect, you could test the contents of $defaultviservers and check if the server you want to connect to is already in there.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Shall get that sorted, thanks.
If I follow this right, you are left with .vmdk only (OS DISK), yes? Do you then copy the vmx config file in a separate workflow? I guess what I'm asking what do you do with the OS vmdk disks when this finishes?
I modified this a bit to hotclone a VM with RDM attached which I didn't want to take.
Changed this line: if ($HD.Name -eq "Hard disk 1") to this: "if ($HD.DiskType -eq "Flat")"
Ran beautifully. Thank you for posting. This was a big help!
Also for anyone who just wants to test out the method - this works in virtual center too. If you issue a snapshot of the VM, browse to the vmdk files, pick the ones you want to copy and try to copy, it will copy. (If you try to copy w/o issuing a snapshot it will fail). Nice if you want to test it out, but obviously if you have more than one VM you'd want to script it out.
Thanks for sharing. Did you ever get this working without the double-connect?
You could do this without the many COnnect-VIServer lines by using "multiple" mode (see Set-PowerCLICOnfiguartion).
But that would mean that you need to add the Server parameter on most of the PowerCLI cmdlets in the script.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Thanks, I'll give it a go. Having to use the -Server parameter is no problem. The biggest issue I have is having to use local credentials.
Could you please help me out with how this will work? So far I have the following:
add-pssnapin VMware.VimAutomation.Core
Set-PowerCLIConfiguration -ProxyPolicy NoProxy -DefaultVIServerMode Multiple -Confirm:$False
Connect-VIServer -Server vcenter #Uses the currently logged on credentials
$CurrentVM = Get-VM -Name "sourceVM"
$VMHost = $CurrentVM.VMHost.Name
$SrcDC = ($CurrentVM | Get-Datacenter | get-view).moref
$SI = Get-View ServiceInstance -Server $VMHost
$VDM = Get-View $SI.Content.VirtualDiskManager -Server $VMHost
But this errors with :Get-View Could not find VIServer with name 'FQDN of my ESXi host'.
Which Get-View is giving you the error ?
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Both
On the Server parameter you need to provide a VIServer object.
You can do this like this
Add-PSSnapin VMware.VimAutomation.Core
Set-PowerCLIConfiguration -ProxyPolicy NoProxy -DefaultVIServerMode Multiple -Confirm:$False
$srv = Connect-VIServer -Server vcenter #Uses the currently logged on credentials
$CurrentVM = Get-VM -Name "sourceVM"
$SrcDC = ($CurrentVM | Get-Datacenter | Get-View).moref
$SI = Get-View ServiceInstance -Server $srv
$VDM = Get-View $SI.Content.VirtualDiskManager -Server $srv
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi fellas, not been about here for a while due to secondment but now I'm back!
A recent act of lunacy in the office has seen our business move away from VMware to Hyper-V 2012, it's not exactly going to plan lol.
Double connect - just lazy coding, for simplicity :smileysilly:
Jeremy - I understand the local creds problem would come up at some point, there is no other way around this than to have a creds list, but remember that this is potentially dangerous.
Could not find VIServer with name 'FQDN of my ESXi host'.
Something possibly not right with the params as LucD is pointing out.
I which case it doesn't get around the multiple connect problem, it just means you don't need to disconnect before connecting directly to the target host to run the CopyVirtualDisk_Task and you can't connect to that host without local credentials.
I have a work around which is to pass the credentials in as a parameter in a scheduled task. This way they aren't sitting around in a text file that could be copied anywhere. I've also created a local account with limited permissions instead of using root.