Author Posts

September 6, 2018 at 2:03 am

Hey guys, was hoping someone could enlighten me on an interesting behavior I have encountered recently.

So this is all related to [System.Console]::WriteLine() and where the output goes when used in a remote session as a different user.

Example:

$myrb = {

[System.Console]::WriteLine("test")

}

Invoke-Command -ScriptBlock $myrb -Credential (Get-Credential) -ComputerName 'localhost'

The above code will NOT produce any output however, the following will:

$myrb = {

[System.Console]::WriteLine("test")

}

Invoke-Command -ScriptBlock $myrb

So my question is, Why?

My best guess is that the 'StandardOutput' output stream used by system.console is trying to use something related to the runspace created by the invoke-command. Maybe there is some fancy advanced option in Register-PSSessionConfiguration however, nothing really sticks out to me.

I have discovered the following method works for retrieving the expected output:

$myrb = {

$sb = [System.Text.StringBuilder]::new()

$sw = [System.IO.StringWriter]::new($sb)

[System.Console]::SetOut($sw)

[System.Console]::WriteLine("Test")

$sb.ToString()

}

Invoke-Command -ScriptBlock $myrb -Credential (Get-Credential) -ComputerName 'localhost'

The trick being the SetOut(). However the 'Why' of it escapes me as to why this is needed.

Any hints or information would be greatly appreciated 🙂

Thanks!

September 6, 2018 at 2:59 am

System.console requires an interactive session, which is why invoking it on a remote computer does not produce any output.  This makes sense when you think about it.  Where's the data going?  There is no console available on a remote machine to allow a response to the output.  This is the reason for one of the main tenets in Powershell scripting best practices – "Thou shall not use write-host" (which is similar to system.console).

 

September 6, 2018 at 1:28 pm

Hey Evan,

I'm guessing your background is in a different programming language as your code is not very "Powershell-y" and you're referencing everything in a C++ format using static members in .NET. For instance, you can build StringBuilder like this:

$stringBuilder = New-Object -TypeName System.Text.StringBuilder

If you're simply trying to output text, you can simply do:

"Hello world"

My next question is what exactly are you trying to do? You're typically going to want to leverage a PSObject to return data that you can filter and export.

September 6, 2018 at 5:17 pm

Ditto on the other comments and lastly, Invoke-Command and specifying the localhost as the target host, is not what this cmdlet is designed for.

When you specify the computer name, you are specifically asking PS to reach across the wire to a remote computer named localhost and that of literally does not exist, remotely.

If you are logged in interactively on a workstation, running commands / scripts locally, unless you have multiple profiles that you know the creds for, there is little reason to specify computername or creds for local commands / scripts as PS always runs in interactively in the current user context.

If you did this, on workstations, this call to localhost or $env:computername, it will simply error out vs not specifying the local computer.

Invoke-Command -ComputerName $env:COMPUTERNAME -ScriptBlock {'hello'}

# Results

[LWS001] Connecting to remote server LWS001 failed with the following error message :
The client cannot connect to the destination specified in the request. ...

Invoke-Command -ScriptBlock {'hello'}

# Results

# Hello

Doing the above on a server works in both cases.

The PS documentation specifically states this.

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-6
… To run a single command on a "remote" computer, use the ComputerName parameter. ...
... on a local computer to evaluate or run a string in a script block as a command. PowerShell converts the script block to a command and runs the command immediately in the current scope, instead of just echoing the string at the command line.

September 6, 2018 at 11:11 pm

THANKS for the advice fellas. I think @Richard Burrs almost had the most helpful comment.

@Rob Simmers – Disagree completely mate, sorry. Your method slower and outdated – https://learn-powershell.net/2014/09/07/more-new-stuff-in-powershell-v5-a-new-way-to-construct-things/

Also irrelevant given the question.

@postanote – ...

unless you have multiple profiles that you know the creds for

ding ding ding. So sorry you didn't read the question and wrote all that stuff:

So this is all related to [System.Console]::WriteLine() and where the output goes when used in a remote session as a different user.

 

I had thought I didn't really need to given the question but it looks like I was wrong..
Background: I am loading .net assemblies containing classes, types and methods responsible for carrying out a database upgrade (DbUp if you are familiar with it). Without going into the 'whys', this needs to be done under a specific user profile and needs to be initiated by another user (nt authority system in this case). The default IUpgradeLog implementation uses [System.Console]::WriteLine() to write log messages. I wanted that output and I got that output by redirecting the output via the Console.SetOut method.

SO my question: Why? Where does that output go and why do I need to redirect it. @Richard Burrs touched on a possible explanation  with  'requires an interactive session' but doesn't fully explain where the output stream is directed to and why powershell cannot read it by default and why the output stream requires redirection to something else (stringbuilder, file, someCustomTextReader, whatever).

I am not looking for Powershell-y best-practices and 'Write-Host is the devil' shpeels. I am a curious person hoping someone out there has a better understanding of the System namespace and powershell runspaces and why the standard output of Console.Write is not captured and returned to the session invoking the command.

Thanks!

September 7, 2018 at 2:07 am

You asked "Where does that output go and why do I need to redirect it."

It's directed by default to the standard output stream (console window).  Unless the output is redirected via [System.Console]::SetOut, it will end up in a virtual Black hole if the standard output is not available.