1

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.

Justin Beagley
  • 279
  • 2
  • 5
  • 14
  • 1
    the `Invoke-Command` cmdlet [when run with `-ComputerName` or `-Session`] will always add the following properties >>> `PSComputerName, PSShowComputerName, RunspaceId` <<< if you don't want them, then you will need to remove them. the simplest way is to pipe the object to >>> `Select-Object -Property * -ExcludeProperty PSComputerName, PSShowComputerName, RunspaceId` << – Lee_Dailey Dec 13 '19 at 20:15
  • ah - it only throws back the pscomputername and runspaceID in $row. But that's, interesting. Cause any other time i've assigned a variable to the invoke-command output, i've never seen those properties returned. But ok, thanks – Justin Beagley Dec 13 '19 at 20:57
  • you are most welcome! [*grin*] i found it out when i used `Select-Object` to add a calculated prop & then used `Export-Csv` ... and found those props. [*grin*] now i usually either explicitly build the desired `PSCustomObject` OR use the code snippet i showed previously. – Lee_Dailey Dec 13 '19 at 21:36

2 Answers2

2

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

  • Use [int] $hello to get the numeric value of the original, enum (System.Enum-derived) value, without the extra NoteProperty members such as PSComputerName that the remoting infrastructure adds (see below).

  • Use $hello.Value to get the string representation of the enum value (its symbolic name rather than its number).

  • If you know the original System.Enum-derived type, and that type is also available in your local session, you can cast the deserialized object back to its original type; e.g.:
    [Microsoft.Foo.Bar.ClusterRole] $hello

  • $hello is technically an [int], but decorated with extra properties, and information about the original type recorded in the hidden .pstypenames array, which reflects the original type's inheritance hierarchy with the type names prefixed with Deserialized.; e.g. Deserialized.Microsoft.Foo.Bar.ClusterRole; PowerShell's output formatting system causes such an object to be formatted via (implicitly applied) Format-Table, which in this case shows everything but the actual [int] value - only the NoteProperty members are shown.

Generally, you can exclude the unwanted properties as follows:

  • For objects of types that deserialize type-faithfully, which includes strings and .NET primitive types (such as [int], [long], ...) plus a few more (see MS-PSRP, the PowerShell Remoting Protocol specification), you can access $hello.psobject.BaseObject to get the underlying object without any NoteProperty members.

  • For others, you can create a new object (invariably of type [pscustomobject]), by piping to Select-Object with the unwanted properties excluded, as suggested by Lee Dailey):

    • $hello | Select-Object * -Exclude PSComputerName, PSShowComputerName, RunspaceId

    • Alternatively, you can focus on selecting just the properties you do want.

Read on for why that is necessary.


PowerShell's remoting infrastructure adds the following properties to every object returned from a remote invocation, as NoteProperty members:

  • PSComputerName ... the name of the remote computer

  • RunspaceId ... the ID of the runspace in which the remote command executed.

  • PSShowComputerName ... a hidden property that, when set to $true on all objects returned via Invoke-Command's -HideComputerName switch, suppresses display of the PSComputerName property in the default output (but the property is still there); you can only see the PSShowComputerName itself if you pipe a remotely received object to Get-Member -Force.

  • Additionally, System.Enum-derived types, which are returned as [int] instances, a [string]-typed Value property NoteProperty member is added that contains the enum value's symbolic name.

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 in 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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

I believe this would work, if you don't want the computername:

(invoke-command $hi {Get-ClusterNetwork 'cluster backups'}).role
js2010
  • 23,033
  • 6
  • 64
  • 66