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.

3
  • 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 <<< Commented Dec 13, 2019 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 Commented Dec 13, 2019 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. Commented Dec 13, 2019 at 21:36

2 Answers 2

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 [string] instances only, you can access .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 decorates every object returned from a remote invocation with the following NoteProperty ETS (Extended Type System) 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, are decorated with a [string]-typed Value NoteProperty that contains the enum value's symbolic name (the enum's type name can be inferred from .pstypenames[0] -replace '^Deserialized\.').

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

(invoke-command $hi {Get-ClusterNetwork 'cluster backups'}).role

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.