Cross-vCentre vMotions

There has been lot's of "v" going on at my work of late, specifically vMotions. If you're like me you like to do things as tidily as possible and often when you take over a virtual infrastructure there is only so much patch/repair work you can do before you decide that a clean slate is the best way to move on.

However this can be painful as a migration plan. Short of disconnecting and connecting hosts between the vCenter servers you're unlikely to come up with many friendly ways of moving VMs unless they are in the same SSO domain. This is where the function script below comes in and allows you to queue up vMotions using the vCenters "initiate vMotion receive" API call.

Please note that after the script was written some months ago I have since learned of similar efforts from William Lam and VMware Labs cross-vcenter workload migraiton utility 

The main complex portion of the script below is the target VC API call:

$EndpointRequest = [System.Net.Webrequest]::Create(('https://' + $DestinationVC))
$DestinationVCThumbprint = ($EndpointRequest.ServicePoint.Certificate.GetCertHashString()) -Replace '(..(?!$))','$1:'
$DestinationVCService = New-Object VMware.Vim.ServiceLocator
$ServiceCredential = New-Object VMware.Vim.ServiceLocatorNamePassword
$ServiceCredential.username = $AdminCredentials.UserName
$ServiceCredential.password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($AdminCredentials.Password))
$DestinationVCService.credential = $ServiceCredential
$DestinationVCService.instanceUuid = $DestinationVCConnection.InstanceUuid
$DestinationVCService.sslThumbprint = $DestinationVCThumbprint
$DestinationVCService.url = ('https://' + $DestinationVC)
$RelocateSpecification.service = $DestinationVCService

This segment is effectively a web request to the destination VC to retrieve its certificate thumbprint and then a vimAutomation ServiceLocator object populated with the required information to target the VM relocation specification object defined later in the script.

 

Function time:

Param
   ([Parameter(Mandatory=$True)]$VMName = '',
    [Parameter(Mandatory=$True)]$DatastoreName = '',
    [Parameter(Mandatory=$True)]$Cluster = '',
    $VMHostName = '',
    [Parameter(Mandatory=$True)]$VMNetworkName = '',
    [Parameter(Mandatory=$True)]$FolderName = '',
    $SourceVC = 'oldvcname',
    $DestinationVC = 'newvcname',
    $SwitchType = 'DVS',
    [Parameter(Mandatory=$True)][System.Management.Automation.CredentialAttribute()]$AdminCredentials = ''
   )

#Import those modules you need
Import-Module VMware.VimAutomation.core,VMware.VimAutomation.Vds

#Connect to source and destination VC
$SourceVCConnection = Connect-VIServer -Server $SourceVC -Credential $AdminCredentials
$DestinationVCConnection = Connect-VIServer -Server $DestinationVC -Credential $AdminCredentials

#Source VM View
$VMView = Get-View (Get-VM -Server $SourceVCConnection -Name $VMName) -Property Config.Hardware.Device

#Relocate Specification
$RelocateSpecification = New-Object VMware.Vim.VirtualMachineRelocateSpec
#Choose a host or select from target cluster
If ($VMHostName -ne '')
   {$RelocateSpecification.host = (Get-VMHost -Server $DestinationVCConnection -Name $VMHostName).Id}
Else
   {$RelocateSpecification.host = (Get-VMHost -Server $DestinationVCConnection -Location (Get-Cluster -Name $Cluster) | Where-Object {$_.ConnectionState -eq 'Connected' -and $_.PowerState -eq 'PoweredOn'})[0].Id}
Try 
   {$DestinationResourcePool = (Get-Cluster -Server $DestinationVCConnection -Name $Cluster -ErrorAction Stop).ExtensionData.resourcePool}
Catch
   {$DestinationResourcePool = (Get-ResourcePool -Server $DestinationVCConnection -Name $Cluster).ExtensionData.MoRef}
$RelocateSpecification.pool = $DestinationResourcePool
$RelocateSpecification.datastore = (Get-Datastore -Server $DestinationVCConnection -Name $DatastoreName).Id
$RelocateSpecification.Folder = (Get-Folder -Name $FolderName -Server $DestinationVC -Type VM).Id
    
#Service Endpoint
$EndpointRequest = [System.Net.Webrequest]::Create(('https://' + $DestinationVC))
$DestinationVCThumbprint = ($EndpointRequest.ServicePoint.Certificate.GetCertHashString()) -Replace '(..(?!$))','$1:'
$DestinationVCService = New-Object VMware.Vim.ServiceLocator
$ServiceCredential = New-Object VMware.Vim.ServiceLocatorNamePassword
$ServiceCredential.username = $AdminCredentials.UserName
$ServiceCredential.password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($AdminCredentials.Password))
$DestinationVCService.credential = $ServiceCredential
$DestinationVCService.instanceUuid = $DestinationVCConnection.InstanceUuid
$DestinationVCService.sslThumbprint = $DestinationVCThumbprint
$DestinationVCService.url = ('https://' + $DestinationVC)
$RelocateSpecification.service = $DestinationVCService
    

$VMNetworkAdapters = @()
$VMDevices = $VMView.Config.Hardware.Device
foreach ($VMDevice in $VMDevices) {if($VMDevice -is [VMware.Vim.VirtualEthernetCard]) {$VMNetworkAdapters += $VMDevice}}

$Count = 0
ForEach ($VMNetworkAdapter in $VMNetworkAdapters)
   {$VMNetworkName = ($VMNetworkName -split ',')[$Count]
    $DeviceSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $DeviceSpec.Operation = 'edit'
    $DeviceSpec.Device = $VMNetworkAdapter
    Switch ($SwitchType)
       {'DVS'
           {$DVSPortGroup = Get-VDPortgroup -Server $DestinationVC -Name $VMNetworkName
            $DVSUUID = (Get-View $DVSPortGroup.ExtensionData.Config.DistributedVirtualSwitch).Uuid
            $DVSPortGroupKey = $DVSPortGroup.ExtensionData.Config.key
            $DeviceSpec.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo
            $DeviceSpec.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection
            $DeviceSpec.device.backing.port.switchUuid = $DVSUUID
            $DeviceSpec.device.backing.port.portgroupKey = $DVSPortGroupKey
           }
        Default
           {$DeviceSpec.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo
            $DeviceSpec.device.backing.deviceName = $VMNetworkName
           }
       }
    $RelocateSpecification.DeviceChange += $DeviceSpec
    $Count++
   }

Write-Host ('Migrating ' + $VMName + ' from ' + $SourceVCConnection.Name + '\' + (Get-Datastore -Id (Get-VM -Server $SourceVCConnection -Name $VMName).DatastoreIdList).Name  + ' to ' + $DestinationVCConnection.Name)
$VMView.RelocateVM_Task($RelocateSpecification,'defaultPriority') | Out-Null


#Disconnect from source and destination VC
Disconnect-VIServer -Server $SourceVCConnection -Confirm:$False
Disconnect-VIServer -Server $DestinationVCConnection -Confirm:$False

Running this spits out a very simple migrating message followed by a "relocate virtual machine" task on the source host and, after that task hits 40%, an "initiate vMotion receive operation" task on the destination host. The source datastore is in there for good measure as I used one large datastore that was not the final destination in my migration.

Hope this helps those of you starting afresh.

o7

vSphere IPMI Information

First of all thank you for visiting my new Blog. As my first post I wanted to do something useful (most things I think up while doodling are a bit silly for a first post).

A recent issue at work showed that there weren't any iLO addresses in vSphere. The IPMI/iLO settings in vSphere allow hosts to be powered on from the vsphere client either manually or automatically as part of DPM. With a few hundred hosts to manage I couldn't really stomach entering the information manually so cue the HPE iLO cmdlets!

For this script you will need:

  1. VMware PowerCLI 6 or higher (6.5r1 can be found here)
  2. HPE iLO cmdlets (v2.0 can be found here)

The first important thing to get out of the way is working with your naming convention. You'll need a solid host to iLO convention or it won't be all that easy to implement. In my case this is already a thing, for the most part, and it's a simple letter swap. Some customers I've worked at use the iLO-<hostname> convention too.

Next is the fun to be had with iLO cmdlets and certificates. You can disable the certificate authentication with a trigger now so it's not too much trouble. I intend on doing a post about auto-enrolling the iLO's for CA certificates soon.

Lastly is knowing about the unique way VMware like to make cmdlets. For reasons unknown to science, they don't expose all attributes from the Get- command like some and don't even allow you to ask for all attributes with a trigger (Active Directory cmdlets for example). To be completely different their extended object attributes and functions are found after performing a Get-View which is used below to expose the function to update IPMI information. It's good to play with Get-View to see what it can do for you as the options are often poorly documented but powerful.

On with the function:

#Modules
Import-Module vmware.vimautomation.core,HPEiLOcmdlets

#Connect to vsphere
Connect-VIServer '<your vcenter here>'

#credentials for connecting and vsphere (domain accounts work too <NETBIOS>\<username>)
$iLOUN = Read-Host -Prompt 'iLO Username:'
$iLOPW = Read-Host -AsSecureString -Prompt 'iLO Password:'

Function Update-IPMIInfo ($VMHostName)
   {$VMHost = Get-VMHost $VMHostName
    #Show what's already there if anything
    If ($VMhost.ExtensionData.Config.Ipmi -ne $Null) {Write-Host $VMhost.ExtensionData.Config.Ipmi.BmcIpAddress -ForegroundColor Magenta}

    #Do your own Naming convention thing here as mine probably wont work for you! Perhaps: ('ILO-' + $VMHost.Name)
    $iLOName = $VMhost.Name.Replace("s","r")

    #Grab the data from iLO
    $iLOConnection = Connect-HPEiLO -IP $iLOName -Credential (New-Object System.Management.Automation.PSCredential ($iLOUN,$iLOPW)) #-DisableCertificateAuthentication
    $iLONet = Get-HPEiLOIPv4NetworkSetting -Connection $iLOConnection
    
    #Create and fill in the IPMI data
    $IpmiInfo = New-Object Vmware.Vim.HostIpmiInfo
    $IpmiInfo.BmcIpAddress = $iLONet.IPv4Address
    $IpmiInfo.BmcMacAddress = $iLONet.MACAddress
    $IpmiInfo.Login = $iLOUN
    $IpmiInfo.Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($iLOPW))

    #Finally update the host
    $VMHostView = Get-View $VMHost
    $VMHostView.UpdateIpmi($IpmiInfo)

    #Refresh the variable and show the new IPMI data
    $VMHost = Get-VMHost $VMHost.Name
    $VMhost.ExtensionData.Config.Ipmi
   }

#Single example
Update-IPMIInfo '<hostname as it appears in vsphere>'

#Numbered hosts example (updates Host01,Host02 and Host03)
0..3 | % {Update-IPMIInfo ('Host' + ("{0:D2}" -f $_))}

#Whole of vSphere example
Get-VMHost | % {Update-IPMIInfo $_.Name}

 I hope this helps out some people or sparks ideas in others. I'd like to hear from you either way so please do comment below.

o7