My PowerShell script does not work when opening it from a batch file or the command line, but it works when running directly

345 Views Asked by At

What I am trying to do:

Use a .bat file to call a .ps1 file (don't ask) that generates a self-signed certificate and key pair (.pem), then the .bat file will move each .pem to a new directory.

What I have done:

I have the .ps1 file working correctly, it generates both files, but it does require PowerShell to be run as administrator. I have the .bat file calling the .ps1 file when it should. Within the batch file, I am opening PowerShell, which then opens another PowerShell instance as administrator (because there is no way to run PS as admin from the command line, correct me if I'm wrong). Here is the line of code:

PowerShell -Command "& {Start-Process PowerShell -ArgumentList '-NoExit -File ""cert-gen.ps1""' -Verb RunAs}"

What is happening:

When this line is run, I see a PS window pop up and immediately close and it does not generate my cert and key. Since it is closing immediately, I cannot see if there any error within that window, and the command line does show any errors. I am pretty sure I have -NoExit in the correct place.

Bonus:

This needs to be fully automated with no user interaction, so the prompting for UAC elevation is not going to work. I eventually need to figure out a way around this as well, so if there is a solution that fixes all of these problems in one go, that is even better!

1

There are 1 best solutions below

0
mklement0 On
:: From a batch file
PowerShell -Command "Start-Process PowerShell -ArgumentList '-NoExit -File \"%CD%\cert-gen.ps1\"' -Verb RunAs"
  • The primary problem was that when powershell.exe, the Windows PowerShell CLI, is invoked with elevation, it invariably uses C:\Windows\System32 as the working directory. Thus, it wasn't able to find your cert-gen.ps1 script there.

    • %CD%\cert-gen.ps1 ensures that the script is passed by its full path, which solves that problem. %CD% is expanded by cmd.exe up front, to the full path of the current directory.

    • Note that your script must be prepared to handle running with C:\Windows\System32 as the working directory; if needed, it can use the automatic $PSScriptRoot variable to refer to its own directory, for instance.

    • As an aside: It is unfortunate that a CLI session invoked via -NoExit automatically closes if the script targeted with -File isn't found (when using -Command with a failing command, the session stays open); GitHub issue #10471 proposes changing this behavior.

  • The unnecessary & { ... } enclosure was removed:

    • There's no reason to use "& { ... }" in order to invoke code passed to PowerShell's CLI via the -Command (-c) parameter - just use "..." directly. Older versions of the CLI documentation erroneously suggested that & { ... } is required, but this has since been corrected.
  • While "" for escaping embedded " chars. happens to work in this case, it doesn't work robustly, so \" is used instead:

    • However, with pwsh.exe, the PowerShell (Core) 7+, "" does work robustly, and is preferable to \", because it avoids edge cases where cmd.exe's parsing can break a command - see this answer for details.

As for the UAC part of the question:

  • The only secure way to prevent the batch file from triggering a UAC prompt is to run it from an already elevated session - but note that starting such a session itself will trigger a UAC prompt.

    • If you ensure that your batch file therefore as a whole runs with elevation (which itself could be considered a security risk, if not all operations in it actually require elevation), you can simplify your PowerShell CLI call to (if -NoExit was just for troubleshooting, consider removing it):

      PowerShell -NoExit -File cert-gen.ps1
      
    • That is, direct invocation is then possible, because child processes launched from an elevated process are elevated too.

  • Insecure alternatives are discussed in this answer.