Search code examples
powershellbatch-filecmdelevated-privilegesexecutionpolicy

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?


Solution

    • 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""'"