ssheikhgpo
Contributor
Contributor

Thank you for your quick help. I think I've got it working. It seems like when moving a VM to another site one must provide the ServiceLocator for the destination vCenter even if it the destination is a linked site. And sadly to create the ServiceLocator object one must provide it with the credentials to connect to that vCenter even if its already connected.   

Function that seems to be working for me now is:

function Create-VMMoveScheduledTask {
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
        $VM,

        [Alias("Folder")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Folder]
        $DestinationFolder,

        [Alias("VMHost")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]
        $DestinationHost,

        [Alias("Datastore")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]
        $DestinationDatastore,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        # Array of Hashes. Keys = MACAddress. Values = PortGroup (object).
        [ValidateScript({
            if ($_.keys | Where-Object {$_ -notmatch "^[a-fA-F0-9]{2}([:-]?[a-fA-F0-9]{2}){5}$"}) {return $false}
            if ($_.values | Where-Object {!($_ -is [VMware.VimAutomation.Vds.Types.V1.VDPortgroup])}) {return $false}
            return $true;
        })]
        $vNICPortGroups,

        [Alias("Name")]
        [Parameter(Mandatory = $true)]
        [String]
        $TaskName = "Create-VMMoveScheduledTask $(Get-Date -Format G)",      
        [Alias("Description")]

        [Parameter(Mandatory = $true)]
        [String]
        $TaskDescription = "Create-VMMoveScheduledTask $(Get-Date -Format G)",
        [Parameter(Mandatory = $true)]
        [ValidateScript({([DateTime]$_) -gt (([DateTime]::Now).AddSeconds(15)) -and ([DateTime]$_) -lt (([DateTime]::Now).AddDays(7))})]
        [DateTime]
        $StartTime,

        [Alias("Email")]
        [Parameter()]
        [ValidatePattern("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")]
        [string]
        $AlertEmail,

        [Alias("Credential")]
        [Parameter(Mandatory = $true)]
        [pscredential]
        $DestinationVCCredential
    )

    $SourceVC = $VM.Uid -replace "^.*@(.*):.*$",'$1'
    $DestinationVC = $DestinationHost.Uid -replace "^.*@(.*):.*$",'$1'
    $si = Get-View ServiceInstance -Server $SourceVC
    $scheduledTaskManager = Get-View $si.Content.ScheduledTaskManager -Server $SourceVC

    $vmView = $VM | Get-View

    # ScheduledTask specs
    $spec = New-Object VMware.Vim.ScheduledTaskSpec
    $spec.Name = $TaskName
    $spec.Description = $TaskDescription
    $spec.Enabled = $true
    if ($AlertEmail) {$spec.Notification = $AlertEmail}
    $spec.Scheduler = New-Object VMware.Vim.OnceTaskScheduler
    $spec.Scheduler.runat = $StartTime
    $spec.Action = New-Object VMware.Vim.MethodAction
    $spec.Action.Name = "RelocateVM_Task"

    # vMotion specs
    $arg1 = New-Object VMware.Vim.MethodActionArgument
    $arg1.Value = New-Object VMware.Vim.VirtualMachineRelocateSpec

    # ServiceLocator for destination - we should not need this ?
    $arg1.Value.service = New-Object VMware.Vim.ServiceLocator
    $arg1.Value.service.instanceUuid = ($global:defaultviservers | Where-Object {$DestinationHost.Uid -match  $_.Name}).InstanceUuid.toUpper()
    $ServiceCred = New-Object VMware.Vim.ServiceLocatorNamePassword
    $ServiceCred.username = $DestinationVCCredential.UserName
    $ServiceCred.password = $DestinationVCCredential.GetNetworkCredential().Password
    $arg1.Value.service.credential = $ServiceCred
    $arg1.Value.service.InstanceUuid = ($global:defaultviservers | Where-Object {$_.Name -like $DestinationVC}).InstanceUuid
    # $arg1.Value.service.sslThumbprint = $thumbprint
    $arg1.Value.service.url = ("https://" + $DestinationVC)

    # Destination Datastore
    $arg1.Value.datastore = $DestinationDatastore.Id

    # Destination Host
    $arg1.Value.host = $DestinationHost.Id

    # Destination ResourcePool
    if ($DestinationHost.ParentId -match "Cluster") {
        $arg1.Value.pool = ($DestinationHost | Get-Cluster).ExtensionData.ResourcePool
    }
    else {
        $arg1.Value.pool = $DestinationHost.ExtensionData.ResourcePool
    } #

    # Destination Folder
    $arg1.Value.folder = ($DestinationFolder | Get-View).MoRef

    # vNIC PortGroups
    $arg1.Value.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $VMNICs = $VM | Get-NetworkAdapter
    $VMNICs | ForEach-Object {$i = 0} {
        if ($i) {$arg1.Value.deviceChange += New-Object VMware.Vim.VirtualDeviceConfigSpec}
        $arg1.Value.deviceChange[$i].Operation = "edit"
        $arg1.Value.deviceChange[$i].device = $_.ExtensionData
        $arg1.Value.deviceChange[$i].device.backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo
        $arg1.Value.deviceChange[$i].device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection
        $arg1.Value.deviceChange[$i].device.backing.port.SwitchUuid = ($vNICPortGroups.($_.MacAddress) | Get-VDSwitch | Get-View).Uuid
        $arg1.Value.deviceChange[$i].device.backing.port.PortgroupKey = $vNICPortGroups.($_.MacAddress).Key
        $i++
    }

    $spec.Action.Argument += $arg1

    # Set vMotion priority
    $arg2 = New-Object VMware.Vim.MethodActionArgument
    $arg2.Value = [VMware.Vim.VirtualMachineMovePriority]"defaultPriority"

    $spec.Action.Argument += $arg2

    # Create scheduledTask
    return ($scheduledTaskManager.CreateScheduledTask($vmView.MoRef, $spec))
}

 

Reply
0 Kudos