Elevate / Sudo on PowerShell - powershell

I have found a few PowerShell elevate / sudo functions, but none of them seem to work well (in a "as intuitively and seamlessly as on every Unix and Linux distribution" way). They are mostly redundant as they don't work well. If someone has a seamlessly working elevate / sudo on PowerShell they'll know it.
The problems with the functions that I've seen are:
• They only work with external scripts by calling another instance of powershell.exe. i.e. If you want to do something as simple as sudo gci or sudo Get-ChildItem that will generate an error as the methods don't seem to like calling aliases or Cmdlet (for some reason!).
• You cannot seamlessly elevate the existing console session up to Administrator, and this seems to require that an elevate / sudo function opens a completely new console (seems cumbersome to have to open a new console for nothing!)?
Does anyone have a reliable elevate / sudo that they use? I don't expect it to be perfect, if there are good technical reasons why things like the above do not work (maybe to do with limitations of the PowerShell host itself not being capable enough) then that's fine, but it would be good to know how far we can get with a functional elevate / sudo within PowerShell. It's often a shame that, although PowerShell is massively more advanced than bash (and it's object manipulation capabilities blow away Python and Perl imo also), sometimes it seems like some of the most simple capabilities in Unix-land, like sudo, blow away what is possible in PowerShell - I'd love to see those gaps filled so that PowerShell can be shown to be every bit as capable as Unix (and more so!!) for a change.

Nothing native in the box of course, so, an apples/oranges comparison when talking sudo stuff with Windows.
Security boundaries/functionalities are just different, as well all know, and the sudo equivalent in Windows (and thus PowerShell) is RunAs and that will pop Windows UAC, no getting around that, without turning UAC off (don't do this) or setting up an AppCompat shim.
So, when you say functions, are you saying you have already tired these:
Find-Module -Name '*sudo*' |
Select Name, Version, Type, Description
# Results
<#
Name Version Type Description
---- ------- ---- -----------
Sudo 2.1.0 Module Use functionality similar to sudo in PowerShell. GitHub: https://github.com/pldmgg/Sudo
PSSudo 1.4.0 Module Function for executing programs with adminstrative privileges
#>
This type of question comes up a lot here and has been answered several times. So, are you saying, you tried the below?
How to sudo on powershell on Windows
Start-Process -Verb RunAs powershell.exe -Args "-executionpolicy bypass -command Set-Location \`"$PWD\`"; .\install.ps1"
Sudo !! equivalent in PowerShell
runas /user:domain\administrator $^
Is there any 'sudo' command for Windows?
doskey sudo= runas /user:Administrator "cmd /k cd \"%cd%\" & $*"
runas /noprofile /user:Administrator cmd
See also:
Support sudo #3232
5 Windows Alternatives to the Linux sudo Command

gsudo is a sudo for Windows that behaves like Unix sudo (elevates a command or your cmd/ps shell in your current console windows). It works in Powershell, but with limitations: The elevated memory space can't share objects with the non-elevated one, so variables can't be shared, and some kind of marshalling of objects must be done. Currently gsudo does the most naive, but at least honest, marshalling: just strings can be passed to and from. You can pass a string literal with the command that needs to be elevated to gsudo. Then gsudo returns a string that can be captured, not powershell objects.
# Commands without () or quotes
PS C:\> gsudo Remove-Item ProtectedFile.txt
or
PS C:\> gsudo 'Remove-Item ProtectedFile.txt'
# On strings enclosed in single quotation marks ('), escape " with \"
$hash = gsudo '(Get-FileHash \"C:\My Secret.txt\").Hash'
# For variable substitutions, use double-quoted strings with single-quotation marks inside
$hash = gsudo "(Get-FileHash '$file' -Algorithm $algorithm).Hash"
# or escape " with \""
$hash = gsudo "(Get-FileHash \""$file\"" -Algorithm $algorithm).Hash"
# Test gsudo success (optional):
if ($LastExitCode -eq 999 ) {
'gsudo failed to elevate!'
} elseif ($LastExitCode) {
'Command failed!'
} else { 'Success!' }
Or, you can just call gsudo to elevate your current shell, in Powershell:
PS C:\> gsudo
(Accept UAC popup)
PS (ADMIN) C:\> Remove-Item ProtectedFile.txt
PS (ADMIN) C:\> exit
PS C:\>

Related

Executing scripts is the same as dot sourcing?

I saw some surprising behavior in powershell recently.
Consider the script c:\scripts\tst1.ps:
Set-location c:\
Now from powershell:
C:\scripts PS> .\tst1.ps1
C:\ PS>
Why does the calling shell change to “C:\” according to “set-location”? shouldnt the “set-location” run from a subshell according to normal scripting rules for other shells like bash?
I thought that you had to “dot source” the script to get this type of behavior??? That is: “. .\tst1.ps1”? What is happening here.
is my powershell misconfigured on my host computer or is this the actual behavior of powershell these days?
Is there a way to reconfigure powershell to get the old behavior that I expect that is similar to bash shell where the script runs in a sub shell and exits and the current directory is uneffected?
You can run your script with the new instance of powershell:
C:\scripts PS> powershell .\tst1.ps1

Command Prompt executes this correctly but Powershell doesn't?

Having some weirdness where Powershell is not executing this command correctly. It can't understand the INSTALLFOLDER or TARGETDIR and just throws up the Windows Installer help box.
Whereas command prompt processes this just fine??? It's very weird. PS version is 5.1.18362 I believe.
the command is
msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn
My msi is a Wix installer msi and has the property INSTALLFOLDER
This is not how you run this command in PowerShell. There are several ways to run external command in PowerShell and it a well-documented use case. Links to follow
• Using PowerShell and external commands and their parameters or
switches.
Running external commands, always require special consideration.
• 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)
• Execution of external commands in PowerShell done right
https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right
https://mnaoumov.wordpress.com/2015/03/31/execution-of-external-commands-native-applications-in-powershell-done-right-part-2
https://mnaoumov.wordpress.com/2015/04/05/execution-of-external-commands-native-applications-in-powershell-done-right-part-3
http://edgylogic.com/blog/powershell-and-external-commands-done-right
• Quoating specifics
https://trevorsullivan.net/2016/07/20/powershell-quoting
PowerShell has a list of characters that mean specific things, vs what cmd.exe implementation means, and if you need those, they must be properly terminated. See the below
About Special Characters
PowerShell - Special Characters And Tokens
So, to make your command work in the PowerShell consolehost, ISE, VSCode, do something like this...
# Using teh Call operator
& 'msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn'
or
... this...
# Using Start-Process
$ConsoelCommand = 'msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn'
Start-Process powershell -ArgumentList "-NoExit","-Command &{ $ConsoleCommand }" -Wait
Make sure you check for existence of that actual folder, but it's likely the problem is the space
instead of this:
msiexec.exe /i "C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi" INSTALLFOLDER="C:\Installation Test" /qn
try this:
invoke-command -scriptblock {cmd /c "msiexec.exe /i `"C:\Users\sadas\aasasdd\sda\asdasd\19526-Debug-x64.msi`" INSTALLFOLDER=`"C:\Installation` Test`" /qn"}
Notice that all the double-quotes are escaped AND a the space in "C:\Installation Test" is escaped. Your problem will boil down to this space and how you handle it. Also try changing double to single quotes. If you decide to use variables in your command, definitely add the strings (""+"") to create the command so that it will expand the variables correctly.

Why doesn't this msiexec.exe command work in powershell?

I am trying to execute the following command through powershell, in a script invoked by an Advanced Installer generated installation program. The problem is that when the script executes, it chokes on a call to MSIEXEC.exe. More specifically, it puts up a windows dialog of the msiexec help screen.
Ok so maybe it doesn't like the way advanced installer is executing it. So I take the actual line that is causing problems:
msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
And when I execute this directly in powershell, I still get the same stupid help screen. I have tried every conceivable variation of this command line:
/a and /passive instead of /i and /q
with double quotes
with single quotes
the msi unquoted
in an admin elevated shell
in a normal privilege shell
the msi located on the desktop instead of the temp folder
using /x to uninstall in case it was already installed
In all cases, I get the damnable "help" dialog. The only thing that appears to make a difference is if I leave off the INSTALLLOCATION and ADDLOCAL options. (These are apparently used as per "Unattended Installation part 2" found here: https://docs.mongodb.com/tutorials/install-mongodb-on-windows/). In that case it just exits quietly without installing anything.
I'm honestly at my wits' end having been beating my head against the wall on this all afternoon.
By the way, the reason I'm installing mongo in such an absurd way is I need a method of having a single-install system for my company's product. It depends on Mongo, and we have to have it run as a server and use authentication, so I have to have scripts to create the admin and database user and put it into authenticated mode. Since I needed to know where mongo was installed (to execute mongod.exe and mongo.exe) I need to query the user first for the location, then pass on the install location to the mongo installer. If I'm completely off the rails here please let me know that there's a better way.
Thanks
EDITED: I forgot to mention I wrote my complete powershell script and tested it before trying to execute it through advanced installer. The script worked until I tried to run it through the installer. Strange that I still can't execute the command though manually now.
It seems that in order to pass paths with embedded spaces to msiexec, you must use explicit embedded "..." quoting around them.
In your case, this means that instead of passing
INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\', you must pass INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"'[1]
Note the embedded "..." and the extra \ at the end of the path to ensure that \" alone isn't mistaken for an escaped " by msiexec (though it may work without the extra \ too).
To put it all together:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
INSTALLLOCATION='"C:\Program Files\MongoDB\Server\3.4\\"' ADDLOCAL='all'
Caveat:
This embedded-quoting technique relies on longstanding, but broken PowerShell behavior - see this answer; should it ever get fixed, the technique will stop working; by contrast, the
--% approach shown below will continue to work.
A workaround-free, future-proof method is to use the PSv3+ ie helper function from the Native module (in PSv5+, install with Install-Module Native from the PowerShell Gallery), which internally compensates for all broken behavior and allows passing arguments as expected; that is, simply prepending ie to your original command would be enough:
# No workarounds needed with the 'ie' function from the 'Native' module.
ie msiexec.exe /q /i 'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' ADDLOCAL='all'
The alternative is to stick with the original quoting and use --%, the stop-parsing symbol, but note that this means that you cannot use PowerShell variables in all subsequent arguments:
msiexec.exe /q /i `
'C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi' `
--% INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL='all'
Note that msiexec, despite having a CLI (command-line interface), is a GUI-subsystem application, so it runs asynchronously by default; if you want to run it synchronously, use
Start-Process -Wait:
$msiArgs = '/q /i "C:\Users\ADMINI~1\AppData\Local\Temp\mongo-server-3.4-latest.msi" INSTALLLOCATION="C:\Program Files\MongoDB\Server\3.4\\" ADDLOCAL=all'
$ps = Start-Process -PassThru -Wait msiexec -ArgumentList $msiArgs
# $ps.ExitCode contains msiexec's exit code.
Note that the argument-list string, $msiArgs, is used as-is by Start-Process as part of the command line used to invoke the target program (msiexec), which means:
only (embedded) double-quoting must be used.
use "..." with embedded " escaped as `" to embed PowerShell variables and expressions in the string.
conversely, however, no workaround for partially quoted arguments is needed.
Even though Start-Process technically supports passing the arguments individually, as an array, this is best avoided due to a longstanding bug - see GitHub issue #5576.
[1] The reason that INSTALLLOCATION='C:\Program Files\MongoDB\Server\3.4\' doesn't work is that PowerShell transforms the argument by "..."-quoting it as a whole, which msiexec doesn't recognize; specifically, what is passed to msiexec in this case is:
"INSTALLLOCATION=C:\Program Files\MongoDB\Server\3.4\"

psexec trying to exec script error

I am learning how to interact with PowerShell and PsTools, and I have a problem with psexec.
I got a ps1 script named test.ps1 and inside it I have Get-Service which gives me the all services in my computer. Now I am going into PowerShell and go to c:\pstools. Then I type
psexec.exe C:\test\test.ps1
and it fails and returns me this error:
%1 is not a valid Win32 application
What could be the problem?
PsExec launches an executable. You need to specify the executable for PowerShell and associated arguments:
psexec.exe -accepteula -nobanner -s -h -d powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "C:\test\test.ps1"
The immediate answer to your question is:
psexec requires a (binary) executable as its first argument and cannot execute scripts directly.
Therefore, you must pass the Windows PowerShell executable name to psexec and in turn pass the desired script to the latter as an argument, via the -File parameter:
psexec powershell -File C:\test\test.ps1
That said, this particular use of psexec is pointless, as it would execute the script locally, as the current user, in which case use of psexec is a needless complication:
If you already know that, and the psexec command at hand is just a simplified example, never mind.
Otherwise, read on below.
The ps in psexec and PsTools has nothing to with PowerShell; PsTools is a collection of CLIs for managing Windows machines remotely, including processes, a common abbreviation of which is "ps", inspired by the standard ps Unix utility, which in turn inspired the initial tool in the collection, pslist; the primary purpose of psexec is to invoke arbitrary command lines on remote machines[1]
.
To invoke a PowerShell script locally:
From inside PowerShell itself, simply invoke the file path directly:
PS> c:\test\test.ps1
PS> & "c:\test\test.ps1", if the file path is / must be quoted or is provided via a variable or expression.
From outside of PowerShell, such as cmd.exe ("Command Prompt") or bash, you must invoke the PowerShell executable explicitly and pass it the script file path via the -File parameter:
Windows PowerShell: C:\> powershell -file c:\test\test.ps1
PowerShell Core: C:\> pwsh -file c:\test\test.ps1
In other words: the PowerShell's executable name is
powershell.exe for Windows PowerShell,
vs. pwsh for the cross-platform PowerShell Core edition (with extension .exe on Windows).
If you do need remote execution:
Pass \\-prefixed machine name(s) or IP address(es) to psexec; e.g., the following command executes the hostname utility on machine somemachine:
psexec \\somemachine hostname
There is no benefit to using psexec without targeting a different machine.[1]
However, psexec is normally not needed, because PowerShell has built-in support for remoting (i.e., the ability to execute commands on other machines; remoting requires setup, however - run Get-Help about_Remote_FAQ for more information); e.g., the equivalent of the above command is:
Invoke-Command -ComputerName somemachine { hostname }
[1] As TheIncorrigible1 points out, psexec can also be used for local execution as the system account (NT AUTHORITY\SYSTEM, the account that represents the computer as a whole) with the -s option.
Additionally, you can also run locally as another user, using the -u parameter - which, however, the standard runas utility can do as well (the latter doesn't offer passing the target user's password as a parameter for security reasons, but does offer to securely save a password for later reuse).
Run psexec -h for help.

Need to unblock remote ps script run using psexec

As part of our automatic build and deploy using TFS, I need to execute a powershell script on a target server. The following is the (simplified for clarity) command run on the build server by the TFS Build Agent PreBuild step, in the (pre-build.ps1) script...
C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1 -name AppPool-DEV -usr User -pw pass
If I run the powershell part of the command on the WSRMO632WEB box in a command window, I get the warning...
Security warning
Run only scripts that you trust. While scripts from the internet can be useful,
this script can potentially harm your computer. If you trust this script,
use the Unblock-File cmdlet to allow the script to run without this warning message.
Do you want to run \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1?
[D] Do not run [R] Run once [S] Suspend [?] Help (default is "D"):
If I choose R, the script runs and performs correctly.
My problem is that I cannot get the syntax correct to incorporate the Unblock-File cmdlet.
I'm currently thinking that I'm going to have to use multiple psexec commands, one to copy the file from the build server, one to unblock it and a third to finally run it.
Surely it should be easier than that, but I can't find a suitable example and can't get the syntax right.
Any suggestions, please?
You can use Powershell's -command to first do an Unblock-File, then run it as a script.
C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe "-command \"$file='\\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1'; $file; Unblock-File $file; & $file\"" -name AppPool-DEV -usr User -pw pass
Quotes are necessary so that the full command string will be passed to Powershell. Add backslashes to escape themselves as necessary.
UPDATE: You can also try feeding the required command into standard input.
echo r | C:\Builds\<snip>\psexec.exe -accepteula -s -i \\WSRMO632WEB powershell.exe \\TFS-BAGENT-POC\<snip>\PreBuild-AppPool.ps1 -name AppPool-DEV -usr User -pw pass
This way Powershell will run, get the "R" for "Run once" and run the script, without any changes to the script or calling command.