Using System.Diagnostics.ProcessStartInfo to start ssh.exe from a UDDashboard/UDButton

Product: PowerShell Universal
Version: 2.0.0

Hey all,

Forgive me if this has been answered elsewhere but I’ve done a couple of searches and not turned anything up.

I’m trying to run an ssh command via a dashboard from my PSU server (which is Windows Server 2019). This is the code snippet:

$Pages += New-UDPage -Name 'Updates' -Content {

    $psi = New-object System.Diagnostics.ProcessStartInfo
    $psi.CreateNoWindow = $true 
    $psi.UseShellExecute = $false 
    $psi.RedirectStandardOutput = $true 
    $psi.RedirectStandardError = $true 
    $psi.FileName = 'ssh.exe' 
    $psi.Arguments = @("user@remotesystem -t `"/usr/bin/bash -ic `'~/.local/bin/ansible-playbook -i ~/ansible/hosts ~/ansible/run_win_sec_updates.yml`'`"") 
    $process = New-Object System.Diagnostics.Process 
    $process.StartInfo = $psi 
    [void]$process.Start()
    $output = $process.StandardOutput.ReadToEnd() 
    $process.WaitForExit() 
    $message = $output | Out-String

    Show-UDModal -Content { New-UDTypography -Text $message }
    Wait-Debugger

Trouble is, this process (ssh.exe) never completes. If I run the same System.Diagnostics.ProcessStartInfo object from a remote PSSession, or via PS console on the machine, it executes (and completes) fine.

I’ve taken a look at the process started by PSU (failing) and one started manually (working) using Get-CimInstance Win32_Process, but cannot for the life of me see any difference between the two.

If anyone has any ideas/pointers for where I can take this, or further debugging I could try, I’d be very grateful!

I wonder if this is a side effect of how we start dashboard processes. Are you running in the integrated environment or another environment?

Hi Adam, thanks for getting back to me.

It doesn’t seem to make any difference which environment I choose: Integrated (parent process being universal.server), Windows PoSH 5.x (powershell.exe), Core 7.1.3 (pwsh.exe), the ssh process starts and only stops if I intervene to stop it.

I’ve run the same piece of code logged on to the console locally and via remote PSSession in both Windows PoSH 5.x and Core 7.1.3 and it succeeds without a hitch. It’s a real head-scratcher.

As an update this morning, I tried encapsulating the process using cmd /c (using a System.Diagnostics.ProcessStartInfo object) but the ssh.exe process failed to terminate again, meaning that the cmd.exe process didn’t terminate either.

I’ve just run a separate test using:

$ansiblesesh = New-PSSession -HostName "hostname.fqdn" -UserName "localuser" -SSHTransport
$output = Invoke-Command -Session $ansiblesesh -ScriptBlock { /usr/bin/bash -ic '~/.local/bin/ansible-playbook -i ~/ansible/hosts ~/ansible/run_win_sec_updates.yml' }
    

instead of the previous method.

This starts ssh.exe on my PSU server, but again it fails to terminate and never returns anything to the $output variable.

Can anyone suggest a way to ‘break free’ of the dashboard process as a parent so that I can test if ssh behaves differently that way?
As previously mentioned (and in this latter test using New-PSSession) I’ve used the same code successfully inside a console pwsh process.

Afternoon update: I attempted to manually start a parent pwsh.exe process in a similar way to PSU’s dashboard method.

On my machine, at least, the parent pwsh process was started using the following command:

"C:\Program Files\PowerShell\7\pwsh.exe"  -NoProfile -Command "& { [System.Reflection.Assembly]::LoadFrom('C:\Program Files (x86)\Universal\Host.dll') | Out-Null; [UniversalHost.AgentService]::StartDashboard(54926, 1980) }"

Adding -Interactive and -NoExit switches and trying to start the same command manually failed, but when I stripped back to:

pwsh.exe -i -noexit -NoProfile -Command "& { [System.Reflection.Assembly]::LoadFrom('C:\Program Files (x86)\Universal\Host.dll') | Out-Null }"

… it gave me a prompt.

Running the single line of code that locks up in my dashboard:

$output = Invoke-Command -HostName "user@remotesystem" -ScriptBlock { /usr/bin/bash -ic '~/.local/bin/ansible-playbook -i ~/ansible/hosts ~/ansible/run_win_sec_updates.yml' }

…ran successfully in this new pwsh process.

So, not sure it’s the method of calling, but I could be wrong!

I might need to add some more logging to the dashboard PS host inside UD. I wonder if it’s attempting to prompt for something.

Thanks Adam. Should I log it in github?

That would be super helpful. Thanks!

I’ve submitted this:

Worth noting that I’ve reproduced the issue just using an Automation | Script, so it should be easy to duplicate.

1 Like

Could this have to do with ssh prompting for a password? I can certainly reproduce this and I don’t have keys configured for the server\client. I do not see the output from ssh prompting for the password at all when running in PSU. Inside a PS console, it prompts fine.

Are you running PSU as the same account as when you are trying it locally? I’m not an SSH expert but I think when you generate the keys that get installed to the current user account so just verifying that it’s running as the same user.

That’s a very good point, Universal.Server would be running as NT AUTHORITY\SYSTEM.

I can test using username/password on a box that supports that, which should eliminate the running user. If that works, I will need to test again after generating a key pair under the Local System account!

Watch this space.

Yes! That seems to be it. Copying the SSH keys over to the Local System profile (C:\Windows\System32\config\systemprofile.ssh) and running the Invoke-Command script again returns properly.

So it was my fault all along! It seemed like something that so many other people would have seen a problem with that I knew it had to be something I was doing wrong. :grin:

Thanks for your help Adam. I can get back to creating my ansible interaction dashboard.

1 Like