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
Related
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.
I'm trying to use Windows Sandbox with a PowerShell logon command. This is the LogonCommand section of my WSB file:
<LogonCommand>
<Command>C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -executionpolicy unrestricted -file "C:\\Users\\WDAGUtilityAccount\\Desktop\\boot.ps1" -noexit</Command>
</LogonCommand>
The Windows Sandbox instance loads up okay suggesting no syntactic/validation issues with the WSB file content, but the PowerShell window is not shown. Adding -windowstyle normal has no effect.
I suspect the LogonCommand content is run in a command prompt which is not made visible so running the command to open PowerShell from it somehow "inherits" the terminal window not being visible.
Is it possible to force the PowerShell terminal window to reveal itself in such a case? I want to do this so that I can see the errors that I get because the PowerShell script is not executing as expected and I'm blind to any output/progress indication.
Found an answer (doesn't look like the cleanest option, but works):
<Command>powershell -executionpolicy unrestricted -command "start powershell {-noexit -file C:\Users\WDAGUtilityAccount\Desktop\boot.ps1}"</Command>
powershell switches from CMD to PowerShell
-windowstyle normal won't work to make this PowerShell window visible
-executionpolicy unrestricted allows the nested PowerShell to run from file
start powershell runs another PowerShell with visible window
Running this directly for LogonCommand will not work
-noexit tells the nested PowerShell to remain visible
This is not necessary but it is useful for debugging the script errors
-file C:\Users\WDAGUtilityAccount\Desktop\boot.ps1 runs the given script
Share it with the machine by using a MappedFolder in the WSB configuration
When creating a Windows shortcut to launch a PowerShell script the following works fine when double clicked as a regular user and with right click Run as administrator:
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "Bypass" -Command "&{& 'C:\Script.ps1'}"
However, when the path is relative and not known upfront the following works fine when double clicked as a regular user but not with right click Run as administrator:
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy "Bypass" -Command "&{& '.\Script.ps1'}"
My question, how can I have it work in both cases when the path is relative? I tried using PSScriptRoot but that didn't work either.
Thank you for your help.
When launching as admin from Explorer, you must provide an absolute path to the script.
Explorer.exe ignores the starting directory from the shortcut when launching a process as admin. Instead, Admin-level processes always launch with the current directory in [Environment]::GetFolderPath('System') (usually C:\Windows\System32)
The easy way to run in a different directory is to change directory at the beginning of your script. The following line will cd to the directory the script is in.
Set-Location $PsScriptRoot
If the script needs to start in a different path, then you may have to write a function to discover where that path is on the local machine (such as enumerating USB drives)
You can use your current solution for non-admin promoted shortcuts then auto promote the script internally:
# ========================================= Admin Rights =======================================================
# Usage: asAdmin $PSCommandPath
function asAdmin
{
[string]$cmdPath = $args[0]
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$cmdPath`"" -Verb RunAs; exit }
}
Environment: Windows 7
With the help of straight forward article from Scott, I am able to have "PowerShell Here" as the right click item of windows explorer.
Right clicking "PowerShell Here" opens a powershell command prompt with selected folder as the current working directory.
But I want little bit different which I am not able to do - inplace of opening the powershell prompt, I want to run a powershell script taking argument as the selected drive/folder/filename!
So, I updated the following line of "powershellhere.inf" file,
;existing one
;HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -NoExit ""cd '%1'"""
;updated one, added -Command <ScriptFile>
HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -Command d:\temp.ps1 -NoExit ""cd '%1'"""
But when I right click and select the "PowerShell Here", it's not running the script in the selected drive/folder/file, it's running in C:\Windows\System32 folder.
The string I believe you are looking for:
"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -NoExit -File ""C:\temp\test.ps1"" ""'%1'"""
-NoExit had to be before the -File else it was being picked up as an argument to the ps1 file. Also you need to put a full file path. I'm sure you could use environment variables. In my script i just had Write-Host $args[0] which output the path.
I was having an issue with the path being passed at first. I think the single quotes were not the ones I expected them to be inside the script. My script now changes directory successfully. Contents of test.ps1
$location = $args[0].Trim("'")
Write-Host "Path is valid = $(Test-Path $location)"
Set-Location $location
Use -File instead of -Command and get rid of the cd stuff:
HKCR,Drive\Shell\PowerShellHere\command,,,"""C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe"" -File d:\temp.ps1 -NoExit
-Command is for literal commands on the command line. You may also want to set the -ExecutionPolicy if you have an issue with that.
If you don't want the prompt to stick around after executing the script, get rid of -NoExit.
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.