VMware Cloud Community
cbuhl
Contributor
Contributor

vmotion (change compute, storage and network) script issues

Hello,

The purpose of this script is to vMotion all of the VMs in the input CSV ($MigrationCSVPath) to a new cluster, new datastore or datastore cluster, and also change from a virtual switch PG to a distributed switch port group with the same name.

I'm having an issue with this script where it doesn't seem to work for every VM. The script will work for some VM's but not for others and I'm not sure why. The input file for the list of VM's to be migrated looks like this:

#TYPE Selected.VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl

"Name","Id","Folder"

"servername","VirtualMachine-vm-36942","Folder3"

The error that I receive is:

Move-VM Object reference not set to an instance of an object.

At C:\MigrateScript.ps1:61 char:5

Move-VM -VM (Get-VM -Id $VM.Id) -destination $DestinationCluster  ..

+ CategoryInfo: NotSpecified: (:) [Move-VM], VimException

+ FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.MoveVM

I suspect it has something to do with the network adapters but haven't been able to determine a solution yet.

Param (

    [parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage='Type vCenter server IP or FQDN you want to connect')]

    [alias('vc')]

    [String]$vCenter,

    [parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, HelpMessage='Type Destination Cluster')]

    [alias('c')]

    [String]$DestinationCluster,

    [parameter(Position=2, Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage='Type name of DS_DSC')]

    [alias('ds')]

    [String]$DestinationDS_DSC,

    [parameter(Position=3, Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage='Type maximum number of parallel vmotions to allow')]

    [alias('mpv')]

    [String]$MaxParallelvMotion,

    [parameter(Position=4, Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage='Path to CSV Containing VM Names to Migrate')]

    [alias('mcv')]

    [String]$MigrationCSVPath,

    [parameter(Position=5, Mandatory=$true, ValueFromPipelineByPropertyName=$true, HelpMessage='Type target  virtual switch  name')]

    [alias('dvs')]

    [String]$TargetSwitch

)

Process {

    if ($global:DefaultVIServers.Name -notcontains $vCenter) {

        try {

            Connect-VIServer $vCenter -ErrorAction Stop

        }

        catch {

            Write-Host $($Error[0].Exception) -ForegroundColor Red

            break

        }

    }

    try {

        $ClusterInfo = Get-Cluster $DestinationCluster -ErrorAction Stop

       

        if(Get-Datastore $DestinationDS_DSC){

        $Datastore = Get-Datastore $DestinationDS_DSC

        }

        else{

        $Datastore = Get-DatastoreCluster $DestinationDS_DSC

        }

    }

    catch {

        Write-Host $($Error[0].Exception) -ForegroundColor Red

        break

    }

    #CSV requires one column with label "Name".

    $VMsToRelocate = Import-Csv -path $MigrationCSVPath

   

    foreach ($VM in $VMsToRelocate)

    {

        $networkadapters = Get-NetworkAdapter -VM $VM.Name

        $destinationPortGroup = Get-VDPortgroup  -VDSwitch $TargetSwitch -Name $networkadapters.NetworkName

        $networkadapters | Format-Table -AutoSize

        $destinationPortGroup | Format-Table -AutoSize

    Write-Host "Relocating VM:" $VM.Name "to" $DestinationDS_DSC

    Move-VM -VM (Get-VM -Id $VM.Id) -destination $DestinationCluster -NetworkAdapter $networkadapters -PortGroup $destinationPortGroup -datastore $Datastore  -RunAsync

   

    do

    {

        sleep 5

    } while((Get-Task -Status Running | where{$_.Name -eq 'RelocateVM_Task'}).Count -gt $MaxParallelvMotion)

    } #end of foreach

Tags (1)
0 Kudos
14 Replies
LucD
Leadership
Leadership

You didn't specify which vSphere version and which PowerCLI version you are using, but it could be something like in Re: Move-VM doesn't work against new VCSA 6.7 instance

The solution is to use the API method.


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

0 Kudos
cbuhl
Contributor
Contributor

Hi LucD,

Sorry, the version is 11.3.0 build 13990089.

That certainly looks to be the same issue as I am having. I modified the script to try using the API method, with the following changes:

        $spec = New-Object VMware.Vim.VirtualMachineRelocateSpec

        $spec.network = $destinationPortGroup.ExtensionData.MoRef

        $spec.datastore = $Datastore.ExtensionData.MoRef

        $spec.Pool = $destinationRG.ExtensionData.MoRef

       

      $vm.ExtensionData.RelocateVM($spec, [VMware.Vim.VirtualMachineMovePriority]::defaultPriority)

It results in the error "The property 'network' cannot be found on this object. Verify that the property exists and can be set.

It seems to be having troubles with the network still. The value of $destinationPortGroup appears to have a valid value (it's not blank).

0 Kudos
cbuhl
Contributor
Contributor

I see that the $spec variable is created with 11 fields:

Service      :
Folder       :
Datastore    :
DiskMoveType :
Pool         :
Host         :
Disk         :
Transform    :
DeviceChange :
Profile      :
LinkedView   :

It doesn't seem to have a network specification which is why it is supplying the error. Is there a way to add that field?

0 Kudos
LucD
Leadership
Leadership

Yes, you have to use the DeviceChange property to change the vNIC PGs.
Something like this

$vmName = 'MyVM'

$rpName = 'MyRP'

$vdPGName = 'MyPG'


$vm = Get-VM -Name $vmName

$rp = Get-ResourcePool -Name $rpName

$vdPg = Get-VDPortgroup -Name $vdPGName


$spec = New-Object VMware.Vim.VirtualMachineRelocateSpec

$spec.Pool = $rp.ExtensionData.MoRef

$spec.datastore = $Datastore.ExtensionData.MoRef

$vm.ExtensionData.Config.Hardware.Device | where{$_ -is [VMware.Vim.VirtualEthernetCard]} | %{

    $dev = New-Object VMware.Vim.VirtualDeviceConfigSpec

    $dev.Operation = "edit"

    $dev.Device = $_

    $dev.device.Backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo

    $dev.device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection

    $dev.device.backing.port.switchUuid = $vdPg.VDSwitch.ExtensionData.Uuid

    $dev.device.backing.port.portgroupKey = $vdPg.ExtensionData.Config.key


    $spec.DeviceChange += $dev

}


$vm.ExtensionData.RelocateVM($spec, [VMware.Vim.VirtualMachineMovePriority]::defaultPriority)


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

0 Kudos
cbuhl
Contributor
Contributor

I tested out the deviceChange but I'm getting some errors. Would this script also handle a scenario where there are multiple adapters present on a VM?

pastedImage_0.png

0 Kudos
LucD
Leadership
Leadership

Yes, the script runs through all vNIC attached to the VM.

But I noticed from the 1st error that it says

    $dev.Device = $vdPg.ExtensionData

while that should be

    $dev.Device = $_

That was an error I corrected immediately after posting my reply, but you might have copied before my correction was in.


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

0 Kudos
cbuhl
Contributor
Contributor

Yep, I copied before the update. It's looking really close.

pastedImage_1.png

I modified the Get-ResourcePool to $destinationRG = (Get-Cluster $destinationCluster | Get-ResourcePool -Name Resources) since I have multiple clusters all with the same RP "Resources".

The cluster I am running this against only has one resource pool, but it seems to think there is multiple values being provided.

pastedImage_2.png

I adjusted the $spec.Pool = ($destinationRG.ExtensionData.MoRef | Select Value) to try and only grab the "Value" from the MoRef output but that hasn't worked.

When the script runs I made it output the value of $spec.Pool and this is what I see. For some reason its keeping "Type" inside of the variable even though its blank. I think that might be the issue.

pastedImage_3.png

0 Kudos
LucD
Leadership
Leadership

A MoRef is always an object with 2 properties, Type and Value.
You need to pass the complete object

$spec.Pool = $rp.ExtensionData.MoRef

If you have more than 1 resourcepool in $rp after the Get-ResourcePool, you will have to select 1.

Or you can make your selection more specific.

$rp = Get-ResourcePool -Name $rpName -Location (Get-Cluster -Name NewCluster)


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

0 Kudos
cbuhl
Contributor
Contributor

Ok, that makes sense. It doesn't like something with spec.Pool though. The output at the top shows whats in the $spec.Pool variable being supplied. It only contains one RG so it should be correct then?

pastedImage_0.png

0 Kudos
LucD
Leadership
Leadership

Is your new cluster in the same datacenter as the old one, or is it in a new datacenter?
If your cluster is in the same datacenter and the VM is in the pool named Resources on the old cluster, you can even leave the Pool property out.


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

0 Kudos
cbuhl
Contributor
Contributor

Same datacenter, but it needs to go to a new cluster. I thought the resource pool field would help specify which cluster the VM would be moving to.

I was able to correct it using the following syntax: $spec.Pool = $DestinationCluster.ExtensionData.ResourcePool

but for some reason it complains that the VM is not changing clusters.

pastedImage_0.png

0 Kudos
LucD
Leadership
Leadership

Is DRS enabled on the target cluster?

If not, you will also need to fill the $spec.Host property with a MoRef from one of the ESXi nodes in the target cluster.


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

0 Kudos
cbuhl
Contributor
Contributor

Yes, DRS is enabled on both source and target clusters.

pastedImage_0.png

0 Kudos
LucD
Leadership
Leadership

Can you check in the vpxd log to see if there are any further details why the vMotion fails?


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

0 Kudos