2

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?

antonio
  • 9,910
  • 11
  • 64
  • 121

2 Answers2

5
  • 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.

  • Using -Verb RunAs to launch a command with elevation (as admin), invariably uses the SYSTEM32 directory 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 , the -Command CLI parameter must be used, and a Set-Location (cd) call must precede a call to a script specified by relative path.

  • 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 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:

  • From cmd.exe, "^"" (sic) is the most robust way to pass " that are embedded in an overall "..." string to powershell.exe (from a shell-free context, such as a scheduled task, use """ or, more simply, \".

  • For simplicity, for the doubly nested " chars. the \-escaping technique is used above, with the \ chars. themselves requiring escaping as \\.

Note: From the PowerShell CLI perspective - including in PowerShell (Core) 7+ (see below) - \" always works, but its use is problematic from cmd.exe, which doesn't understand \" as an escaped " char. and therefore treats it as a regular string delimiter, which can cause it to misinterpret what's been \"...\" as being part of an unquoted strings, where metacharacters such as & can then break the command, because they're interpreted by cmd.exe itself, up front; e.g., powershell -c " \"Abbot & Costello\" " breaks from cmd.exe, requiring either ^& instead of " or, as shown above, escaping embedded " as "^"" instead:
powershell -c " "^""Abbot & Costello"^"" "


When you call pwsh.exe instead - the PowerShell (Core) 7+ CLI - two simplifications are possible:

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

  • pwsh.exe now has a separate -WorkingDirectory parameter, which therefore allows invoking the script with the -File parameter - do note, however, that the file path is resolved before the working directory is set, so the full path is used below.

pwsh.exe -Command "Start-Process -Verb RunAs pwsh.exe '-NoExit -ExecutionPolicy Bypass -WorkingDirectory ""C:\path 1"" -File ""C:\path 1\setup 1.ps1""'"
mklement0
  • 312,089
  • 56
  • 508
  • 622
  • 1
    Thank you. Your `"^""` solved the issue. Still sad to observe how, despite all their efforts, Microsoft is unable to give us a simple syntax to call an admin script. Something that in Linux would be as simple as `sudo '/path/to/setup'`. – antonio Jan 17 '22 at 13:05
1

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 `"$PSCommandPath`""
        )
    }
    Start-Process @params
    Exit
}

"I'm elevated"
# Code goes here
Santiago Squarzon
  • 20,988
  • 4
  • 10
  • 27
  • 1
    Thank you, but, as mentioned in the question, I do not know in advance what the execution policies on the target computers could possibly be, and they could be set to restricted. – antonio Jan 17 '22 at 12:38