Powershell Elevated and pass a command - powershell

So I am building a script that launches an executable in the common files folder of x86 folder. The issue I am having is the script uses the (x86) as if the x86 is a command that should be run first. Does anyone have any ideas on how to prevent that?
$Pulse = "${env:CommonProgramFiles(x86)}\Pulse Secure\JamUI\JamCommand.exe"
$ImportFile = '"-importfile "C:\Program Files (x86)\MyFile.preconfig"'
Start-Process powershell -ArgumentList "-noexit", "-noprofile", "-command &{Start $Pulse, $ImportFile}" -Verb Runas

As Mathias notes, there is no strict need to launch another PowerShell instance with elevation (run as admin, -Verb RunAs), because you can directly elevate the target executable, JamCommand.exe; however, if you want to run the executable in another PowerShell session that stays open afterwards - as your use of -noexit suggests - you indeed do:
$pulse = "${env:CommonProgramFiles(x86)}\Pulse Secure\JamUI\JamCommand.exe"
$importFileArg = '-importfile "C:\Program Files (x86)\MyFile.preconfig"'
Start-Process -Verb RunAs powershell #"
-noexit -noprofile -c & "$pulse" $importFileArg
"#
Note:
This runs JamCommand.exe directly in the elevated PowerShell session, synchronously (assuming it is a console application), and the interactive session is only entered after that program exits. (Your attempt contains another Start-Process call (via alias Start), which would make a console program run in yet another console window, which auto-closes when the program exits.)
All pass-through arguments are encoded in a single (here)-string passed to the (positionally implied) -ArgumentList parameter, which allows direct control of the command line that is used to launch process, and is generally the most robust approach due to a long-standing bug in how passing arguments individually, as elements of an array is handled - see this answer.

Related

Trying to run a headless executable command through Powershell that works on cmd line

I am trying to run an executable through powershell to run headless, to install a program onto a VM/LocalHost machine. I can get the wizard to open, but for whatever reason I cannot get it to run headless. Here is the cmd line that I run that works:
start /WAIT setup.exe /clone_wait /S /v" /qn"
This is my attempts in powershell
Start-Process .\setup.exe /S -Wait -PassThru
Start-Process .\setup.exe /S /v /qn -Wait -PassThru
Start-Process setup.exe -ArgumentList '/clone_wait /S /v /qn' -Wait
In the cmd line instance the application installs without issue - in the powershell instance the wizard opens and is on the first "Next" prompt. Any help would be appreciated!
I also attempted to add the additional parameters "/v" and "/qn" which return an error : Start-Process : A positional parameter cannot be found that accepts argument '/v'
The bottom attempt runs but it's not waiting for the installation to complete
You may be overthinking it. Remember that PowerShell is a shell. One of the purposes of a shell is to run commands that you type.
Thus: You don't need Start-Process. Just type the command to run and press Enter.
PS C:\> .\setup.exe /clone_wait /S /v /qn
Now if the executable (or script) you want to run contains spaces in the path or name, then use the call/invocation operator (&) and specify the quotes; for example:
PS C:\> & "\package files\setup.exe" /clone_wait /S /v /qn
(This behavior is the same no matter whether you are at the PowerShell prompt or if you put the command in a script.)
This worked for me. You need to quote the whole argumentlist, plus embed double quotes to pass what you want to /v.
start-process -wait SetupStata16.exe -ArgumentList '/s /v"/qb ADDLOCAL=core,StataMP64"'
Running the command normally and then using wait-process after might be a simpler alternative, if you're sure there's only one process with that name:
notepad
wait-process notepad
To follow-up to all that you have been given thus far. Running executables via PowerShell is a well-documented use case.
PowerShell: Running Executables
Solve Problems with External Command Lines in PowerShell
Top 5 tips for running external commands in Powershell
Using Windows PowerShell to run old command-line tools (and their
weirdest parameters)
So, from the first link provides more validation of what you've been given.
5. The Call Operator &
Why: Used to treat a string as a SINGLE command. Useful for dealing with spaces.
In PowerShell V2.0, if you are running 7z.exe (7-Zip.exe) or another command that starts with a number, you have to use the command invocation operator &.
The PowerShell V3.0 parser do it now smarter, in this case you don’t need the & anymore.
Details: Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are stored in variables and represented by strings. Because the call operator does not parse the command, it cannot interpret command parameters
Example:
& 'C:\Program Files\Windows Media Player\wmplayer.exe' "c:\videos\my home video.avi" /fullscreen
Things can get tricky when an external command has a lot of parameters or there are spaces in the arguments or paths!
With spaces you have to nest Quotation marks and the result it is not always clear!
In this case it is better to separate everything like so:
$CMD = 'SuperApp.exe'
$arg1 = 'filename1'
$arg2 = '-someswitch'
$arg3 = 'C:\documents and settings\user\desktop\some other file.txt'
$arg4 = '-yetanotherswitch'
& $CMD $arg1 $arg2 $arg3 $arg4
# or same like that:
$AllArgs = #('filename1', '-someswitch', 'C:\documents and settings\user\desktop\some other file.txt', '-yetanotherswitch')
& 'SuperApp.exe' $AllArgs
6. cmd /c - Using the old cmd shell
** This method should no longer be used with V3
Why: Bypasses PowerShell and runs the command from a cmd shell. Often times used with a DIR which runs faster in the cmd shell than in PowerShell (NOTE: This was an issue with PowerShell v2 and its use of .Net 2.0, this is not an issue with V3).
Details: Opens a CMD prompt from within powershell and then executes the command and returns the text of that command. The /c tells CMD that it should terminate after the command has completed. There is little to no reason to use this with V3.
Example:
#runs DIR from a cmd shell, DIR in PowerShell is an alias to GCI. This will return the directory listing as a string but returns much faster than a GCI
cmd /c dir c:\windows
7. Start-Process (start/saps)
Why: Starts a process and returns the .Net process object Jump if -PassThru is provided. It also allows you to control the environment in which the process is started (user profile, output redirection etc). You can also use the Verb parameter (right click on a file, that list of actions) so that you can, for example, play a wav file.
Details: Executes a program returning the process object of the application. Allows you to control the action on a file (verb mentioned above) and control the environment in which the app is run. You also have the ability to wait on the process to end. You can also subscribe to the processes Exited event.
Example:
#starts a process, waits for it to finish and then checks the exit code.
$p = Start-Process ping -ArgumentList "invalidhost" -wait -NoNewWindow -PassThru
$p.HasExited
$p.ExitCode
#to find available Verbs use the following code.
$startExe = new-object System.Diagnostics.ProcessStartInfo -args PowerShell.exe
$startExe.verbs

Open a powershell window from an existing powershell window, and keep it open after running code

I can open a powershell window from an existing one and provide it code to run here
But I can't get the second window to stay open once the code has run
Here's what I want to run in the first powershell window, and I would like the powershell window that opens to stay open after running the code (currently, it closes immediately)
start powershell { ECHO "hi" }
Note
I tried some suggestions here but not having any luck
Also, I got a fix (of sorts) using something like start powershell { ECHO "hi"; TIMEOUT 20 } but that's not going to keep the window permanently open
PowerShell -Command {Write-Host "Test" } -NoExit
from about Powershell. To start the Powershell in a new window you can use:
start-process powershell.exe -ArgumentList "-noExit", "-command","Write-host 'TEST'; Write-Host 'Test2'"
Important -Command must be the last parameter (about Powershell):
When the value of Command is a string, Command must be the last parameter specified because any characters typed after the command are interpreted as the command arguments.
Generally:
If you pass a command (-Command) or script file (-File) to execute to PowerShell's CLI (powershell.exe in Windows PowerShell, pwsh.exe in PowerShell [Core] v6+), PowerShell by default executes the command / script and then exits.
With a command or script specified for execution, you need to add the -NoExit switch if you want the new session to remain open.
Caveat: (Unless you call directly from within PowerShell with a script block), a positional argument - i.e., one neither preceded by -Command nor -File - is implicitly bound to:
-Command in Windows PowerShell
-File in PowerShell [Core] v6+.
Therefore, it's advisable to use the target parameter name explicitly.
With Start-Process (whose built-in alias on Windows - but not on Unix - is start):
Note: The primary use of Start-Process is to launch an independent process asynchronously in a new window. However, the latter isn't supported on Unix-like platforms, where Start-Process's utility is therefore limited.
start powershell { ECHO "hi" } happens to work from PowerShell (except that the window closes right after executing the command), but it's important to note that you cannot actually pass script blocks to Start-Process, only strings.
Start-Process accepts an executable name (implied -FilePath parameter), and an array of string arguments (implied -ArgumentList / -Args parameter).
If you pass a script block ({ ... }), it is automatically stringified, meaning that its literal string contents are used (stripped of the { and }) as the (only) -ArgumentList string value.
Thus, bypassing the unnecessary script-block creation, your command - with -NoExit applied as desired - should be (with explicitly named parameters; note that -Command and its argument must come last):
Start-Process -FilePath powershell -ArgumentList '-NoExit -Command ECHO "hi"'
Note:
While passing arguments individually, as an array to -ArgumentList is arguably conceptually cleaner, it is actually better to pass all arguments as a single string, using embedded quoting as necessary, due to a longstanding bug - see GitHub issue #5576.
Trying to open an interactive shell in a new window as a different user, via the -Credential parameter, is broken up to at least PowerShell 7.1, resulting in keyboard input getting blocked both in the new window and in the caller's window - see this answer for a workaround with runas.exe and GitHub issue #12129.

Run PowerShell, admin mode, in directory from shell context menu

I have created an Explorer Shell cascading context menu for opening PowerShell within a directory using the code shown below. (Note: I have a sample path hard-coded in the admin command for testing.)
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell\powershell.exe]
"MUIVerb"=""
"SubCommands"="powershell;powershell_admin"
"Icon"="PowerShell.exe"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell]
#="Open PowerShell here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell\command]
#="powershell.exe -NoLogo -NoExit -Command Set-Location '%V'"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin]
#="Open PowerShell (admin) here"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\powershell_admin\command]
#="powershell.exe -Command 'Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs'"
The non-admin command works perfectly. The admin command ignores everything in the ArgumentList.
If I open a PowerShell and execute the code within the single-quotes(') directly it works fine. E.G.:
Start-Process PowerShell -ArgumentList \"-NoLogo -NoExit -Command Set-Location C:\Python27\" -Verb RunAs
But when executing from the context menu it opens in Admin mode but displays the logo and doesn't execute the Set-Location.
Thanks in advance
This is a cool utility.
I installed the Registry entries to experiment, and now that it's working I'll probably keep it.
In my experiments the window closed immediately, even before it brought up the UAC dialog, until I enclosed the outer command in escaped double quotes and the inner parameters in single quotes. Then it worked fine for every folder except those under Program Files. For that we need to enclose %v in single quotes, and double-escape the double-quotes surrounding it:
#="powershell.exe -command \"start-process powershell.exe -ArgumentList \\\"-NoLogo -NoExit -Command Set-Location '%v'\\\" -verb RunAs\""
I used %v - the context folder name, which seemed to be your original intention based on the menu label 'Open Powershell (admin) here'.
Debugging notes:
For experimentation it's a little easier to make the changes in Regedit.exe and let the export functionality add the escaping characters as necessary
In Windows 10 you can make changes directly within Regedit.exe and the context menu/action is updated immediately, which I confirmed by adding a timestamp to the menu label
In other versions of Windows it may be necessary to stop and restart explorer.exe

Batch script not calling Powershell

I have a batch script that calls a Powershell file in administration mode. I found this code a while ago, and it's worked great ever since:
PowerShell -NoProfile -ExecutionPolicy Bypass -Command
"& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File %PSFile%' -Verb RunAs}";
This time though, I called the batch script from another program. This program says the process worked, but it didn't actually do anything. Examining the logs from echo, I can see the batch script is being called, but it's not calling Powershell. I tried running the batch script manually, and it calls PS fine, so something with how the batch script is being called by the other program is messing with how it calls PS.
This in mind, I tried changing the batch script to directly run my .ps1 file, instead of starting a new admin instance of powershell to start it. My new .bat file looked like this:
Powershell -File %PSFILE% -Verb RunAs
Calling this from the other program sucessfully calls my Powershell script, but I get a bunch of errors from the PS script, since it's not an admin PS session like it needs to be.
How can I change my batch script to call Powershell as an admin, without using Powershell to call itself (which doesn't seem to work with the program that needs to run it)?
EDIT: After trying a bunch of tweaks, I've found I don't even need to be in admin mode to do what this script does. However, I still get access denied errors when running it through the program (admin or not). So something about running it from the program is making it need more permissions than when I run the batch script manually.
This is what I do (inside the .bat file):
If the .bat is NOT running as admin
powershell.exe -Command "powershell.exe 'C:\path\to\script.ps1' -Verb runAs"
If the .bat is running as admin
powershell.exe -ExecutionPolicy Bypass -Command "C:\path\to\script.ps1"
You could use a small utility I wrote called elevate32.exe/elevate64.exe.
elevate64 -- C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File "d:\path to script\scriptfile.ps1"
elevate32.exe (32-bit version) and elevate64.exe (64-bit version) basically elevate whatever command line you pass to them.
You can get it here (ElevationToolkit1.zip):
http://www.westmesatech.com/misctools.html
An alternative is to use a short WSH script that, when called, provokes elevation. An example is on Aaron Margosis' blog here:
https://blogs.msdn.microsoft.com/aaron_margosis/2007/07/01/scripting-elevation-on-vista/
Script:
// elevate.js -- runs target command line elevated
if (WScript.Arguments.Length >= 1) {
Application = WScript.Arguments(0);
Arguments = "";
for (Index = 1; Index < WScript.Arguments.Length; Index += 1) {
if (Index > 1) {
Arguments += " ";
}
Arguments += WScript.Arguments(Index);
}
new ActiveXObject("Shell.Application").ShellExecute(Application, Arguments, "", "runas");
}
else {
WScript.Echo("Usage:");
WScript.Echo("elevate Application Arguments");
}
The limitations of this approach is that it relies on the WSH command-line parser and can't wait for the program to terminate. These limits may not be a problem in your scenario.
Looks like I was totally off as to the problem source. This was a permissions error on some folders I was editing. The program I was running the scripts through acts as a separate service. I had to add that with modify permissions to the security groups of all the folders I was editing. No elevation required in the scripts, just modifying permissions.

PowerShell: Run script from shortcut using relative path

EDIT: To future readers, in short, PowerShell scripts weren't intended to be used this way which is why there is no elegant solution.
I have the following line which runs a script as an administrator from a shortcut:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile
-noexit Start-Process Powershell -verb RunAs -ArgumentList "C:\Documents\WindowsPowerShell\Scripts\test.ps1"
I want to change:
"C:\Documents\WindowsPowerShell\Scripts\test.ps1"
to a relative path like:
".\test.ps1"
but I haven't figured out how I can do that. How can I run the script relative to the location of the shortcut? (The shortcut and script are in the same folder)
Here is an ugly workaround.
Shortcut.lnk file with Target: %COMSPEC% /C .\launcher.cmd (source) and Start In: %CD% (or blank).
Launcher.cmd file with contents:
Powershell -noprofile -noexit -File %CD%\PSlauncher.ps1
PSlauncher.ps1 file with contents:
Start-Process Powershell -verb RunAs -ArgumentList ($pwd.path + "\test.ps1")
Surely there is a better solution. Maybe with the -WorkingDirectory parameter of Start-Process? Or storing credentials with the Convert*-SecureString cmdlets? Count me curious.
Why do you want a shortcut?
After much trial and error, I've come up with a solution:
Create a shortcut with this (edited for your .ps1) to have scrips run as admin relative to any directory:
CMD /C PowerShell "SL -PSPath '%CD%'; $Path = (GL).Path; SL ~; Start PowerShell -Verb RunAs -Args \""SL -PSPath '"$Path"'; & '".\YourScriptHere.ps1"'"\""
You'll have to empty the shortcut's "Start in" field to have its relative path be set as the working directory.
Or, here's a script that will generate one of these shortcuts for each .ps1 in a directory (with "Start in" already cleared):
(GCI | Where-Object {$_.Extension -eq ".ps1"}).Name | ForEach-Object {
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut((GL).Path+"\$_ Run.lnk")
$Shortcut.TargetPath = 'CMD'
$Shortcut.Arguments = "/C PowerShell `"SL -PSPath `'%CD%`'; `$Path = (GL).Path; SL ~; Start PowerShell -Verb RunAs -Args \`"`"SL -PSPath `'`"`$Path`"`'; & `'`".\$_`"`'`"\`"`""
$Shortcut.IconLocation = 'PowerShell.exe'
$Shortcut.Save()
}
If needed, add -NoExit, -ExecutionPolicy Unrestricted, etc. just after the first \".
Notes:
The reason for a second, admin instance of PowerShell launching from the first, is that launching as admin directly (by ticking a shortcut's "Run as administrator" box), for some reason ignores "Start in" and always launches in System32.
CMD is being used to launch the first instance because PowerShell currently fails to resolve paths containing square brackets, interpreting them as regex characters. This would normally be avoided using the LiteralPath parameter (aka PSPath), but here, the path is being passed behind the scenes at launch, and it's up to the developers to fix (I just filed a bug report here).
When I run a script from a shortcut, it uses the path of the actual script. You can check the current directory with pwd (present working directory). You can check (and then use) the path of the script with split-path -parent $MyInvocation.MyCommand.Definition like the answer says in What's the best way to determine the location of the current PowerShell script?.
So to answer your question, you should already be able to use relative paths. Have you tried it? If so, what was your experience?
For your script, set it to open using Powershell by default. Create a shortcut for your script and assign it a hot key by right clicking on your shortcut, selecting properties, click the shortcut tab. Move your cursor the select shortcut key and define a shortcut key. Each time you press the shortcut key your script will run
This is definitely made out to be more difficult than it is.
This issue is more likely to affect you on Windows Server. On regular Windows, you can run Set-ExecutionPolicy unrestricted and it will stay in affect on the machine, on Windows Server (at least on AWS), setting the execution policy from a powershell script only lasts for the session of the script and it closes immediately so you can't see the error.
I just successfully modded the registry on an AWS instance bypassing group policy and can simply right click powershell scripts from any directory and send a shortcut to the desktop that is runnable.