Get-PSSession and Invoke-Command Pipeline Question

I trying to learn PowerShell core 6.0, and today i met issue i can’t understand.

Get-PSSession | Write-Output -InputObject {$_}

This work fine. I can pass Get-PSSession result to Write-Output by using {$_}.

Get-PSSession | Invoke-Command -Session {$_} -ScriptBlock {ls}

This one is not working. With error:“Cannot bind parameter ‘Session’. Cannot convert the “$_” value of type “System.Management.Automation.ScriptBlock” to type “System.Management.Automation.Runspaces.PSSession””

But below one work fine after i add foreach and remove “{}” from “$_”

 Get-PSSession | foreach{ Invoke-Command -Session $_ -ScriptBlock {ls}}

Can anyone help me understand this?

So for the benefit of those coming later, just know that these are exactly the same:

Get-PSSession | Write-Output -InputObject {$_}
Get-PSSession | Write-Output
Get-PSSession

E.e., Write-Output is unnecessary. But I get why you’re using it in this example, and it plays into why it’s unnecessary. It’s important to know that $_ isn’t globally defined in PowerShell; it doesn’t always mean ‘the thing that came in from the pipeline’. $_ only works in specific areas where PowerShell has been programmed to look for it.

In the above example, the -InputObject parameter of Write-Output is coded to bind objects of type [object] from the pipeline. As everything inherits from System.Object, that means -InputObject will accept literally anything. That’s why the above works.

Get-PSSession | Invoke-Command -Session {$_} -ScriptBlock {ls}

The -Session parameter doesn’t accept pipeline input, as described in the command’s documentation. So you’re trying to “work around” that, I think.

By enclosing $_ in {}, you’ve not “executed” $_ to see if it contains anything; you’ve instead made a script block, which cannot be coerced into being a session. Thus, the error you got.

Your first example “works,” but it mainly does so by accident; Write-Object isn’t really intended to be used that way, and you’ve just stumbled across a situation where it does.

The “correct” workaround (one of many) might be:

Invoke-Command -Session (Get-PSSession) -ScriptBlock {ls}

In your last example, ForEach-Object is in fact one of the magic places where $_ works. By removing {}, you’ve stopped creating a script block and instead are allowing ForEach-Object to treat $_ as it normally would - to represent the piped-in object.