Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide guidance as to when Start-Process is appropriate vs. direct / &-based invocation #6239

Closed
mklement0 opened this issue Jul 6, 2020 · 11 comments · Fixed by #9168
Closed
Assignees
Labels
area-management Area - Microsoft.PowerShell.Management module area-native-cmds Area - native command support

Comments

@mklement0
Copy link
Contributor

mklement0 commented Jul 6, 2020

Related: #5152

Using Start-Process to invoke console (terminal) programs is (almost always) inappropriate, but, unfortunately, very common - instead, such programs should be invoked by direct invocation / via &, the call operator.

Proper guidance at the start of the Start-Process topic would go a long way to help clear up the confusion:


Note: Start-Process launches the new process asynchronously by default; add -Wait to wait for the newly created process to terminate.

  • DO NOT use Start-Process if you want to run a console (terminal-based) program synchronously, in the same window, with its standard streams connected to PowerShell's streams and the exit code reflected in $LASTEXITCODE - just invoke such a program directly / via &
    (e.g. whoami.exe or & whoami.exe rather than Start-Process whoami.exe).

    • Even if you use Start-Process -NoNewWindow -Wait, you won't be able to capture or redirect the program's output (you can only save stdout and stderr (separately) to files, as text, via -RedirectStandardOut and -RedirectStandardError). Additionally, the process' exit code will not be reflected in $LASTEXITCODE when you use Start-Process.

    • However, if your use case really calls for Start-Process (see below) and you need to obtain the process exit code, you can add -PassThru to the Start-Process call, which returns a process-information object (System.Diagnostics.Process) whose .ExitCode property can be examined after the newly launched process has exited, which you can ensure by also passing -Wait to Start-Process, or by calling .WaitForExit() on the object later, or by checking if .HasExited indicates $true.

  • [Only needed on Unix] DO use Start-Process to launch a GUI program asynchronously on Unix-like platforms (e.g., Start-Proces gedit).

    • Note: On Windows, GUI programs launch asynchronously even with direct invocation / &, so Start-Process Notepad and Notepad have the same effect.
  • [Only needed on Unix] DO use Start-Process to launch a detached process via the standard nohup utility, i.e. a process that will run invisibly, detached from the calling terminal, sending its output to a file.

    • Note: On Windows, you can achieve a similar effect by launching with -WindowStyle Hidden (albeit without automatic saving of output in a file).
  • [Windows-only] DO use Start-Process for starting console applications in a new window.

    • On Unix-like platforms, -NoNewWindow is invariably implied, and use of Start-Process for console programs there only makes sense if either (a) they neither prompt for input nor produce output or (b) -Wait is also used - but then direct invocation / & is the better choice - see The Start-Process topic contains incorrect and misleading information about use on Unix-like platforms #3013

    • [Windows-only] With -WindowStyle <style> you can additionally control the new process' window style (both for console windows and the windows of GUI applications, though they latter may not respect the setting), such as whether to start the window maximized, minimized, or even hidden (see next point).

  • [Windows-only] DO use Start-Process -WindowStyle Hidden, if you want to launch a process hidden.

  • [Windows-only] DO use Start-Process with -Verb RunAs in order to launch a process elevated (with administrative privileges, with triggers a UAC security prompt), invariably in a new window.

    • Caveat: -Verb RunAs cannot be combined with the -RedirectStandard* parameters, so if you want to capture the elevated process' output in files, you'll need to launch a shell process with a command line that uses that shell's redirection features from inside the elevated process, along the lines of Start-Process -Verb RunAs cmd.exe '/c "net session > out.txt"'
  • [Windows-only] DO use Start-Process with -Credential if you want to launch a process with a different user identity, invariably in a new window.

    • Caveat: This can not be combined with -Verb RunAs, so in order to run as a different user and with elevation, Start-Process calls must be nested, as demonstrated in this Stack Overflow answer.

From a cross-platform perspective, the short of it is:

  • On Unix-like platforms, Start-Process is useless except for two (unusual) scenarios: launching a GUI application asynchronously and launching a detached process via nohup

Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

@mi-hol
Copy link

mi-hol commented Mar 23, 2021

@mklement0 my use case is to start a new pwsh process with elevated administrator permission from a standard user as I try to emulate 'sudo'.

From my understanding impersonation using the -Credential paramater is only available in Start-Process.
Note: search for "-Credential" in online documentation gives tons of false positive search hits :(

Do I miss an obvious alternative that would avoid above mentioned drawbacks and issues?

@mklement0
Copy link
Contributor Author

Good point, @mi-hol: I forgot to include the -Verb RunAs and -Credential use cases.

Note that they cannot be combined, however; if you do need to combine them - which in the typical case simply means that the admin user name is pre-populated in the UAC dialog, but you'll still have to supply the password interactively - you'll have to nest Start-Process calls.

I've updated the initial post, including with a link to a Stack Overflow answer that shows the nesting technique.

@mi-hol
Copy link

mi-hol commented Mar 23, 2021

Thanks @mklement I had used the nested Start-Process calls already.

I noticed thought that on my tests this technique works only with Windows powershell as the shell to run a second elevated pwsh.
Not sure if this is "by design" or a bug in pwsh's Windows compatibility.

Working example with Windows powershell:

[string]$AdminAcctName = "??replaceWithyourAdminAcctName??"
[string]$AdminAcctPwd = "??replaceWithyourAdminAcctPassword??"
[securestring]$secAdminAcctPwd = ConvertTo-SecureString $AdminAcctPwd -AsPlainText -Force
[pscredential]$AdminCredential = New-Object System.Management.Automation.PSCredential ($AdminAcctName, $secAdminAcctPwd)

Start-Process powershell.exe -Credential $AdminCredential -ArgumentList "Start-Process -FilePath 'pwsh.exe' -Verb runAs"

Failing example with pwsh:

[string]$AdminAcctName = "??replaceWithyourAdminAcctName??"
[string]$AdminAcctPwd = "??replaceWithyourAdminAcctPassword??"
[securestring]$secAdminAcctPwd = ConvertTo-SecureString $AdminAcctPwd -AsPlainText -Force
[pscredential]$AdminCredential = New-Object System.Management.Automation.PSCredential ($AdminAcctName, $secAdminAcctPwd)

Start-Process pwsh.exe -Credential $AdminCredential -ArgumentList "Start-Process -FilePath 'pwsh.exe' -Verb runAs"

@mklement0
Copy link
Contributor Author

@mi-hol, you're missing the -Command / -c parameter in the pwsh.exe example:
(powershell.exe defaults to -c, but pwsh.exe now defaults to -file, so as to support Unix shebang lines):

# Note the use of `-c`
Start-Process pwsh.exe -Credential (get-credential) -ArgumentList "-c Start-Process -FilePath 'pwsh.exe' -Verb runAs"

@wikiped
Copy link

wikiped commented Dec 1, 2021

Another point that deserves to be added to the list:

  • [Windows-only] DO NOT use Start-Process with -Verb RunAs when you want to capture StandardOutput / StandardError from elevated process - there is NO built-in way to do that.

This, of course, leaves out the answer to the question what is the obvious 'Powershell way' to do it. And it seems there is none. Assuming that resorting to New-Object System.Diagnostics.Process is not really a 'Powershell way' to get it done.

@mklement0
Copy link
Contributor Author

Thanks, @wikiped, good point. Please see my update to the initial post; I've folded the information as caveat into the bullet point about -Verb RunAs, and I've also included the only solution I'm aware of (direct use of System.Diagnostics.Process wouldn't help, the limitation is likely at the level of the WinAPI).

@wikiped
Copy link

wikiped commented Dec 3, 2021

Thank you @mklement0 for updating the list. I was struggling with System.Diagnostics.Process to get it working and do realize now that it won't help either. There is basically no way to make elevated process return its result back to the calling process. They are completely different processes at the end and it is necessary to use one of IPC approaches:

  • Write to file on disk
  • Use Pipes
  • Use Events
  • User TCP/IP

Writing to file on disk is probably the easiest to implement among those.

@mklement0
Copy link
Contributor Author

@wikiped, saving to files is the only thing that Start-Process itself offers, and the initial post now shows a workaround for how to achieve that in combination with -Verb RunAs: by launching the target program indirectly, via a shell process whose own redirection features can then be used.

As for IPC approaches: I haven't dug deeper, but I suspect that pipes and events aren't an option for security reasons (prevented by design, at least with respect to the standard streams), and that a TCP/IP-based mechanism would require both the caller and the elevated callee to be explicitly designed for that.

@yair-mantis
Copy link

thanks for this, a small tuning

i would link to the call operator &, as that is how its referred in the docs.

@mklement0
Copy link
Contributor Author

Thanks, @yair-mantis - I've updated the initial post accordingly.

@yecril71pl
Copy link
Contributor

Note: the remark no prompt for input was not included in the fix. This is probably a good thing because this description is too vague. I understand that pwsh does not offer job control so whether it prompts or not, the input it will get from standard input will be empty unless redirected, which may but need not be OK. But it can prompt for input using another communication channel and produce its output elsewhere too, especially if it runs as a daemon like ftpd for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-management Area - Microsoft.PowerShell.Management module area-native-cmds Area - native command support
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants