In this article, our guest blogger—Brent Challis—provides an example of how to emulate the Linux sudo (super user do) command in PowerShell.

I am confident that I am not the only person to experience the problem of opening a PowerShell console to execute a command and getting:
PS C:\>Start-Service MSSQLServer Start-Service : Service 'SQL Server (MSSQLSERVER) (MSSQLServer)' cannot be started due to the following error: Cannot open MSSQLServer service on computer '.'. At line:1 char:1 + Start-Service MSSQLServer + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OpenError: (System.ServiceProcess.ServiceController:ServiceController) [Start-Service], ServiceCommandException + FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand PS C:\>
These are the joys of needing to run certain commands in a shell run as The Administrator. Somewhat irritating but unavoidable.
A colleague, who is a keen Linux person, introduced me to the sudo (super user do) command, which can execute a command as the super user. My immediate reaction was that there must be a way to emulate this in PowerShell, and here is my approach.
The Start-Process command has a -Verb parameter. If you use runas as the verb on its own, the executable will launch with elevated permissions. You will be prompted by the UAC to confirm the execution.
The most straightforward solution to my problem would be:
function sudo { $commandLine = (Get-History | Select-Object -Last 1).CommandLine Start-Process -FilePath powershell.exe -ArgumentList $commandLine -Verb runas } |
This would then run the last command as The Administrator.
There are, obviously, problems with this:
- The name does not conform to the Verb-Noun convention.
- There is no flexibility in what it does.
- The shell to be launched is hard coded and launches PowerShell 5.
My solution is a command called Invoke-CCCommandAsTheAdministator. The CC prefix on the noun indicates that it is in the ChallisConsultancy module. While the name is verbose, intellisense helps.
The objectives for my version of the command were:
- To define a Linux-compatible alias, sudo.
- To enable the command to execute another command with elevated permissions.
sudo Start-Service MSSQLServer
sudo wsl –shutdown - To support the use of ! to run the last command.
sudo ! - To support the use of !! to display the history of commands so that I can select the one that I want to run.
sudo !!
The function is defined as:
function Invoke-CCCommandAsTheAdministrator { [CmdletBinding()] [Alias("sudo")] |
This means that the alias is part of the command, so individual users do not need to define their own aliases. This provides compatibility with Linux in the same way the alias of ls for Get-ChildItem does. (The alias sudo is used throughout the article as it matches my intended use, makes my typing easier, and keeps the sample code shorter.)
Now when I forget to open an elevated shell, the solution is straightforward:
PS C:\>Start-Service MSSQLServer Start-Service : Service 'SQL ServerSQLSERVER) (MSSQLServer)' cannot be started due to the following error: Cannot open MSSQLServer service on computer '.'. At line:1 char:1 + Start-Service MSSQLServer + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OpenError: (System.ServiceProcess.ServiceController:ServiceController) [Start-Service], ServiceCommandException + FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand PS C:\>sudo !
As I am writing this for myself, which makes the coding as much a journey as it is a destination, I can explore other functionality without being concerned with feature creep—which is a legitimate concern for developers working within the constraints of formal projects. So, to the starting version of the command, I added some more requirements:
- Select which edition of PowerShell to use: Desktop, Core, or use the same one as the calling shell.
- Specify whether or not to automatically close the elevated shell after the command has executed.
- Specify whether or not the calling shell will block until the elevated shell is closed. This would mean that the command could be used in a script which needs to wait until the elevated shell closes before moving to the next line of code.
- Support using sudo to simply launch an elevated shell.
This command must be able to process any command entered by the user. As a developer, the code customarily asks the user for the information it needs. In this case, the user tells my code what needs to be done. Hence, as I do not know what is going to be in the command line—as it will be controlled by the user—I can simply illustrate some possible responses. It could be:
- sudo launch an elevated shell
- sudo ! launch an elevated shell and run the last command
- sudo !! launch an elevated shell and run the selected command
- sudo Start-Service MSSQLServer launch an elevated shell running the Start-Service command to start the SQL Server instance
- sudo Start-Service -Name MSSQLServer same as above but with more elements on the line
- sudo Start-Service -Name MSSQLServer -Verbose same as above but with even more elements on the line to demonstrate the uncertainty regarding the input
- sudo wsl –shutdown reboot my Linux installations based on Windows Support for Linux
My approach to dealing with this is a defined parameter Command at position 0 to collect the first element and then CommandLine to collect all the entries that are not associated with parameters:
Param ( [Parameter(Position=0)] [String]$Command, [parameter(ValueFromRemainingArguments=$true)] [String[]]$CommandLine |
Having collected the first entry from the command line, it could be:
- Blank or Null
- !
- !!
- Start-Service (for example)
To deal with these options, I create a parsedCommandLine from the value of the Command Parameter and the CommandLine collection of entries that are not associated with parameters:
$parsedCommandLine = "" switch($Command) { "!" {$parsedCommandLine = (Get-History | Select-Object -Last 1).CommandLine} "!!" {$parsedCommandLine = (Get-History | Sort-Object -Property iD -Descending |Select-Object -Property CommandLine | Out-GridView -OutputMode Single -Title "Select Command").CommandLine} default {$parsedCommandLine = ($Command + " " + ($CommandLine -join " ")).Trim()} } |
I could have used Select-ItemFromList (a command that I wrote before Out-GridView supported selections) and have it display a text menu to enable the selection of a command from the command history. Instead, I used Out-GridView as it was convenient.
This provided the functionality that I initially wanted.
To get a better understanding of the options available when launching the shells I used:
PowerShell -?
and
pwsh -?
These shells allow me to see what options exist and then create parameters to support my desired functionality.
The full parameter block definition is:
Param ( [Parameter(Position=0)] [String]$Command, [Alias("noe")] [Switch]$NoExit, [Alias("wfc")] [Switch]$WaitForCompletion, [ValidateSet("Default","Desktop","Core")] [String]$PowerShellEdition = "Default", [parameter(ValueFromRemainingArguments=$true)] [String[]]$CommandLine ) |
If the PowerShellEdition is defined as Default I determine what shell is being used by:
if ($PowerShellEdition -eq "Default") { $PowerShellEdition = $PSVersionTable["PSEdition"] } |
Then I process the result by starting the appropriately identified shell with the constructed argument:
Process { switch ($PowerShellEdition) { "Core" { Start-Process -FilePath pwsh.exe -ArgumentList ($noExitValue + " -Command " + $parsedCommandLine) -Wait:$WaitForCompletion -Verb runas } "Desktop" { Start-Process -FilePath powershell.exe -ArgumentList ($noExitValue + " " + $parsedCommandLine) -Wait:$WaitForCompletion -Verb runas } } } |
And, of course, no command is complete without comment-based help. I used the command:
New-CCCommentBasedHelp -Name Invoke-CCCommandAsTheAdministrator
This allowed me to create a skeleton that includes entries for each parameter and populates it with information from attributes and the code for the command and parameters. This is the skeleton as the starting point:
<#
.SYNOPSIS
Invoke - A brief description of what the function Invoke-CCCommandAsTheAdministrator does.
This keyword can be used only once in each topic.
.DESCRIPTION
Invoke - A detailed description of what the function Invoke-CCCommandAsTheAdministrator does.
This keyword can be used only once in each topic.
.PARAMETER Command
Parameter Description
.PARAMETER NoExit
Aliases: noe
Parameter Description
.PARAMETER WaitForCompletion
Aliases: wfc
Parameter Description
.PARAMETER PowerShellEdition
Valid values: Default, Desktop, Core
Default Value: "Default"
Parameter Description
.PARAMETER CommandLine
Parameter Description
.EXAMPLE
Invoke-CCCommandAsTheAdministrator -Command -NoExit -WaitForCompletion -PowerShellEdition -CommandLine
.INPUTS
The Microsoft .NET Framework types of objects that can be piped to the
function or script. You can also include a description of the input
objects.
.OUTPUTS
.NOTES
Command Alias: "sudo"
Confirm Impact: Medium
Supports Positional Binding
#> |
The final help block is:
<#
.SYNOPSIS
Invoke-CCCommandAsTheAdministrator is designed to run a command with the
elevated permissions of The Administrator
.DESCRIPTION
When running Invoke-CCCommandAsTheAdministrator, or using its alias of sudo,
the command identified will be executed in another PowerShell process
which will run as The Administrator.
You will be asked for permission to use the elevated context by the UAC.
.PARAMETER Command
This identifies the command to be executed with elevated permissions.
There are some special options:
! Execute the previous command without waiting for the new process
to complete before returning control. The new powershell process
does not automatically exit.
!! Displays the history of commands and will execute the selected
command without waiting for the new process to complete before
returning control. The new powershell process does not
automatically exit.
.PARAMETER NoExit
Aliases: noe
When this switch is used the elevated shell does not close after completing
the command.
.PARAMETER WaitForCompletion
Aliases: wfc
This switch causes the calling script to block until the elevated shell
completes the command and exits - either automatically or manually if
called with the NoExit switch.
.PARAMETER PowerShellEdition
Valid values: Default, Desktop, Core
Default Value: "Default"
This parameter defines which shell to use. If set to Default the same
shell as the calling shell will be used.
.PARAMETER CommandLine
This parameter is designed to collect the rest of the command line to
enable the Invoke-CCCommandAsTheAdministrator command to be used to launch
an elevated shell and run a specific command.
.EXAMPLE
Invoke-CCCommandAsTheAdministrator -Command Start-Service MSSQLServer -NoExit -WaitForCompletion -UseWindowsPowerShell
.EXAMPLE
Invoke-CCCommandAsTheAdministrator !
Execute the previous command as The Administrator using the same shell as the
calling environments and do not exit the new Powershell process.
.EXAMPLE
sudo Start-Service MSSQLServer -NoExit -WaitForCompletion -PowerShellEdition Core -Verbose
will run PowerShell Core to start the SQL Server service. The elevated shell will
not close and the calling shell will block until the elevated shell is closed
manually.
The Verbose message will be:
VERBOSE: Running Core edition to execute Start-Service MSSQLServer, The calling code will wait until the shell
closes.. The shell needs to be manually closed.
.NOTES
Confirm Impact: Medium
Supports Positional Binding
This command has an alias of sudo (Linux 'Super User Do').
#> |
I have found this to be very useful and hope you do as well.
For me, this is an example of how readily PowerShell can be customized to suit individual users’ needs and extend to provide the desired functionality.
Happy scripting.
P. Brent Challis MTech, MCT, MACS (Snr)
- You can download the script here.
- You can check out Brent’s personal blog here.
We love to hear from you! Please comment below, or in our feedback forum and reference this post.