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!
:: 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 usesC:\Windows\System32
as the working directory. Thus, it wasn’t able to find yourcert-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 bycmd.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.
- There’s no reason to use
-
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 wherecmd.exe
‘s parsing can break a command – see this answer for details.
- However, with
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.
You start
CMD.exe
(a command line interface) to start aPowerShell.exe
session (another command line interface) where you start anotherPowerShell.exe
session (again another command line interface). Sorry, but I cannot “(don’t ask)” … 🤷🏼♂️🤔@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.
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!