antonio
antonio

Reputation: 11110

Run a PowerShell script from a cmd batch as admin

I have a PowerShell setup which I want to execute on a computer where perhaps the execution policy is restricted and requires admin rights.
Ideally, I could wrap it in a cmd batch like follows:

powershell -Command "Start-Process powershell -Verb runAs -ArgumentList '-noexit','-ExecutionPolicy','bypass','-File','C:\path\setup.ps1'"

The problem is that I can't make it to work when C:\path\setup.ps1 contains spaces, and also the path does not work if relative (with cd C:\path).
Any help?

Upvotes: 4

Views: 20451

Answers (2)

Santiago Squarzon
Santiago Squarzon

Reputation: 59772

Here you have an example of a script that checks if the process is running elevated and if it's not it attempts to start a new process elevated. There is no need to nest files or use CMD in this case.

This obviously comes with the caveat of an UAC prompt, as any other process that is started with elevated permissions.

$isAdmin = [System.Security.Principal.WindowsPrincipal]::new(
    [System.Security.Principal.WindowsIdentity]::GetCurrent()).
        IsInRole('Administrators')

if(-not $isAdmin) {
    $params = @{
        FilePath     = 'powershell' # or pwsh if Core
        Verb         = 'RunAs'
        ArgumentList = @(
            '-NoExit'
            '-ExecutionPolicy ByPass'
            '-File "{0}"' -f $PSCommandPath
        )
    }

    Start-Process @params
    return
}

"I'm elevated"
# Code goes here

Upvotes: 2

mklement0
mklement0

Reputation: 437052

  • In Windows PowerShell (see bottom section for PowerShell (Core) 7+), using Start-Process -Verb RunAs to launch a command with elevation (as admin), invariably uses C:\Windows\SYSTEM32 as the working directory - even a -WorkingDirectory argument, if present, is quietly ignored.
    Thus, in order to set a custom working directory and to invoke a script there, the -Command CLI parameter must be used, and a Set-Location (cd) call must precede a call to a script specified by relative path.

  • While passing the pass-through arguments individually to the Start-Process cmdlet's -ArgumentList parameter may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer.

  • Doing all this from cmd.exe, via powershell.exe, the Windows PowerShell CLI, complicates matters due to escaping requirements.

Applied to your powershell.exe CLI call,
assuming working dir. C:\path 1 and script file setup 1.ps1:

powershell -Command "Start-Process -Verb RunAs powershell '-NoExit -ExecutionPolicy Bypass -Command cd \\\"C:\path 1\\\"; & \\\".\setup 1.ps1\\\"' "
  • Note how the embedded " chars. are doubly escaped, as \\\": first as \" in order to be preserved during the outer powershell.exe call, and then again for the inner one.

  • While this typically works, there are edge cases where \-based escaping is not enough, namely if the script path or file name contains a cmd.exe metacharacter, such as &; in that case, "^"" (sic) must be used for the first round of "-escaping:

powershell -Command "Start-Process -Verb RunAs powershell '-NoExit -ExecutionPolicy Bypass -Command "^"" cd \\"^""C:\path 1\\"^""; & \\"^"".\setup 1.ps1\\"^"" "^""'"

Note:

  • From cmd.exe (batch files), "^"" (sic) is the most robust way to pass " that are embedded in an overall "..." string to powershell.exe, as shown above, whereas it is "" for pwsh.exe, the PowerShell (Core) CLI (see below).

  • By contrast, from a shell-free context (e.g., a scheduled task) \" works robustly, in both editions.

  • See this answer for details.


When you call pwsh.exe instead - the PowerShell (Core) 7+ CLI - simplifications are possible and a workaround may not even be needed:

  • pwsh.exe does preserve the caller's working directory by default even with -Verb RunAs and does respect a -WorkingDirectory argument with -Verb RunAs.

  • In addition to \", pwsh.exe more simply supports "" for embedding " chars. in "..." strings; the latter works robustly from cmd.exe

  • pwsh.exe itself now has a -WorkingDirectory parameter, which therefore allows invoking the script with the -File parameter:

pwsh.exe -WorkingDirectory "C:\path 1" -Command "Start-Process -Verb RunAs pwsh.exe '-NoExit -ExecutionPolicy Bypass -File ""C:\path 1\setup 1.ps1""'"

Upvotes: 6

Related Questions