Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additions to the xMove script #24

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 151 additions & 45 deletions powershell/xMove-VM.ps1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<#
<#
.SYNOPSIS
This script demonstrates an xVC-vMotion where a running Virtual Machine
is live migrated between two vCenter Servers which are NOT part of the
Expand All @@ -11,7 +11,8 @@
.NOTES
File Name : xMove-VM.ps1
Author : William Lam - @lamw
Version : 1.0
Modified : Alex Thomson (disk spec components from Grzegorz Kulikowski)
Version : 1.1
.LINK
http://www.virtuallyghetto.com/2016/05/automating-cross-vcenter-vmotion-xvc-vmotion-between-the-same-different-sso-domain.html
.LINK
Expand All @@ -26,26 +27,34 @@

Function xMove-VM {
param(
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)
]
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$sourcevc,
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$destvc,
[Parameter(Mandatory=$true)]
[String]$vm,
[Parameter(Mandatory=$true)]
[String]$switchtype,
[Parameter(Mandatory=$false)]
[String]$switch,
[Parameter(Mandatory=$true)]
[String]$cluster,
[Parameter(Mandatory=$false)]
[String]$datastore,
[Parameter(Mandatory=$true)]
[String]$sourceVMHost,
[Parameter(Mandatory=$true)]
[String]$vmhost,
[Parameter(Mandatory=$false)]
[String]$vmnetworks
)

#Create Relocation Spec for use in the function
$spec = New-Object VMware.Vim.VirtualMachineRelocateSpec

# Retrieve Source VC SSL Thumbprint
$vcurl = "https://" + $destVC
add-type @"
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;

Expand All @@ -68,15 +77,58 @@ add-type @"

# Source VM to migrate
$vm_view = Get-View (Get-VM -Server $sourcevc -Name $vm) -Property Config.Hardware.Device
$src_vm_tags = @(get-TagAssignment -server $sourcevc -entity (Get-VM -Server $sourcevc -Name $vm))

# Dest Datastore to migrate VM to
$datastore_view = (Get-Datastore -Server $destVCConn -Name $datastore)

# Determine the destination datastore to migrate VM to
# If you passed a datastore string we will use it here instead of figuring out the current location of disks.
if($datastore -eq $null){
$datastore_view = (Get-VMHost -Server $destVC -Name $vmhost | Get-Datastore -Server $destVC -Name $datastore)
#Add the datastore to the relocation spec object
$spec.datastore = $datastore_view.Id
}
else{
# No datastore was passed so we need to build the move spec with the current disk information
$sourceDisks = $vm_view.Config.Hardware.Device | where {$_ -is [vmware.vim.virtualdisk]}
$VMXDestinationDisk = ($vm_view.Config.Hardware.Device | where {$_ -is [vmware.vim.virtualdisk]}) | where {$_.DeviceInfo.Label -eq "Hard disk 1"}
$numberOfDisks = @($sourceDisks).Length

#Convert source VC VMX datastore backing into target VC datastore backing
$sourceVMXBacking = "Datastore-"+$VMXDestinationDisk.Backing.Datastore.Value
$destVMXBacking = get-datastore -server $destVC (get-datastore -server $sourceVC -id $sourceVMXBacking | select name -ExpandProperty name -Unique) | select id -ExpandProperty id -Unique
$destVMXBacking = $destVMXBacking.substring(10)

$spec.datastore = New-Object VMware.Vim.ManagedObjectReference
$spec.datastore.type = “Datastore”
$spec.datastore.Value = $destVMXBacking

#Specs to make our disks stay where they are
$spec.disk = New-Object VMware.Vim.VirtualMachineRelocateSpecDiskLocator[] ($numberOfDisks)
$i=0
Foreach($disk in $sourceDisks) {
#Add the string "Datastore" before the ID to be able to search for the datastore by ID
$sourceDiskBacking = "Datastore-"+$disk.Backing.Datastore.Value

#Convert source VC datastore backing into target VC datastore backing
$destDiskBacking = get-datastore -server $destVC (get-datastore -server $sourceVC -id $sourceDiskBacking | select name -ExpandProperty name -Unique) | select id -ExpandProperty id -Unique

#Strip off the first "datastore" in the string
$destDiskBacking = $destDiskBacking.substring(10)

$spec.disk[$i] = New-Object VMware.Vim.VirtualMachineRelocateSpecDiskLocator
$spec.disk[$i].diskId = $disk.Key
$spec.disk[$i].datastore = New-Object VMware.Vim.ManagedObjectReference
$spec.disk[$i].datastore.type = “Datastore”
$spec.disk[$i].datastore.Value = $destDiskBacking
$i++
}
}

# Dest Cluster to migrate VM to
$cluster_view = (Get-Cluster -Server $destVCConn -Name $cluster)
$cluster_view = (Get-Cluster -Server $destVC -Name $cluster)

# Dest ESXi host to migrate VM to
$vmhost_view = (Get-VMHost -Server $destVCConn -Name $vmhost)
$vmhost_view = (Get-VMHost -Server $destVC -Name $vmhost)

# Find all Etherenet Devices for given VM which
# we will need to change its network at the destination
Expand All @@ -89,8 +141,6 @@ add-type @"
}

# Relocate Spec for Migration
$spec = New-Object VMware.Vim.VirtualMachineRelocateSpec
$spec.datastore = $datastore_view.Id
$spec.host = $vmhost_view.Id
$spec.pool = $cluster_view.ExtensionData.ResourcePool

Expand All @@ -112,12 +162,24 @@ add-type @"
$count = 0
if($switchtype -eq "vds") {
foreach ($vmNetworkAdapter in $vmNetworkAdapters) {
# New VM Network to assign vNIC
$vmnetworkname = ($vmnetworks -split ",")[$count]

# Extract Distributed Portgroup required info
$dvpg = Get-VDPortgroup -Server $destvc -Name $vmnetworkname
$vds_uuid = (Get-View $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid
if($vmnetworks -eq $null -and $switch -ne $null){
#Set VDS variable to the string for the destination switch
$sourceVDS = $switch
$sourceDVPG = Get-VDSwitch -server $sourceVC $switch | Get-VDPortgroup -server $sourceVC | where {$_.key -eq $vmNetworkAdapter.backing.port.portgroupkey}
}
elseif($switch -eq $null -and $vmnetworks -ne $null){
#Get current VDS name and set the portgroup to the passed in string
$sourceVDS = Get-VDSwitch -server $sourceVC | where {$_.key -eq $vmNetworkAdapter.backing.port.switchuuid}
$sourceDVPG = ($vmnetworks -split ",")[$count]
}
else{
# Extract Source VDS and Portgroup names
$sourceVDS = Get-VDSwitch -server $sourceVC | where {$_.key -eq $vmNetworkAdapter.backing.port.switchuuid}
$sourceDVPG = $sourceVDS | Get-VDPortgroup -server $sourceVC | where {$_.key -eq $vmNetworkAdapter.backing.port.portgroupkey}
}
#Get the destination switch information
$dvpg = Get-VDSwitch -server $destvc $sourceVDS.name | Get-VDPortgroup -Server $destvc -Name $sourceDVPG.name
$vds_uuid = (Get-View -server $destvc $dvpg.ExtensionData.Config.DistributedVirtualSwitch).Uuid
$dvpg_key = $dvpg.ExtensionData.Config.key

# Device Change spec for VSS portgroup
Expand All @@ -131,51 +193,95 @@ add-type @"
$spec.DeviceChange += $dev
$count++
}
} else {
}
else {
foreach ($vmNetworkAdapter in $vmNetworkAdapters) {
# New VM Network to assign vNIC
$vmnetworkname = ($vmnetworks -split ",")[$count]

if($vmnetworks -ne $null){
#Set Portgroup variable to the string for the destination portgroup
$sourcePG = ($vmnetworks -split ",")[$count]
}
else{
$sourcePG = Get-VirtualSwitch -server $sourceVC -vmhost $sourceVMHost | Get-VirtualPortgroup -server $sourceVC | where {$_.key -eq $vmNetworkAdapter.backing.devicename}
}

# Device Change spec for VSS portgroup
$dev = New-Object VMware.Vim.VirtualDeviceConfigSpec
$dev.Operation = "edit"
$dev.Device = $vmNetworkAdapter
$dev.device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo
$dev.device.backing.deviceName = $vmnetworkname
$dev.device.backing.deviceName = $sourcePG.name
$spec.DeviceChange += $dev
$count++
}
}
}
try{
Write-Host "`nMigrating $vmname from $sourceVC to $destVC ...`n"

Write-Host "`nMigrating $vmname from $sourceVC to $destVC ...`n"
# Issue Cross VC-vMotion
$task = $vm_view.RelocateVM_Task($spec,"defaultPriority")
$task1 = Get-Task -Id ("Task-$($task.value)") -server $sourceVC
$task1 | Wait-Task -Verbose

# Issue Cross VC-vMotion
$task = $vm_view.RelocateVM_Task($spec,"defaultPriority")
$task1 = Get-Task -Id ("Task-$($task.value)")
$task1 | Wait-Task -Verbose
if($src_vm_tags.count -gt 0){
Write-Host "`nAssigning tags to $vm on $destVC`n"
foreach ($tag in $src_vm_tags){
New-TagAssignment -tag (get-tag -Name $tag.tag.name -server $destVC) -entity (Get-VM -Server $destvc -Name $vm) -server $destVC
}
}
}
catch{
Write-Host "There was some error trying to submit the move task." -ForegroundColor Red
}
}

Set-StrictMode -version 2
$ErrorActionPreference = "Stop"

# Variables that must be defined

$vmname = "TinyVM-2"
$sourceVC = "vcenter60-1.primp-industries.com"
$sourceVCUsername = "administrator@vghetto.local"
$sourceVCPassword = "VMware1!"
$destVC = "vcenter60-3.primp-industries.com"
$destVCUsername = "administrator@vghetto.local"
$destVCpassword = "VMware1!"
$datastorename = "la-datastore1"
$clustername = "LA-Cluster"
$vmhostname = "vesxi60-5.primp-industries.com"
$vmnetworkname = "LA-VM-Network1,LA-VM-Network2"
$switchname = "LA-VDS"
$importedVMList = get-content C:\vms_to_move.csv
$sourceVC = "sourceVC"
$sourceVCUsername = "administrator@vsphere.local"
$sourceVCPassword = "password1"
$destVC = "destinationVC"
$destVCUsername = "administrator@vsphere.local"
$destVCPassword = "password2"
$destClusterName = "TargetDRSCluster"
#Set the switchname variable to $null if the destination switch uses the same name as the source switch. The script to determine the source switch name.
#Set the switchname variable to the target switch name if the source switch name and target switch name differ
$switch = $null
#Switch type should be set to either "vds" or "vss"
$switchtype = "vds"
#Set the vmnetworkname variable to $null if the destination portgroup uses the same name as the source portgroup. The script will determine the source portgroup name
#Set the vmnetworkname variable to the destination portgroup name if you want to set a destination network for ALL VMs. Be careful!
$vmnetworkname = $null
#Set the datastore variable to $null to allow the script to determine the VM's current disk location and to create the move spec.
#Set the datastore to a string to move all VMs in $importedVMList to that datastore
$datastore = $null

# Connect to Source/Destination vCenter Server
$sourceVCConn = Connect-VIServer -Server $sourceVC -user $sourceVCUsername -password $sourceVCPassword
$destVCConn = Connect-VIServer -Server $destVC -user $destVCUsername -password $destVCpassword

xMove-VM -sourcevc $sourceVCConn -destvc $destVCConn -VM $vmname -switchtype $switchtype -switch $switchname -cluster $clustername -vmhost $vmhostname -datastore $datastorename -vmnetwork $vmnetworkname

foreach ($vmname in $importedVMList){
Write-Host "`nNow evaluating $vmname to determine VM hardware version...`n" -ForegroundColor White
$vmobject = get-vm $vmname -server $sourceVC
$vmhostobject = get-cluster $destClusterName -server $destVC | get-vmhost | sort-object MemoryUsageGB | select -First 1
if($vmhostobject.MemoryUsageGB + $vmobject.MemoryGB -gt $vmhostobject.MemoryTotalGB){
Write-Error "`nCluster does not appear to have enough memory resources.`
Exiting script to allow for manual intervention.`n"
}
if($vmobject.version -eq "v4"){
Write-Error "`nVM is hardware version 4.`
There appears to be a bug that will successfully move the VM but upon `
power-cycle or vMotion on the target side will lose the assigned port-group. `
This is a sanity check to make sure you don't lose connection to your VM at `
a random time post-move!"
}
Write-Verbose "The destination host will be $($vmhostobject).name"
xMove-VM -sourcevc $sourceVCConn -destvc $destVCConn -VM $vmname -switchtype $switchtype -cluster $destClusterName -vmhost $vmhostobject.name -sourceVMHost [string]$vmobject.vmHost
}

# Disconnect from Source/Destination VC
Disconnect-VIServer -Server $sourceVCConn -Confirm:$false
Expand Down