PowerShell Redirection Not Working for New-PSSession

Created on 3 May 2020  路  18Comments  路  Source: PowerShell/PowerShell

Description

I believe I have a good understanding of message streams in PowerShell but I do not understand why I cannot redirect or suppress this warning message from New-PSSession (see _Actual behavior_, below).

Context (skip if you don't care)

I have a legitimate reason for wanting to control all message streams - I am trying to capture and serialise the output from any arbitrary command to send the results of each stream via std_out to a parent process that is running a different language (currently NodeJS, but the format is agnostic). I'm wrapping commands with Invoke-Command and it works great, but New-PSSession has one warning (of several) that does not behave like the others.

Steps to reproduce

This snippet is only a minimum to recreate the warning. It does not include any of the redirection / suppression methods I have tried

param (
    $username,
    $password
)
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
$URL = "https://ps.outlook.com/powershell"
New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $URL -Credential $creds -Authentication Basic -AllowRedirection

Methods that do not work

Note: the ... in each represents the parameters from the sample in _Steps to reproduce_, I've omitted for clarity.

New-PSSession *>$null ...
New-PSSession ... | Out-Null
New-PSSession *>$null ... -OutVariable x -ErrorVariable y -WarningVariable z
New-PSSession *>$null ... -WarningAction SilentlyContinue
$WarningPreference = 'SilentlyContinue'
New-PSSession ...

I've even tried taking temporary control of std_out:

$std_out = [System.Console]::Out
$out_writer = New-Object IO.StringWriter
[System.Console]::SetOut($out_writer)

$std_err = [System.Console]::Error
$err_writer = New-Object IO.StringWriter
[System.Console]::SetOut($err_writer)

$sess = New-PSSession ...

[System.Console]::SetOut($std_out)
[System.Console]::SetError($std_err)

Expected behavior

Warnings are ideally redirected, but neither redirection nor suppression works for this particular warning.

Actual behavior

The following warning is always written to console (text is yellow).

WARNING: Using New-PSSession with Basic Authentication is going to be deprecated soon, checkout https://aka.ms/exops-docs for using Exchange Online V2 Module which uses Modern Authentication.

Environment data

Tested on 7.1 and 5.1

Name                           Value
----                           -----
PSVersion                      7.1.0-preview.2
PSEdition                      Core
GitCommitId                    7.1.0-preview.2
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Name                           Value
----                           -----
PSVersion                      5.1.18362.752
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.752
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
Area-Cmdlets-Core Issue-Question Resolution-Answered

Most helpful comment

Many thanks to @fMichaleczek for providing a clear repro. The problem is that the warning is created during session creation script processing, and therefore outside the usual remoting command channel which handles data streams. The result is that the server writes the warning directly to the console host through the session remote host call.

Unfortunately, I don't see any workaround for this other than having Exchange session not write the warning during session creation.

All 18 comments

Maybe -WarningAction SilentlyContinue resolve your issue?

@iSazonov Thanks, but as per my question, that doesn't work.

Also, if anyone wants 500 fake internet points, by all means answer the same question on Stack Overflow

WARNING: Using New-PSSession with Basic Authentication is going to be deprecated soon, checkout https://aka.ms/exops-docs for using Exchange Online V2 Module which uses Modern Authentication.

The warning string is not in PowerShell Core - no such string in resx files. It comes from Exchange Online V2 Module. I guess they do not use the warning stream at all. It seems the module is not OSS so you have to use Windows Feedback tool or other channel to communicate with the module owners.

Learn how to download and use the Exchange Online PowerShell V2 module to connect to Exchange Online PowerShell.

@iSazonov I am not using the EXO V2 module. I am calling New-PSsession, which is part of PowerShell Core:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7

Regardless, this is in my opinion, a fundamental issue with the PowerShell language. If a programming language is non-deterministic, it's not a very well implemented language.

The redirection behavior outlined, (again under PowerShell Core), is not being honoured:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7

The New-PSSession cmdlet creates a PowerShell session (PSSession) on a local or remote computer. When you create a PSSession, PowerShell establishes a persistent connection to the remote computer. Use a PSSession to run multiple commands that share data, such as a function or the value of a variable. To run commands in a PSSession, use the Invoke-Command cmdlet. To use the PSSession to interact directly with a remote computer, use the Enter-PSSession cmdlet. For more information, see about_PSSessions. You can run commands on a remote computer without creating a PSSession by using the ComputerName parameters of Enter-PSSession or Invoke-Command. When you use the ComputerName parameter, PowerShell creates a temporary connection that is used for the command and is then closed. Starting with PowerShell 6.0 you can use Secure Shell (SSH) to establish a connection to and create a session on a remote computer, if SSH is available on the local computer and the remote computer is configured with a PowerShell SSH endpoint. The benefit of an SSH based PowerShell remote session is that it can work across multiple platforms (Windows, Linux, macOS). For SSH based sessions you use the HostName or SSHConnection parameter set to specify the remote computer and relevant connection information. For more information about how to set up PowerShell SSH remoting, see PowerShell Remoting Over SSH. Note When using WSMan remoting from a Linux or macOS client with a HTTPS endpoint where the server certificate is not trusted (e.g., a self-signed certificate). You must provide a PSSessionOption that includes -SkipCACheck and -SkipCNCheck to successfully establish the connection. Only do this if you are in an environment where you can be certain of the server certificate and the network connection to the target system.

@iSazonov Can I ask what the semantics are for the label Resolution-Answered?

I am not using the EXO V2 module.

But the message you posted suggests otherwise.
See Get-Module output in your session.

@iSazonov

I am calling New-PSSession, a PowerShell Core module.
I am using redirection, a feature of PowerShell Core.

Nowhere in the redirection documentation does it say that it's optional, bypassable, or there can exist multiples of the same stream. I understand if a .NET module has direct access to std_out, that's a different issue. But as my issue clearly states, this does not override as it should, and this warning is (almost certainly) emitted via _a_ warning stream.

You're telling me it's the server I'm connecting to that's the problem? I cannot agree with this. It shouldn't matter if the server I am connecting to is running Exchange or Minecraft. The contract that the PowerShell language proposes is broken. Redirection should be as fundamental to this language as operators and conditions (2 + 2) -ne 5.

@daverickdunn This is what you SHOULD provide if you need the support of the PowerShell team (repeatable step and code that does not involve starting from other projects.)

Steps to reproduce

New-Item -ItemType Directory -Path C:\Temp -ErrorAction SilentlyContinue
'Write-Warning "Warning stream"' | Set-Content C:\Temp\InitScript.ps1
"@{ SchemaVersion = '2.0.0.0' ; GUID = '$([Guid]::NewGuid())' ; SessionType = 'Default' ; ScriptsToProcess = 'C:\temp\InitScript.ps1' } " | 
    Set-Content C:\Temp\Temp.pssc
Register-PSSessionConfiguration -Name Test -Path C:\Temp\Temp.pssc *> $null
Restart-Service WinRM

$session = New-PSSession -ComputerName $env:COMPUTERNAME -ConfigurationName Test -WarningAction SilentlyContinue
$session2 = New-PSSession -ComputerName $env:COMPUTERNAME -ConfigurationName Test *> $null

Unregister-PSSessionConfiguration -Name test

Expected behavior

empty screen

Actual behavior

WARNING: Warning stream
WARNING: Warning stream

@PaulHigin Could you please comment the issue?

@fMichaleczek My initial question and your feedback are hardly worlds apart. What are the "other projects" you're talking about?

@daverickdunn I think all @fMichaleczek meant was that your reproduction is dependent on having an Exchange server to open a new session in, so it's a bit more of a complicated reproduction that could be affected by a number of factors external to powershell itself. 馃檪

That said, the more straightforward reproduction he mentioned makes it clear that regardless of the session configuration / where the session is being created, warnings that occur during creating / initializing the PSSession are not being handled at all by the New-PSSession cmdlet and are simply being passed through.

@vexx32 Thanks for clarifying what he meant, but what is this barrier to entry for reporting a problem all about? It's boiled down to what I observed, with no fluff. Should reporters skip creating issues and go straight to pull requests? It's nonsense.

This is my code. How can I suppress warning "Using New-PSSession with Basic Authentication is going to be deprecated soon..."? Appending &?HideBannermessage=true to the URL does not work.

$user = "[email protected]"
$pass = "P@ssW0rd"
$password = $pass | ConvertTo-SecureString -asPlainText -Force
$creds = New-Object -typename System.Management.Automation.PSCredential -Argumentlist $user, $password
Connect-MSOLService -Credential $creds -ErrorAction Stop
Import-Module MSOnline

# Get list of tenants & loop
Get-MsolPartnerContract -All | ForEach { 
    $tenantprefix = [string]$_.DefaultDomainName

    # Configure the connection url and connect
    $ConnectionUri = "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$tenantprefix"
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionUri -Credential $creds -Authentication Basic -AllowRedirection -WarningAction SilentlyContinue
    Import-PSSession $Session -AllowClobber -WarningAction SilentlyContinue -DisableNameChecking | Out-Null

#run some cmdlets here...

}

Many thanks to @fMichaleczek for providing a clear repro. The problem is that the warning is created during session creation script processing, and therefore outside the usual remoting command channel which handles data streams. The result is that the server writes the warning directly to the console host through the session remote host call.

Unfortunately, I don't see any workaround for this other than having Exchange session not write the warning during session creation.

@PaulHigin So in summary, are you saying there are other threads / processes that can access the console directly? Is there absolutely no way to capture and separate that output, even outside of redirection? As I state in my original comment, I've tried temporarily overriding stdout, to no avail. Am I doing that incorrectly, or is that approach completely without merit?

Sorry for the multiple questions. I hope you can see what I'm pursuing. If redirection is not canonical and side effects are always a risk, I'd just like to bottom out my options for making output deterministic. PowerShell went a long way to separate data into dedicated streams , it's a shame if that has to fall apart when we get as far as IPC.

You know what, I think I just answered my own question. I'll move to file IPC, other threads can pollute the console all they want.

I solved my own issue... The & had to be escaped in the ConnectionUri

$ConnectionUri = "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($tenantprefix)&HideBannermessage=true"

@devblackops
It seems that Microsoft isn't going to solve this issue, Therefor I sharing my solution for it:

function GetRedirectUri ([string]$uri, [string]$upn, [string]$bearer_token) {
    $tokenValue = ConvertTo-SecureString "Bearer $bearer_token" -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 
    try {
        Invoke-WebRequest "https://$uri/powershell-liveid?BasicAuthToOAuthConversion=true;PSVersion=7.0.3" -Method 'POST' -Credential $credential -MaximumRedirection 0
    }
    catch {
        if ($_.Exception.Response.StatusCode -eq "Redirect") {
            $uri = $_.Exception.Response.Headers.Location.AbsoluteUri
        } else {
            throw $_.Exception
        }
    }

    return $uri
    <#
        .DESCRIPTION
        Solve Bug - When using bearer token the new-pssession unable to get redirect url for establishing pssession.
        This function get redirect URI by interacting with WSMAN directly via Rest-API.

        .PARAMETER uri
        Security & Compliance Center uri.

        .PARAMETER upn
        User Principal Name (UPN) is the name of a system user in an email address format..

        .PARAMETER bearer_token
        Valid bearer token value.

        .EXAMPLE
        CreateNewSession("ps.compliance.protection.outlook.com", "[email protected]", "dfhsdkjhkjhvkdvbihsgiu")

        .OUTPUTS
        [string] Redirect uri if redirected. 

        .LINK
        https://github.com/PowerShell/PowerShell/issues/12563
    #>
}

function CreateNewSession([string]$uri, [string]$upn, [string]$bearer_token) {
    $tokenValue = ConvertTo-SecureString "Bearer $bearer_token" -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 
    $uri = GetRedirectUri $uri $upn $bearer_token
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $uri -Credential $credential -Authentication Basic -AllowRedirection

    if (!$session) {
        throw "Fail - establishing session to $uri"
    }

    return $session
    <#
        .DESCRIPTION
        Creates new pssession using Oauth2.0 method.

        .PARAMETER uri
        Security & Compliance Center uri.

        .PARAMETER upn
        User Principal Name (UPN) is the name of a system user in an email address format..

        .PARAMETER bearer_token
        Valid bearer token value.

        .EXAMPLE
        CreateNewSession("ps.compliance.protection.outlook.com", "[email protected]", "dfhsdkjhkjhvkdvbihsgiu")

        .OUTPUTS
        PSSession - PSSession object.

        .LINK
        https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/new-pssession?view=powershell-7
    #>
}
Was this page helpful?
0 / 5 - 0 ratings