WriteLine() behaviour in a remote session

Welcome Forums General PowerShell Q&A WriteLine() behaviour in a remote session

This topic contains 5 replies, has 4 voices, and was last updated by

 
Participant
3 months, 1 week ago.

  • Author
    Posts
  • #111065

    Participant
    Points: 0
    Rank: Member

    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!

  • #111070

    Participant
    Points: 12
    Rank: Member

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

     

  • #111100

    Participant
    Points: 342
    Helping Hand
    Rank: Contributor

    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.

  • #111118

    Participant
    Points: 331
    Helping Hand
    Rank: Contributor

    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.

  • #111145

    Participant
    Points: 0
    Rank: Member

    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!

  • #111152

    Participant
    Points: 12
    Rank: Member

    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.

     

     

     

     

The topic ‘WriteLine() behaviour in a remote session’ is closed to new replies.