A self elevating PowerShell script

A self elevating PowerShell script

Rate This
  • Comments 13


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

Attachment: Elevator.zip
Leave a Comment
  • Please add 1 and 5 and type the answer here:
  • Post
  • 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"

  • Sean Kearney -

    Yes, you will get a prompt to elevate like any other app - so no security loopholes here :-)

    Cheers,

    Ben

  • i found this tool for batch scripts that lets you elevate from a cmd line without PS;

    www.winability.com/elevate

  • 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 path\name 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

  • ...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:\windows\System32 - 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)...

    -/\/\ark

  • 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!

  • Many Thanks , was just searching for this , to run a script in elevated mode .

    You made my day :)

  • thanks ben, going to try elevator for some W8 issues I need to solve.

  • This is pure awesome, thank you very much!

  • Effing awesome, that's what this is.

  • Awesome! Thank you very much.

    Cheers

  • Just found this and it worked a charm!  Thanks Ben :)

  • 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 "$PSHOME\PowerShell.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?  

Page 1 of 1 (13 items)