VMware Cloud Community
Sfet
Contributor
Contributor

Clone OS disk only, script complete and working...

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"

0 Kudos
13 Replies
LucD
Leadership
Leadership

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

0 Kudos
Sfet
Contributor
Contributor

Shall get that sorted, thanks.

0 Kudos
davidlowen
Contributor
Contributor

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?

0 Kudos
davidlowen
Contributor
Contributor

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.

0 Kudos
jeremyahagan
Contributor
Contributor

Thanks for sharing. Did you ever get this working without the double-connect?

0 Kudos
LucD
Leadership
Leadership

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

0 Kudos
jeremyahagan
Contributor
Contributor

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.

0 Kudos
jeremyahagan
Contributor
Contributor

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'.

0 Kudos
LucD
Leadership
Leadership

Which Get-View is giving you the error ?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
jeremyahagan
Contributor
Contributor

Both

0 Kudos
LucD
Leadership
Leadership

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

0 Kudos
Sfet
Contributor
Contributor

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.

0 Kudos
jeremyahagan
Contributor
Contributor


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.

0 Kudos