Reputation: 850
For a long time I've been using a powershell script containing the following line to automatically start various processes as a different user AND with elevated permissions:
start-process powershell -credential <username> -argumentlist '-command &{start-process <executable.exe> -verb runas -argumentlist <c:\path\to\file\that\executable-should-open.ext>}' -workingdirectory c:\windows\system32
Formatting it this way lets me simply create a convenient shortcut to the script. This works fine... as long as <c:\path\to\file\that\executable-should-open.ext>
contains no spaces. However that file is now located at a filepath which contains spaces and that's something I cannot change.
There's countless threads out there dealing with how to properly escape the -ArgumentList
parameter argument(s) of Start-Process
, but none of the solutions seem to work for this case where the space is in the nested -ArgumentList
. I've tried for days to find the magic sauce combination of single and double quotes, backticks, backslashes, $variables
, $($variables)
, @("arguments","as","arrays")
, not using &
, etc., etc.
Can anyone help me achieve this? I'm certainly open to other solutions that don't use Start-Process
, but ideally the working solution should work in a standalone script, and once run, should leave no additional console windows open.
With most attempts that don't outright throw errors, the behavior I'm seeing is that c:\path\to\file\with spaces\file.ext
is interpreted by the nested Start-Process
cmdlet as two separate arguments, i.e. c:\path\to\file\with
and spaces\file.ext
.
I use this syntax to open several different administrative apps, but as a concrete example, here is the one I've been testing and failing to make work.
Original, working script:
start-process powershell -credential domain\username -argumentlist '-command &{start-process mmc -verb runas -argumentlist c:\custom-aduc.msc}' -workingdirectory c:\windows\system32`
New, non-working script:
start-process powershell -credential domain\username -argumentlist '-command &{start-process mmc -verb runas -argumentlist c:\new path\custom-aduc.msc}' -workingdirectory c:\windows\system32
For reference, the reason I can't change the path is because the custom-aduc.msc
is now being kept on OneDrive, which infuriatingly provides no way to change its local sync location from drive:\path\OneDrive - CompanyName
. This is a heavily supported feature request for OneDrive, but has not been committed to.
Thanks for any insights.
P.S. I've considered alternate solutions such as:
Having the script first download/copy the files to a local path without spaces, however, this prevents me from using the executable to modify the file, or I'd have to monitor changes and re-upload them.
Mounting the OneDrive path as a separate drive letter. But that's much less of a standalone solution, and seems heavy handed.
Upvotes: 1
Views: 1618
Reputation: 850
TL;DR: To answer the titular question: It might be possible, but it's much easier to simply split the Start-Process
commands into different scripts, to avoid nesting one inside the quotes of the other.
After lots more trial and error with escape syntax, I was able to find an acceptable solution. However I had to split it up into two separate script files, so as to avoid nesting the second Start-Process
(and thus its -ArgumentList
arguments) inside any other quotes.
script1.ps1
#runas /user:domain\username "powershell -file \`"c:\script2.ps1\`""
# Equivalent to the above, but provides a nicer credential prompt:
Start-Process -FilePath powershell -Credential domain\username -WorkingDirectory "c:\windows\system32" -ArgumentList "-File `"c:\script2.ps1`""
script2.ps1
Start-Process -FilePath mmc -Verb runas -ArgumentList "`"C:\path\with spaces\aduc.msc`""
The important part is that this way I can use the usual escaping methods for each -ArgumentList
, without them interfering with each other. It may still be possible to use combinations of cmd interpreter escapes (\
), and posh escapes (`
) to do this all in one line, but I don't have the patience to figure that out. It also may be possible to hack this into a single script, but I can't be bothered.
At this point, I can simply create shortcuts which call script1.ps1
to achieve the desired result, just like I used to.
I like to use this for at least 3 different executables, and with 3 different usernames (my average joe account, and two different superuser accounts). To avoid needing two scripts for each executable+username combination, I generalized the scripts like so:
script1.ps1
$app = $args[0]
$user = $args[1]
$script2 = "C:\path\to\script2.ps1"
# Dont run as different user if this is my usual Windows login
# Otherwise I have to enter a password for no good reason
if($user -eq "domain\non-su-account") {
& powershell -file "$script2" $app
}
else {
#runas /user:$user "powershell -file \`"$script2\`" $app"
# Equivalent to the above, but provides a nicer credential prompt:
Start-Process -FilePath powershell -Credential $user -WorkingDirectory "c:\windows\system32" -ArgumentList "-File `"$script2`" $app"
}
script2.ps1
$app = $args[0]
switch($app) {
"aduc" {
Start-Process -FilePath mmc -Verb runas -ArgumentList "`"C:\path\with spaces\aduc.msc`""
break
}
"posh" {
Start-Process -FilePath powershell -Verb runas -ArgumentList "-nologo -noexit -command `"&{. \`"C:\path\with spaces\custom-powershell-profile.ps1\`"}`""
break
}
"posh-noprofile" {
Start-Process -FilePath powershell -Verb runas -ArgumentList "-nologo -noexit"
break
}
"mecm" {
& "C:\Program Files (x86)\Microsoft Endpoint Manager\AdminConsole\bin\Microsoft.ConfigurationManagement.exe"
break
}
}
So the shortcut targets look like:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -file "C:\path\to\script1.ps1" aduc domain\username
Opening powershell and forcefully dot sourcing my custom profile script isn't really necessary, but it avoids the need to touch the powershell profiles of the superuser accounts, making this more portable.
Now that I've thoroughly demonstrated the weakness with which most markdown interpreters render nested/escaped powershell quotes, I bid you a good day. Hopefully this is useful to at least one other person ever again.
Upvotes: 2