powershellscopexenscriptblock

Why does an argument passed into a script block fail to work?


Full disclosure: My problem may be based on a incomplete understanding of the Citrix PowerShell module for Xen Desktop.

I have the following script block. It's called in a loop, once for each VM in a list. I'm using PowerShell Jobs because I want to keep the UI thread free to update the UI while the jobs run.

Code "A"

$j = Start-Job -Name $jobName -ScriptBlock {
    param($url, $uuid, $cred, $snapshotName)
    $ErrorActionPreference = "Stop" 
    try
    {
        $error.clear()
        $xSS = $cred | Connect-XenServer -url $url -NoWarnCertificates -SetDefaultSession -PassThru; 
        $vm = (Get-XenVM -SessionOpaqueRef $xss.opaque_ref -uuid $uuid)
        #Create snapshot
        Invoke-XenVM -Async -SessionOpaqueRef $xss.opaque_ref -VM $vm -XenAction Snapshot -NewName $snapshotName
        return "OK"
    }
    catch
    {
        return ("ERROR: "+$error)
    }
} -ArgumentList $global:configFileVmMetaData.poolUrl, $xenVm.key, $global:cred, $snapshotName

Code "A" works ok, but it takes longer then necessary because I'm executing the Connect-XenServer cmdlet each time I invoke the script block.

So, I tried calling Connect-XenServer once outside the loop and passing in the session variable as shown below in Code "B". The result was the error Could not find open sessions to any XenServers being thrown inside the script block. I'm assuming that the $xss session variable is getting whanged somehow when its passed into the script block.

Any ideas why $xss session variable is getting whanged?

Code "B"

$xSS = $cred | Connect-XenServer -url $global:configFileVmMetaData.poolUrl -NoWarnCertificates -SetDefaultSession -PassThru; 

loop
{

    $j = Start-Job -Name $jobName -ScriptBlock {
        param($xss, $uuid, $snapshotName)
        $ErrorActionPreference = "Stop" 
        try
        {
            $error.clear()
            $vm = (Get-XenVM -SessionOpaqueRef $xss.opaque_ref -uuid $uuid)
            #Create snapshot
            Invoke-XenVM -Async -SessionOpaqueRef $xss.opaque_ref -VM $vm -XenAction Snapshot -NewName $snapshotName
            return "OK"
        }
        catch
        {
            return ("ERROR: "+$error)
        }
    } -ArgumentList $xss, $xenVm.key, $snapshotName

}

Additional examples inspired by the Robert Cotterman answer

In all cases, I still get the Could not find open sessions to any XenServers error

FYI - Using PowerShell 5.1

Example using $using. Variable contents are passed in and back out as expected

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    return ($using:aLocal + " *** " + $using:bLocal)
    }

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

Output

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 45
Remove-Job

Example using args. Variable contents are passed in and back out as expected

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    return ($args[0] + " *** " + $args[1])
    } -ArgumentList ($aLocal, $bLocal)

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

Output

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 49

Example using named args. Variable contents are passed in and back out as expected

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    param($a, $b)
    return ($a + " *** " + $b)
    } -ArgumentList ($aLocal, $bLocal)

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

Output

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 55

Solution

  • I just don't think it works that way. The connection to the Xen server is created for your PowerShell session, with info about that connection collected in the $xss variable. Each job runs its own PowerShell session. So just passing $xss to the job isn't the same, it's just a description of the connection details.