Start-Process with auto elevated permissions and passing command inline - powershell

I need to start a windows service on the local computer through PS by directly running the PS script w.o the need to manually elevate the permissions. This code works for me:
Start-Process powershell -Verb runas -ArgumentList "-file MyFileName.ps1"
Where MyFileName.ps1 contains:
Start-Service MyServiceName
But I want to keep it simple and instead of storing the command into a separate file, I want to run a single script. The following does not work for me:
Start-Process powershell -Verb runas -ArgumentList "-command '& {Start-Service MyServiceName}'"
What am I missing?

Start-Process invokes a new process. The invocation doesn't recognize single quotes as quoting characters, so instead of passing a parameter -command with a command string '&{Start-Service MyServiceName}' you're passing 4 tokens: -command, '&, {Start-Service, and MyServiceName}.
Change this:
"-command '& {Start-Service MyServiceName}'"
into this:
"-command `"& {Start-Service MyServiceName}`""

Related

How do I call an elevated PowerShell from Windows command prompt to execute commands and a PowerShell script file?

The following script works fine in Powershell:
Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Webrequest
'https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1' -OutFile
C:\script.ps1; C:\script.ps1
I'm trying to convert it so that it runs as a CMD/BAT file. I simply need to double click to run it. I need the CMD/BAT file to run Powershell as administrator and from there, it will run the script from above. Here's what I have. It'll just quit straight away without doing anything.
powershell -ExecutionPolicy Bypass -c Start-Process -Verb RunAs -Wait
powershell.exe '-ExecutionPolicy Bypass -Noexit -c Set-Location
"\"\\\"%CD%\\\"\""; -c "& Invoke-Webrequest
\"https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1\"
-OutFile C:\script.ps1; C:\script.ps1" '
Update:
I got it working now. The full output looks like this:
powershell -Command "& ({Start-Process powershell -Verb RunAs -ArgumentList '-ExecutionPolicy Bypass -NoExit -Command Invoke-WebRequest -Uri https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1 -OutFile C:\script.ps1; C:\script.ps1'})"
If you use the -NoExit parameter when starting powershell.exe PowerShell should not close after the script has finished running. Afterwards if you do not see errors, you can look at the $error variable, to see any errors which occurred when running your script.
You are trying to run your PowerShell code, using PowerShell.exe from command prompt, so you should be able to use the same code as you did before, when you were running your code directly in PowerShell before.
To my understanding you want to run powershell code in a batch-file.well Depending upon your preference there are multiple solutions to this problem(well i haven't checked any of them):
Solution:1
make a batch file and give your powershell script to it as a parameter.
Without Admin access:
#ECHO OFF
PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%~dpn0.ps1'"
PAUSE
With Admin access:
#ECHO OFF
PowerShell.exe -NoProfile -Command "& {Start-Process PowerShell.exe -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File ""%~dpn0.ps1""' -Verb RunAs}"
PAUSE
Solution:2
If you don't want an external .ps1 file,then you can try this.just save it as something.bat
powershell -command if ($true)^
Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Webrequest 'https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1' -OutFile C:\script.ps1; C:\script.ps1^
This should do the trick:
powershell -Command "& {Start-Process powershell -Verb RunAs -ArgumentList '-ExecutionPolicy Bypass -NoExit -Command Set-Location -Path C:\whatever\working\directory\you\need; Invoke-WebRequest -Uri https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1 -OutFile C:\script.ps1; C:\script.ps1'}"
Explanation:
When executing commands in PowerShell using the -Command argument, ExecutionPolicies do not apply as your are executing a single command and not a script. Even the execution of a scriptblock consisting of multiple commands counts as the execution of a single command. That's why you can directly call PowerShell from the command prompt like this (without anything else):
powershell -Command ...
-Command expects - to read from stdin or a scriptblock (read more). Scriptblocks have to be enclosed in curly braces ({...}). If you pass a scriptblock from the command prompt to PowerShell, you also have to add the call operator &:
powershell -Command "& {...}"
As you need an elevated PowerShell, you start a new PowerShell process from the previous PowerShell with Start-Process in combination with the -Verb RunAs argument. You add all arguments that you want to pass to the elevated PowerShell to the -ArgumentList argument:
... Start-Process powershell -Verb RunAs -ArgumentList '...' ...
As you want to call a script file, you now need the corresponding ExecutionPolicy. If you don't want to change it on the system, you can bypass the ExecutionPolicy with -ExecutionPolicy Bypass as you already did. And you also add -NoExit here. To pass your desired PowerShell commands, you use the -Command argument again. This time, you don't need the call operator and you can also omit the curly braces as we are now in PowerShell and not in the command prompt anymore.
... -ExecutionPolicy Bypass -NoExit -Command Set-Location -Path C:\whatever\working\directory\you\need; Invoke-WebRequest -Uri https://blah.blob.core.windows.net/laps/AutoAutoPilot.ps1 -OutFile C:\script.ps1; C:\script.ps1 ...

Call variable from within a string

I'm trying to use Powershell to open AD U&C in another domain (/domain switch) using a variable.
No domain switch used, works (just not the domain I want):
Start-Process powershell -Credential $domvar'\'$id -ArgumentList '-command &{Start-Process mmc -verb Runas -ArgumentList "C:\Windows\System32\dsa.msc"}'
Defining a specific domain in an array works:
Start-Process powershell -Credential $domvar'\'$id -ArgumentList '-command &{Start-Process mmc -verb Runas -ArgumentList #("C:\Windows\System32\dsa.msc" /domain=domain.com)}'
BUT
Adding /domain=$domain does not (double quoting everything in the ArgumentList did not help):
Start-Process powershell -Credential $domvar'\'$id -ArgumentList '-command &{Start-Process mmc -verb Runas -ArgumentList "C:\Windows\System32\dsa.msc" /domain=$domain}'
NOR
Changing domain.com to my variable $domain:
Start-Process powershell -Credential $domvar'\'$id -ArgumentList '-command &{Start-Process mmc -verb Runas -ArgumentList #("C:\Windows\System32\dsa.msc" /domain=$domain)}'
Also, a new window pops up and I see the red error font but I'm not sure how to grab that information. Tried using debugging and stepping into but didn't work.
In powershell single quotes do NOT perform variable substitution. In your case you can probably just change the single quotes around your command to double quotes. For the double quotes inside the quoted string you can include the $ as a literal by preceding it with a backtick.
"-command &{Start-Process mmc -verb Runas -ArgumentList `"C:\Windows\System32\dsa.msc`" /domain=$($domain)}"

How to Run Long Powershell script from Windows Command Prompt (CMD)

I tried to launch a long powershell script with the name "long name here.ps1" from command prompt. But I am also trying to ensure that it runs as an administrator command in powershell. I have all execution policies in powershell set accordingly I used the ss64 set-executionpolicy command guide for powershell to get powershell working. But I am trying to use the solution from another stackoverflow question that talks about running commands as administrator. I am running a batch script that needs to execute a powershell script (.ps1) as admin, and I don't mind if the user is prompted by UAC or for the password. I am currently using the following command:
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file "C:\long name here.ps1"' -verb RunAs}"
I found this command at https://ss64.com/ps/powershell.html at the bottom where there are details on how to run a powershell command as administrator. The problem with that code is that my powershell script 1. has arguments, and 2. has a long name. I have tried many different iterations of this command with no success, and the ones that DON'T work are listed below:
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file C:\long` name` here.ps1' -verb RunAs}"
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file:"C:\long name here.ps1' -verb RunAs}"
Also, I am completely lost as to how to send arguments to the actual script.
If I'm reading your question correctly - powershell wont find the file as it stops reading the path name when it encounters a blank space?
The example given here specifies that; powershell commands to be run from command prompt as an administrator should have the following syntax:
powershell.exe -noprofile -command "&{ start-process powershell -ArgumentList '-noprofile -file MyScript.ps1' -verb RunAs}"
Couple of ways to achieve what you're looking for. But the easiest method would be to escape the quotes using a ` character. So something similar to;
powershell.exe -noprofile -command "&{ start-process powershell -ArgumentList '-noprofile -file `"C:\long file name.ps1`"' -verb RunAs}"
Also might be worth checking out other answers here
Use a Freeware Third Party Utility
If a freeware third-party executable is permissible, you can use a short tool I wrote called elevate32.exe (32-bit) and elevate64.exe (64-bit) to launch powershell.exe as administrator with the -File parameter and the script arguments you want to use:
elevate64 -- powershell.exe -File "<path>\<long script name>.ps1" -Arg "<long script argument>"
You can get the tool from www.westmesatech.com (copyrighted freeware, free to use anywhere, no installation needed).
Use a WSH Script
If you can't use an external executable, you can also do this (although it does not handle quoting in as robust a manner as the elevate tool's -- parameter) using a Windows Script Host (WSH) script, elevate.js:
var args = WScript.Arguments;
if ( args.Length >= 1 ) {
var exec = args.Item(0);
var cmdLine = "";
for (var i = 1; i < WScript.Arguments.Length; i++ ) {
cmdLine += cmdLine == "" ? '"' + args.Item(i) + '"' : ' "' + args.Item(i) + '"';
}
var shellApp = new ActiveXObject("Shell.Application");
shellApp.ShellExecute(exec, cmdLine, "", "runas");
}
You can call as follows:
wscript.exe "d:\path\elevate.js" powershell.exe -File "C:\long path\script name.ps1" "long script argument"
Self-Elevate your PowerShell Script
Another option is to write a self-elevating PowerShell script. You can check for elevation in the script; if not elevated, it can launch itself elevated and run any command you need. Example:
$isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ( -not $isElevated ) {
Start-Process powershell.exe "-File",('"{0}"' -f $MyInvocation.MyCommand.Path) -Verb RunAs
exit
}
& "d:\long path name\script name.ps1" "Long Argument 1" "Long Argument 2"
When you use PowerShell.exe -Command you don't need to use quotes. For example, you can run the following:
PowerShell.exe -Command Get-Service 'wuauserv'
Everything after -Command is interpreted as the command. Note also that double quotes in CMD need escaping with a backslash. Therefore:
powershell.exe -Command Start-Process PowerShell -ArgumentList '-NoProfile -File \"C:\long name here.ps1\"' -Verb RunAs
If your file has arguments:
powershell.exe -Command Start-Process PowerShell -ArgumentList '-NoProfile -File \"C:\long name here.ps1\" \"Arg1\" \"Arg2\"' -Verb RunAs

Launch Elevated CMD.exe from Powershell

I am trying to launch an elevated CMD window from PowerShell but I am running into some issues. Below is the Code I have now. There is an admin account on the machine that has the username of "test" and a Password of "test"
$username = "test"
$password = ConvertTo-SecureString "test" -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
Start-Process "cmd.exe" -Credential $cred
This is all working fine for running an application from the user profile with no administrator rights that this script sits in, but when calling the cmd.exe it launches as expected with elevated rights but then immediately closes.
I have also tried calling it with the following:
Start-Process "cmd.exe" -Credential $cred -ArgumentList '/k'
This also is not working.
I tested the elevated permissions by passing in an argument as follows and this works fine.
Start-Process "cmd.exe" -Credential $cred -ArgumentList 'dir > dir.txt'
This will write out a dir.txt file to the C:\Windows\System32\WindowsPowerShell\v1.0 directory which is blocked on the user account but not for the administrator account test.
Any help on getting a persistent cmd window to show would be greatly appreciated.
Thanks
Note: SomeShinyObject came up with the fundamentals of the approach in his answer, but his parameter-passing technique is not robust (update: since corrected) - do not use script blocks in lieu of strings - see bottom.
-Verb RunAs is what makes Start-Process launch a process elevated.
However, -Verb RunAs cannot be combined with the -Credential parameter, so you cannot directly control under what user account the elevation happens - but that is generally not necessary:
If the current user is an administrator, elevation invariably happens in the context of that user, with the GUI prompt just asking for confirmation.
Otherwise, a GUI dialog is shown, asking for an administrator's username and password (with the username field blank).
Security caveats:
Storing a password as plain text is a security risk in general.
Additionally, if you let non-administrative users execute the code below with stored admin credentials, you're effectively giving them administrative rights.
If you still want to implement your script as specified, a workaround requires nesting 2 Start-Process calls:
The 1st one runs an (invariably) non-elevated command invisibly in the context of the specified user - assumed to be an administrative user - using -Credential.
Since the specified user is an administrator, it's not a concern here, but if -Credential targets a non-admin user, it is advisable to also specify a -WorkingDir argument that the specified user is known to have permissions to access - otherwise, the call may fail (the current location is retained, and the target user may not be allowed to access it).
The 2nd one, embedded in the 1st, then uses -Verb RunAs to run the target command elevated, which then happens in the context of the specified user.
Note: Even with a credentials object that includes the password, you will still get the yes/no UAC prompt to confirm the intent to elevate - unless UAC has been turned off (which is not advisable).
The working directory will invariably be $env:SYSTEMROOT\System32; -Verb RunAs ignores even a -WorkingDirectory value; if you want to change to a specific directory, embed a cd command in the command passed to cmd.exe; the bottom section of this related answer shows this technique with a powershell.exe / Set-Location call.
This command does exactly what you asked for - please note the security caveat:
# Construct the credentials object
$username = "jdoe"
# CAVEAT: Storing a password as plain text is a security risk in general.
# Additionally, if you let non-administrative users execute this
# code with a stored password, you're effectively giving them
# administrative rights.
$password = ConvertTo-SecureString "test" -AsPlainText -Force
$cred = New-Object PSCredential -Args $username, $password
# Start an elevated Command Prompt (cmd) as user $username.
Start-Process powershell.exe -Credential $cred -WindowStyle Hidden `
'-noprofile -command "Start-Process cmd.exe -Verb RunAs"'
Note that the embedded, 2nd command is passed as a single string to the (implied) -ArgumentList (a.k.a. -Args) parameter.
In this simple case, with only 1 level of embedded quoting - the " instances inside the '...' string - and no need for expansions (string interpolation), passing a single string is a viable option, but with more sophisticated commands quoting gets tricky.
-ArgumentList is defined as type [string[]], i.e., an array of string arguments. If you pass multiple, ,-separated arguments, it is PowerShell that synthesizes the command line for you:
Caveat: A long-standing bug unfortunately requires that argument with embedded spaces be enclosed in embedded double-quoting - see this answer for details.
The following command demonstrates this technique: It is a variant that passes a command for cmd.exe to execute through, and uses a variable reference in that command:
$msg = 'This is an elevated Command Prompt.'
Start-Process powershell.exe -Credential $cred -WindowStyle Hidden -Args `
'-noprofile', '-command', "Start-Process cmd.exe -Verb RunAs -Args /k, echo, '$msg'"
The cmd.exe command that is ultimately executed (with elevation) is:
cmd /k echo This is an elevated Command Prompt.
Optional Reading: Why using script blocks in lieu of strings is ill-advised
tl;dr
Do not get into the habit of using script blocks where strings are expected. While convenient, it is not robust, and remembering when and why it will fail is nontrivial.
At first glance, script blocks ({ ... }) seem like a convenient option:
Start-Process cmd -ArgumentList { /k echo hi! }
The above executes cmd /k echo hi! in a new console window, as expected.
The syntax is convenient, because the { ... } seemingly provide a context in which quoting is easy: you're free to use embedded " and ' instances to construct your command line.
However, what happens behind the scenes is that a script block is converted to a string, because that's the type of argument(s) -ArgumentList expects, and when a script block is converted to a string, its literal contents - everything between { and } - is used.
This means that no string interpolation takes place, so you cannot use variables or subexpressions.
Take this attempt to pass a command based on a variable:
Start-Process cmd -ArgumentList { /k echo Honey, I`'m $HOME! }
What this will execute is: cmd /k echo Honey, I'm $HOME! - $HOME was not expanded.
By contrast, passing either an interpolated string or the arguments individually works as intended:
# As a single string (argument list):
Start-Process cmd -ArgumentList "/k echo Honey, I'm $HOME!"
# As an array of arguments:
Start-Process cmd -ArgumentList /k, echo, "Honey, I'm $HOME!"
$HOME is expanded (interpolated) in both cases, and something like
cmd /k echo Honey, I'm C:\Users\jdoe is executed.
The best way is to double up your Start-Process first with a -Credential parameter with your admin credentials and then with a -Verb runas on your second Start-Process. After that it gets a little complicated with quoting for CMD.exe.
Overall, it should look something like this.
Start-Process PowerShell -ArgumentList {-noexit -noprofile -Command "Start-Process powershell -argumentlist {-command cmd.exe -args \"/K #yourcommands# \"}" -verb runas} -Credential $Cred
So scratch this. Read mklement0's answer as to why
There's always opportunity to learn more and I didn't know wrapping an ArgumentList in a ScriptBlock prevented variable expansion. So...don't do that.
The method stays the same, though. You'll still need two Start-Process calls, just now you have to get the quoting right.
#Both of these work
Start-Process powershell -Credential $cred -ArgumentList "-noprofile", "-command", "Start-Process cmd.exe -Verb RunAs -ArgumentList /k, echo, 'something'"
# $Something will expand into it's value rather than literally
Start-Process powershell -Credential $cred -ArgumentList "-noprofile", "-command", "Start-Process cmd.exe -Verb RunAs -ArgumentList /k, echo, '$something'"

Send a command to a powershell opened with "Start-Process powershell -Verb runas"

I want to open an admin powershell and send it a command (eventually a script). Right now, it doesn't matter what command, but I've tried things like:
Start-Process powershell -Verb runas < $something
or
$something | Start-process powershell -Verb runas
just to get some text to show up in the new admin powershell window. Any ideas?
That awkward moment when you do a little more research and find what you want: this link will help anyone: how to execute set of commands in elevated mode of powershell
Essentially, add the -argument argument to your command
Start-Process powershell -verb runas -argument dir