1

I am trying to write a Powershell script to automate general tasks on a new computer including installing software. ExecutionPolicy has been the bane of my existence when it comes to blocking me from running automated scripts that I have been writing.

One of my PS scripts requires elevated Admin privileges, so I have a batch file where I am calling to launch PS as Administrator but I am still getting stuck with the script instantly closing/failing due to ExecutionPolicy. Ideally, I would like a way to bypass the ExecutionPolicy on a per script basis instead of completely changing it and then changing it back.

I have looked up different Set-ExecutionPolicy methods but they don't seem to work.

I need a generic command in a .bat file to launch a specified powershell script as Admin AND bypassing the current ExecutionPolicy.

  • 2
    Bypass is your answer. Either that, or sign your scripts. Unless you want to unrestrict all – Abraham Zinala Mar 04 '22 at 01:38
  • 2
    You know there is a command line argument for the execution policy for the PowerShell executable, don't you? You may run `PowerShell /?` to read more about. ;-) – Olaf Mar 04 '22 at 01:45
  • 1
    I'm curious what happens when you load the ps1 script as ScriptBlock and execute it there. Seems I read someplace there are fewer restrictions on code ran in a ScriptBlock. – Darin Mar 04 '22 at 02:39
  • @Darin, scratch my last comment - I misread yours. You are correct: Execution policies only relate to script _files_, so reading a file's _content_ into memory and executing that - be it via constructing a script block first or by passing it directly to the usually-to-be-avoided `Invoke-Expression` - is a way to bypass the active execution policy. That said, the technique won't work if the code executed this way calls _other_ script files, which may also happen _implicitly_ when modules are auto-imported. – mklement0 Mar 04 '22 at 14:14
  • 1
    @mklement0, I knew my comment had at least some validity independent of your confirmation, which is why I just let it slide. But I do appreciate you letting me know what you found out. The particular command I've been working on, and works so far in my situation, is powershell with RemoteSigned (If I hit a wall someday, I may have to switch to bypass). In the command there is prep of several variables, including flags indicating the batch is in pipe or called from GUI, and then a scriptblock wrapped around a scriptblock of a file with the batch parameter line. 437 chars not counting indent – Darin Mar 04 '22 at 15:16
  • Thanks, @Darin. On a meta note, while I understand that it's easier and sometimes even better to let things slide, I am always happy to correct my mistakes (which may involve simple deleting comments that were based on a misconception), so if you're up for it, tell me - sometimes both parties learn in the process. As for your technique: I don't fully understand (it's fine), but let me make one more general point: Unless GPOs control your execution policy, the `-ExecutionPolicy` CLI parameter works and requires no workarounds. Otherwise, the limitations stated in my previous comment apply. – mklement0 Mar 04 '22 at 15:44
  • 1
    @mklement0, to partly remedy "I don't fully understand", you need to understand the goal. Single design for a batch file that calls ps1 file, accepts pipeline input that is passed to ps1 file, can recognize it has either been double clicked or files dragged-N-dropped on it - and ps1 files can detect this, passes the command line as is for PowerShell to process, and doesn't have execution issues that ps1 file have. I think I achieved this goal about 2 weeks ago, with the possible exception of execution policy. I think I need to sit down and really get my head wrapped around it. – Darin Mar 04 '22 at 16:18

1 Answers1

2

I need a generic command in a .bat file to launch a specified powershell script as Admin AND bypassing the current ExecutionPolicy.

The key is to use the -ExecutionPolicy CLI parameter (which only the inner powershell call below needs; the sole purpose of the outer one is to launch the target session with elevation (as admin), which itself doesn't involve execution of a script file):

  • Caveat: If your effective execution policy is controlled by GPOs (Group Policy Objects), it can not be overridden[1] - neither with the -ExecutionPolicy CLI parameter, nor in-session with Set-ExecutionPolicy - the solution below won't work. See this answer for details.

Also - unrelated to execution policies - additional work is required in order to preserve the caller's working directory, because elevated sessions default to the SYSTEM32 directory. Therefore, the command below incorporates an explicit Set-Location call to set the working directory. Note that in order to call a script located in that directory you need to prefix its name with .\ (e.g., .\script.ps1 instead of just script.ps1):

:: Run from cmd.exe / a batch file.
:: The target script file is '.\script.ps1' in this example, and
:: 'foo 1' and 'bar' are sample arguments to pass to it.
powershell -noprofile -c Start-Process -Verb RunAs powershell '-noexit -noprofile -ExecutionPolicy Bypass -c Set-Location -LiteralPath ''%CD:'=''''%''; ^& .\script.ps1 ''foo 1'' bar'

-noexit keeps the elevated session open after the specified .ps1 script terminates; remove it, if you want the session and thereby its window to close automatically.
-noprofile suppresses loading of the profile scripts; in the second powershell call, that isn't strictly necessary, but still advisable for a predictable execution environment.

Note:

  • In order to avoid "-related escaping hell, the above solution uses '...' strings only, as understood by PowerShell in the context of a -c (-Command) CLI call, which, however, assumes the following:

    • The script file name / path itself and the pass-through arguments mustn't contain ' chars. - if they do, they must be enclosed in ''....'' and the embedded ' must be escaped as '''' (sic).

      • For the Set-Location call above, this is handled automatically by using cmd.exe's string-replacement technique on its %CD% variable (%CD:'=''''%).
    • If the script file path or pass-through arguments contain spaces, there mustn't be runs of multiple spaces - if so (which would be very unusual in the case of paths), "-quoting would have to be used. Otherwise, values with spaces must be enclosed in ''...'', as shown with the foo 1 argument above.

    • Since the script file is invoked via -c (-Command) rather than via -f (-File), the interpretation of arguments passed to it may situationally differ - see this answer.


[1] There is a limited workaround: Execution policies only relate to script files, so reading a script file's content into memory and executing that - be it via constructing a script block first or by passing it directly to the usually-to-be-avoided Invoke-Expression - is a way to bypass the active execution policy. A simplified example:
powershell -noprofile -c "Invoke-Expression (Get-Content -Raw c:\path\to\foo.ps1)". That said, the technique won't work if the code executed this way calls other script files, which may also happen implicitly when modules are auto-imported.

mklement0
  • 382,024
  • 64
  • 607
  • 775