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

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!

  • You start CMD.exe (a command line interface) to start a PowerShell.exe session (another command line interface) where you start another PowerShell.exe session (again another command line interface). Sorry, but I cannot “(don’t ask)” … 🤷🏼‍♂️🤔

    – 




  • 1

    @Olaf – I use that technique regularly when I’m in scenarios where powershell’s default execution policy doesn’t allow for executing it directly, like at work with decent security policies.

    – 

  • 2

    If the batch script is run as an administrator, the subsequent powershell calls will be run as administrator as well.

    – 

  • @Olaf – I understand, it is a mess. This is being implemented into an existing complex chain of batch scripts. There are actually a few other layers of scripts calling other scripts that I am leaving out on purpose. The only reason for starting PowerShell and starting another PowerShell is to run that one as administrator, which is the only way I found to do this, stackoverflow.com/questions/19335004/…

    – 




  • @SomethingDark – Yep, that worked. I simplified the .bat line to just PowerShell -File "cert-gen.ps1" and the .ps1 is doing what it’s supposed to do. Now my only hang-up is that I’m not sure if the previous scripts in the chain are launched as admin. I don’t think they are. But regardless, this gets me back on track. Thank you!

    – 

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

Leave a Comment