PowerShell Script that Relaunches as Admin

If were following good security practices we run our Windows system with UAC enabled.  This means that if you forget to launch your PowerShell prompt as Administrator when you run a script that requires administrative privilege then that script will fail.

It would be nice to build a mechanism into our script to “auto-elevate” if UAC is enabled.  The trick to doing this is to run Start-Process –verb runas.  After that you only need to figure out if the current user is an administrator and if UAC is enabled.  And you have to package up the script’s parameters as an array of strings.  All of this can be accomplished fairly easily with this bit of PowerShell script:

function IsAdministrator
{
$Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity)
$Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}


function IsUacEnabled
{
(Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0
}

#
# Main script
#
if (!(IsAdministrator))
{
if (IsUacEnabled)
{
[string[]]$argList = @('-NoProfile', '-NoExit', '-File', $MyInvocation.MyCommand.Path)
$argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList
return
}
else
{
throw "You must be administrator to run this script"
}
}

If you launch this script from a non-elevated context, it will fire up a new PoweShell session that is elevated assuming UAC is enabled.

This entry was posted in PowerShell. Bookmark the permalink.

11 Responses to PowerShell Script that Relaunches as Admin

  1. antize says:

    Add your vote for a built-in way to do this https://connect.microsoft.com/PowerShell/feedback/details/455938/add-requires-statement-for-uac-elevation

    One gotcha about this technique is that it doesn’t work if the script requires parameters.

    • rkeithhill says:

      I had already voted that one up. Good idea and much more “clean” than this extra prologue script.

      • Keith, I figured you’ve seen that one already, was just posting for anyone that reads this.

        More specifically about the argument problem, is doesn’t work for complex parameter types e.g.

        [cmdletbinding()]
        param (
        [parameter(Mandatory=$true)] [System.Security.SecureString] $Password
        )

        One could just do a Read-Host -AsSecureString in the script body as a workaround.

      • rkeithhill says:

        Yeah, it would be much more preferable to have a “#requires -InvokeAsAdministrator” or something like that. Similar to an exe’s manifest declaration. And let PowerShell handle all the corner case complexities like this. 🙂

  2. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, Exchange, SystemCenter and more – #23 - TechCenter - Blog - TechCenter – Dell Community

  3. Pingback: Dell Community

  4. Pingback: Microsoft Most Valuable Professional (MVP) – Best Posts of the Week around Windows Server, Exchange, SystemCenter and more – #23 | ServerGround.net

  5. I wish MS would add a Run as Administrator option to the ps1 right-click menu like there is with cmd files. Even if it requires shift+right-click. I’ve made batch scripts for the sole purpose of bootstrapping PowerShell scripts to get Run as Administrator. I’ve seen the PowerShell power toys that add this (among other things) but I think it should just be installed with PowerShell.

    http://blogs.technet.com/b/elevationpowertoys/archive/2010/01/01/elevation-powertoys-for-the-windows-powershell-integrated-scripting-environment-ise.aspx

    I see why this is tricky for MS, since the PowerShell process can’t self elevate if it encounters a script with an elevation directive. I guess a compromise could be to just refuse to execute scripts marked with said directive (with a helpful error message), and provide some sort of a sudo type cmdlet… Though with sudo, your current environment variables and such as still available to the elevated command…

  6. Sagun says:

    Keith- I ran the below which works, but it is odd that it still generates an error message although it works, any suggestions? I’m just running this from the Powershell GUI Editor 3.5 under a non-admin user account. thanks,

    start-process powershell -verb runas -argument
    Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq “On-Screen Takeoff”} | foreach-object -process {$_.Uninstall()}
    Start-Sleep -Seconds 25
    $arguments=”/quiet”
    Start-Process “\\davisconstruction.com\ROOT\Installs\OnCenter\OST\Testverion3906\ost3906.msi” $arguments

    **works but get the error below

    Start-Process : Missing an argument for parameter ‘ArgumentList’. Specify a parameter of type ‘System.String[]’ and try again.
    At C:\Users\TEMP\AppData\Local\Tempf078c98-bbed-48be-966d-58e2f5f40e8c.ps1:1 char:47
    + start-process powershell -verb runas -argument <<<<
    + CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Microsoft.PowerShell.Commands.StartProcessCommand

    __GENUS : 2
    __CLASS : __PARAMETERS
    __SUPERCLASS :
    __DYNASTY : __PARAMETERS
    __RELPATH :
    __PROPERTY_COUNT : 1
    __DERIVATION : {}
    __SERVER :
    __NAMESPACE :
    __PATH :
    ReturnValue : 0

    ——————–

    • rkeithhill says:

      What you have should work. Are you using some sort of proxy function for Start-Process? Run “Get-Command Start-Process” and see what the ModuleName is.

      • Sagun says:

        I did some more research and modified it a bit. I’m not using any proxy function.
        The 1st script actually calls script called part2, but it errors out and I’m not sure what needs to happen.

        $username = ‘.\admin’
        $password = ‘Window’s”
        $cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
        Invoke-Command -FilePath \\davisconstruction.com\ROOT\Installs\OnCenter\OST\Testverion3906\part2.ps1 -Credential $cred -ComputerName $env:computername

        PART 2
        Get-WmiObject -Class Win32_Product | Where-Object {$_.Name -eq “On-Screen Takeoff”} | foreach-object -process {$_.Uninstall()}
        Start-Sleep -Seconds 25
        $arguments=”/quiet”
        Start-Process “\\davisconstruction.com\ROOT\Installs\OnCenter\OST\Testverion3906\ost3906.msi” $arguments

        ERROR:
        [j3q0yw1-e6530] Connecting to remote server failed with the following error message : WinRM cannot process the request. The following error occured while using Kerberos authentication
        : There are currently no logon servers available to service the logon request.
        Possible causes are:
        -The user name or password specified are invalid.
        -Kerberos is used when no authentication method and no user name are specified.
        -Kerberos accepts domain user names, but not local user names.
        -The Service Principal Name (SPN) for the remote computer name and port does not exist.
        -The client and remote computers are in different domains and there is no trust between the two domains.
        After checking for the above issues, try the following:
        -Check the Event Viewer for events related to authentication.
        -Change the authentication method; add the destination computer to the WinRM TrustedHosts configuration setting or use HTTPS transport.
        Note that computers in the TrustedHosts list might not be authenticated.
        -For more information about WinRM configuration, run the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.
        + CategoryInfo : OpenError: (:) [], PSRemotingTransportException
        + FullyQualifiedErrorId : PSSessionStateBroken

Leave a comment