powershellpowershell-remoting

Local statement output very different than invoke-command output


Logged in to the system directly, I run this statement, and get this output:

(Get-ClusterNetwork 'cluster backups').role
None

This is perfect... beautiful even, in it's simplicity.

However, when I run the exact same statement from a remote machine using invoke-command, which up until now i always just assumed was like typing this exact statement into the CLI of the machine, I get THIS output instead

Invoke-Command -Session $hi -ScriptBlock {(Get-ClusterNetwork 'cluster backups').role}
PSComputerName RunspaceId                           Value
-------------- ----------                           -----
dumdum a84b6c89-dumdum-80d3-ed43230ee8ab            None

Now here's the really funny thing. If i assign a variable to the invoke-command output, it'll have the same output shown above UNLESS - i pipe it to set-clipboard

So the variable

$hello = invoke-command -session $hi -scriptblock {(get-networkcluster 'cluster backups').role}

Now type $hello into prompt and I get:

PSComputerName RunspaceId                           Value
-------------- ----------                           -----
dumdum a84b6c89-dumdum-80d3-ed43230ee8ab            None

Which is expected. But now when I pipe that to set-clipboard and paste - the value is:

$hello | set-clipboard;
get-clipboard
None

Which is the actual value I want. Somehow piping to set-clipboard knows to only pull the property that i originally asked for. Even though the variable, has all the properties. When i run $hello.gettype() - i see the value as Int32. Which makes sense if $hello was only returning the value I wanted, but it's... not.

But if that wasn't weird enough - I'm running a few functions within the invoke-command, this is only one piece - all of the functions return a value i'm trying to report on. So:

$row = '' | select computername, ClusterNetworkRole, IP;
$row.computername = $name;
$row.clusternetworkrole = $hello;
$row.ip = dum.dum.dum.dum;
Return $row;

Do you know what the output of $row.clusternetworkrole is? Take a wild guess. It's every property EXCEPT the one I want.

$row
PSComputerName     : dumdum
RunspaceId         : b898bdad-dumdum-9eff-8a2beeefe78a
ClusterNetworkRole :
Computername       : dum
IP                 : dum.dum.dum.dum

Not only does it give me the exact properties i DON'T want - it actually adds those properties as members of $row.

$row.RunspaceID
b898bdad-dumdum-9eff-8a2beeefe78a

Now i can get the value i want by appending ".value" at the end of the statement, so this isn't so much a problem to be solved as much as it is a question of just what the hell powershell is doing. It's taken this simple, beautiful tiny statement - and wreaked havoc on my life.


Solution

  • In your specific case of an instance of an enum value (an instance of a System.Enum-derived type):

    Generally, you can exclude the unwanted properties as follows:

    Read on for why that is necessary.


    PowerShell's remoting infrastructure decorates every object returned from a remote invocation with the following NoteProperty ETS (Extended Type System) members:

    The PSComputerName and RunspaceId properties are useful in remoting commands that target multiple computers at once: given that the order in which output is received is not guaranteed, these properties tell you where a given output object originated from.

    The PSShowComputerName property allows you to control default display behavior - though, curiously, it has no effect on whether RunspaceId is displayed.

    The Value property for System.Enum-derived types compensates for the loss of type fidelity that typically occurs in remoting commands (and background jobs) - only a limited set of known types deserialize with type fidelity - see this answer.


    While these properties always exist, whether they show by default depends on the specific types of the object returned and either what formatting data is associated with them or applied by default by PowerShell.

    Also, they may show when you pipe to Format-* cmdlets explicitly, and during serialization, such as with ConvertTo-Json.