powershellobjectpowershell-remotingscriptblock

How to pass an Object in a ScriptBlock instead of a single variable?


I am trying to get a Powershell script working where one of my ScriptBlock commands requires an Object as a parameter, rather than a single variable. Example:

Invoke-Command -Computername "RemoteComputer" -Scriptblock {PSCommand -option $Object}

(and yes, I've tried $Using:Object too) It always gives me the following error: Cannot validate argument on parameter 'option'. The argument is null. Provide a valid value for the argument, and then try running the command again. Note I have made the commands and options generic to not "muddy the waters".

Is it even possible to pass an object inside a ScriptBlock, or am I out of luck?

I tried:

Invoke-Command -Computername "RemoteComputer" -Scriptblock {PSCommand -option $Object}

Was expecting a returned object, but got the following error: Cannot validate argument on parameter 'option'. The argument is null. Provide a valid value for the argument, and then try running the command again.


I am trying to script some remote Citrix Storefront actions.

$VP = $Store.VirtualPath
$StoreService = Invoke-Command -ComputerName $Storefront -ScriptBlock {Get-STFStoreService -VirtualPath $Using:VP}
$StoreStuff = Invoke-Command -ComputerName $Storefront -ScriptBlock {Get-STFStoreEnumerationOptions -StoreService $Using:StoreService}

The $StoreService part works and is populated as an object. But the next command seems to have trouble using that object.


Solution

  • Clearly the issue is serialization, when you pass an object to a remote scope either via $using: or via -ArgumentList, said object is serialized and de-serialized back when it reaches the remote scope, and due to this serialization, the object is just a copy, it no longer holds the same reference, this is very important when dealing with reference types, which $StoreService most likely is.

    From Deserialized objects:

    When you run remote commands that generate output, the command output is transmitted across the network back to the local computer.

    Since live .NET objects can't be transmitted over the network, the live objects are serialized or converted into XML representations of the object and its properties. PowerShell transmits the serialized object across the network.

    On the local computer, PowerShell receives the serialized object and deserializes it by converting the serialized object into a standard .NET object.

    However, the deserialized object isn't a live object. It's a snapshot of the object at the time of serialization. The deserialized object includes properties but no methods. You can use and manage these objects in PowerShell, including passing them in pipelines, displaying selected properties, and formatting them.

    The easiest workaround is just to do both operations in the first Invoke-Command call instead of 2 calls to the remote host:

    $VP = $Store.VirtualPath
    
    $result = Invoke-Command -ComputerName $Storefront -ScriptBlock {
        $storeService = Get-STFStoreService -VirtualPath $Using:VP
        $StoreStuff = Get-STFStoreEnumerationOptions -StoreService $storeService
    
        [pscustomobject]@{
            StoreService = $storeService
            StoreStuff   = $StoreStuff
        }
    }
    
    # then you will find both outputs via dot-notation
    $result.StoreService
    $result.StoreStuff