Search code examples
powershellsshopensshpowershell-remoting

How can I check if the PowerShell profile script is running from an SSH session?


I'm trying to work around a bug in Win32-OpenSSH, where -NoProfile -NoLogo is not respected when using pwsh.exe (Core) and logging in remotely via SSH/SCP. One way (of several) I tried, was to add the following in the very beginning of my Microsoft.PowerShell_profile.ps1 profile.

function IsInteractive {
    $non_interactive = '-command', '-c', '-encodedcommand', '-e', '-ec', '-file', '-f'
    -not ([Environment]::GetCommandLineArgs() | Where-Object -FilterScript {$PSItem -in $non_interactive})
}
# No point of running this script if not interactive
if (-not (IsInteractive)) {
    exit
}
...

However, this didn't work with a remote SSH, because when using [Environment]::GetCommandLineArgs() with pwsh.exe, all you get back is: C:\Program Files\PowerShell\6\pwsh.dll regardless whether or not you are in an interactive session.

Another way I tried, was to scan through the process tree and look for the sshd parent, but that was also inconclusive, since it may run in another thread where sshd is not found as a parent. So then I tried looking for other things. For example conhost. But on one machine conhost starts before pwsh, whereas on another machine, it starts after...then you need to scan up the tree and maybe find an explorer instance, in which case it is just a positive that the previous process is interactive, but not a definite non-interactive current process session.

function showit() {
    $isInter = 'conhost','explorer','wininit','Idle',
    $noInter = 'sshd','pwsh','powershell'
    $CPID = ((Get-Process -Id $PID).Id)

    for (;;) {
        $PNAME = ((Get-Process -Id $CPID).Name) 
        Write-Host ("Process: {0,6} {1} " -f $CPID, $PNAME) -fore Red -NoNewline
        $CPID = try { ((gwmi win32_process -Filter "processid='$CPID'").ParentProcessId) } catch { ((Get-Process -Id $CPID).Parent.Id) } 
        
        if ($PNAME -eq "conhost") {
            Write-Host ": interactive" -fore Cyan
            break;
        }
        if ( ($PNAME -eq "explorer") -or ($PNAME -eq "init") -or ($PNAME -eq "sshd") ) {
            # Write-Host ": non-interactive" -fore Cyan
            break;
        }
        ""
    }
}

How can I check if the profile script is running from within a remote SSH session?

Why am I doing this? Because I want to disable the script from running automatically through SSH/SCP/SFTP, while still being able to run it manually (still over SSH.) In Bash this is a trivial one-liner.


Some related (but unhelpful) answers:


Solution

  • If I understand you correctly, I believe I am handling this particular case in my PowerShell profile by checking the value of [System.Console]::IsOutputRedirected.

    I have a bunch of code in Microsoft.Powershell_profile.ps1 that I want to run only in an interactive session, both when started locally as well as through SSH.

    ie. When I run a command over SSH on my remote windows host, like so:

    ssh user@winhost C:\\PROGRA~1\PowerShell\7\pwsh.exe -Command Get-ChildItem
    

    This will execute my PowerShell profile Microsoft.Powershell_profile.ps1 automatically before running the Get-ChildItem command. Inside of my PowerShell profile, I am checking the value of [System.Console]::IsOutputRedirected which returns true in this case. So when it returns true I return early and skip executing all the logic specific to initializing my interactive PowerShell session (command completion, keybindings, terminal colors, etc).

    Conversely, when I SSH into PowerShell on the remote windows host interactively, like so:

    ssh user@winhost C:\\PROGRA~1\PowerShell\7\pwsh.exe
    

    This also executes my PowerShell profile Microsoft.Powershell_profile.ps1 automatically before dropping me into an interactive PowerShell session, but in this case, when I check the value of [System.Console]::IsOutputRedirected it returns false, and I allow the script to continue running to setup my PowerShell profile for an interactive session.

    Note: The above logic handles that particular case when it comes to executing over SSH, however, to handle various different possible ways to run PowerShell interactively, I've had to check additional cases. My current logic for handling all possible cases regarding interactivity is the following:

    if ([System.Console]::IsOutputRedirected -or ![Environment]::UserInteractive -or !!([Environment]::GetCommandLineArgs() | Where-Object { $_ -ilike '-noni*' })) {
        // Non-Interactive
    }
    

    See: