A self elevating PowerShell script
Okay, this is not actually a virtualization related post – but is a purely about PowerShell. None the less – it is something that I use quite often when scripting Hyper-V – so I thought I would post it here.
The long and the short of it is that, as a general rule, I always leave UAC enabled on Windows and never run as Administrator by default. But I do have scripts that need to run as administrator from time to time.
Rather than launching PowerShell “as Administrator” (which would result in me running other scripts as administrator – because it would be convenient) I have put together the following chunk of script:
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "DarkBlue"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
exit
}
# Run your code that needs to be elevated here
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
This means that when you run the script in question – a new window will be opened “as Administrator” (with an appropriate prompt).
Cheers,
Ben
Anonymous
September 23, 2010
Ben that is super cool! Have to try that tonight! I am presuming if UAC is enabled it still safely prompts for "Yep/Nope/Allow Giant Gerbils Free" Yes, majorly cool :) I like Sean "The Energized Tech"Anonymous
September 24, 2010
Sean Kearney - Yes, you will get a prompt to elevate like any other app - so no security loopholes here :-) Cheers, BenAnonymous
September 24, 2010
The comment has been removedAnonymous
February 10, 2011
Great piece of code! One question though... I tried to adapt this for use as a function and found that it won't work because, in that case, $myInvocation.MyCommand.Definition contains the commands from the function instead of the pathname of the script itself... Is there a different/better way to derive the name of the script in which the code is running so this could be adapted as a function and included in a libary? Example: Function ELEVATE {
all the stuff from above...
}
Main script body
ELEVATE Start-Service w32time ... Thanks, -//ark
Anonymous
February 10, 2011
...continued... I'm also having trouble using this method to elevate a script that calls other PowerShell scripts because the working driectory is not the same in the newly invoked (elevated) shell. It changes to C:windowsSystem32 - I tried adding $newProcess.WorkingDirectory = get-location; just before the start command but this did not seem to help (though I did not get any errors and $newProcess.WorkingDirectory appears to be set to C:Scripts as I would expect)... -//arkAnonymous
January 31, 2012
Hey Ben, I found your blog looking for the exact functionality that you have demonstrated. Thanks for the post. I did find two issues though with your code, and I would like to share the updated lines with you here. Issue #1 running inside a function has the scope of the function. I found a post on SO that gives a solution and explains a little about why it is this way. $script:MyInvocation.MyCommand.Path stackoverflow.com/.../how-can-i-find-the-source-path-of-an-executing-script Issue #2 is running a script with a space in the filename. Sure it is bad practice, but I did it and came across errors with it. You just have to use the & '.script name.ps1' format. $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'" My updated code block looks like this: $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; # Specify the current script path and name as a parameter #$newProcess.Arguments = "& '" + $myInvocation.MyCommand.Definition + "'"; $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'" # Indicate that the process should be elevated $newProcess.Verb = "runas"; # Start the new process [System.Diagnostics.Process]::Start($newProcess); I found several other suggestions on how to have the script elevate another script or another process, but yours put it all together in a nice format. Thanks a bunch!- Anonymous
April 20, 2016
I just noticed your comment! Thank you so much as this fixed it :)
- Anonymous
Anonymous
September 24, 2012
Many Thanks , was just searching for this , to run a script in elevated mode . You made my day :)Anonymous
September 24, 2012
thanks ben, going to try elevator for some W8 issues I need to solve.Anonymous
January 08, 2013
This is pure awesome, thank you very much!Anonymous
January 22, 2013
The comment has been removedAnonymous
March 10, 2013
Awesome! Thank you very much. CheersAnonymous
January 19, 2014
Just found this and it worked a charm! Thanks Ben :)Anonymous
February 10, 2014
Some of my scripts are designed for end users that do not have PSH experience. The users have local admin access and only need to right-click and run. These scripts save me time because the user can run them without much effort. In the name of security, these scripts stopped running elevated. Rather than training users or running the scripts myself, I updated the scripts to restart as elevated. So, we are safer now? No hacker will ever figure out how to do this? BTW, elevated drive mappings to shares are separate from un-elevated drive mappings. My scripts use the script directory to locate other scripts and a log folder. After first updating the code to restart elevated, my scripts still failed because the drive was gone. To get around it, I switch to the UNC path before calling the script again. Get-PSDrive displays a "Root" value by default. However, it's the "DisplayRoot" value not displayed by default that is of interest. If it has a value, it's has the UNC path for the mapped drive. $private:scriptFullname = $script:MyInvocation.MyCommand.Definition if ($scriptFullname.Contains([io.path]::VolumeSeparatorChar)) { # check for a drive letter $private:psdrive = Get-PSDrive -Name $scriptFullname.Substring(0, 1) -PSProvider 'FileSystem' if ($psdrive.DisplayRoot) { # check if it's a mapped network drive $scriptFullname = $scriptFullname.Replace($psdrive.Name + [io.path]::VolumeSeparatorChar, $psdrive.DisplayRoot) } } Not sure about & and spaces. I just use $newProcess = new-object System.Diagnostics.ProcessStartInfo "$PSHOMEPowerShell.exe"; $newProcess.Arguments = "-File"$scriptFullname
"" $newProcess.Verb = 'runas'; $process = [System.Diagnostics.Process]::Start($newProcess); I've added other arguments when needed. For example, a parameterized script with a "Show" parameter. (I was experimenting with a Show parameter with a default of 0. The value is 1 when it is restarted.) param ([Int]$Show = 0) ... $newProcess.Arguments = "-File"$scriptFullname
" -Show 1" It's kind of cool what one can do in PowerShell; however, to be honest, I was thinking of going back to reliable cmd files. Then I decided I'm not ready to retire or shoot myself. All this so that I can just do what I did before?Anonymous
May 30, 2014
THANK YOU! 5 Stars and a bookmark! I have to keep this handy, but I've already run it and it solved my issue. No more manual run as admin, simple double-click days are here.Anonymous
June 18, 2014
Sorry, I had to come back to thank James as well. I didn't notice his comment at first, but yes, the code above doesn't like to run with spaces in path or command. His fix did the trick!Anonymous
July 14, 2014
Is there a way to avoid the yes/no/allow uac prompt as well, I want to use this in automationAnonymous
August 21, 2014
Ben, how would you edit this to launch an elevated Exchange Management Shell?Anonymous
August 26, 2014
In order to get an elevated Exchange Management Shell, I made this the first command to be executed after running Ben's script: 'Get-PSSnapin -registered | Add-PSSnapin'Anonymous
September 12, 2014
Awesome script! Works great and is exactly what I am looking for. Kudos!Anonymous
October 07, 2014
The comment has been removedAnonymous
October 07, 2014
Ben - Feel free to link to this post in your blog. Cheers, BenAnonymous
October 28, 2014
This post was a big help in what I'm doing. Thanks for posting it.Anonymous
November 20, 2014
The comment has been removedAnonymous
November 24, 2014
The comment has been removedAnonymous
November 24, 2014
The comment has been removedAnonymous
November 25, 2014
The comment has been removedAnonymous
December 17, 2014
prompts for UAC, that is what i am trying to avoidAnonymous
February 23, 2015
Doesn't work, it just opens another window and prompts you to "press Any Key...", when you do that the window disappears.Anonymous
June 06, 2015
You can't avoid UAC prompt unless you disable UAC first. It's build that way by design to prevent scripts and other applications from fudging with the system unless the user specifically permits it. You can disable UAC (I don't recommend this) in powershell with this: New-ItemProperty -Path HKLM:SoftwareMicrosoftWindowsCurrentVersionpoliciessystem -Name EnableLUA -PropertyType DWord -Value 0 -Force You have to click yes from UAC prompt and then reboot after you do it though.Anonymous
July 25, 2015
This works great my only problem is while executing my script it leaves that elevated prompt up and doesn't minimize. Any way to get that to minimize?Anonymous
July 29, 2015
This does not seem to work on Windows Server 2012. It closes out of all windows once you click yes and does not actually run the script. Any ideas?Anonymous
August 07, 2015
Did not work on Windows 10. Don't know about other operating systems.Anonymous
September 17, 2015
Hey Ben, when you run this script in version 5 of PowerShell (Windows 10), you get: "The property 'Arguments' cannot be found on this object."- Anonymous
February 03, 2016
Did you ever find a way around this problem? Also getting some sort of error on Windows 10 with the provided code.
- Anonymous
Anonymous
December 16, 2015
The script didn't work for me until I removed the space that I had in the file name. Then it worked just fine. (I was using Windows 10, November update)Anonymous
January 28, 2016
The comment has been removedAnonymous
April 20, 2016
I must be missing something...If I run this, it prompts for ADMIN like it should, flashes a screen and goes away. Never runs the commands I have at the bottom of the script. Tried adding PAUSE in there and nothing. Same result.Anonymous
April 29, 2016
The test is wrong. On my work machine, I'm a local admin, so IsInRole returns $true, but if I try to run a script that has #Requires -RunAsAdministrator it will error if I didn't go through the UAC elevation when I launched PowerShell. Which means I will NEVER go to the else, always to the then, regardless if I've elevated or not.So the question is "How do I know if I need to UAC elevate or not?" And calling IsInRole does not answer the question, all it does is tell us if we can elevate with a single click instead of entering new credentials.What I finally did was create a script in my profile dir that has that and is basically a no-op, then I .source a function that inside of a try block calls that script then returns $true, and in the catch block returns $false (and since I call that function from my prompt (to color my username red if I'm elevated and green if I'm not) I only do the test the first time, then I cache it in a ${script:AmIElevated} (the function--included below--is defined in miscScripts.ps1 which I .source in profile. If there's a way to test this directly without the try-catch and separate script file, please share.:function Test-IsAdministrator { [OutputType([Boolean])] [CmdletBinding(SupportsShouldProcess = $false)]PARAM() BEGIN { if (-not (Test-Path -Path 'variable:\script:AmIElevated')) { try { . "$profileDir\Test-IsAdministrator.ps1" ${script:AmIElevated} = $true } catch { ${script:AmIElevated} = $false } } ${script:AmIElevated} }}Anonymous
September 13, 2016
The comment has been removed- Anonymous
October 31, 2016
The comment has been removed
- Anonymous
Anonymous
November 17, 2016
Great!In my environment, it was simpler to launch a cmd.exe, in order to deal with paths and additional arguments$newProcess = new-object System.Diagnostics.ProcessStartInfo "cmd.exe"$newProcess.Arguments = '/c ' + [System.Environment]::GetCommandLineArgs()- Anonymous
November 17, 2016
Ah! And to deal with the location, I used$newProcess.WorkingDirectory = [environment]::CurrentDirectory
- Anonymous
Anonymous
November 20, 2016
Hi You can also create a "Launcher.PS1" script (as below) which will fire up your Powershell script with elevated privileges Start-Process powershell -ArgumentList '-noprofile -file MyScript.ps1' -verb RunAsref. link : http://ss64.com/ps/syntax-elevate.html