powershellelevation

Using Start-Process to launch PowerShell as an admin and run a command. How do I pass arguments to the command?


I have the following script to check if it's running as admin. If not, it launches a new window with the admin token and runs the script again in the admin window. The part I am unable to figure out, is how to pass the parameters originally sent to the script over to the newly launched script

[CmdletBinding()]
param (
    $Test1
)
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID)
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator

if (-not $myWindowsPrincipal.IsInRole($adminRole)) {
    switch ($PSVersionTable.PSVersion.Major) {
        5 {
            Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"{
                Set-Location -Path $pwd
                & '$PSCommandPath'
            }`""
        }
        Default {
            Start-Process PWSH -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"{
                Set-Location -Path $pwd
                & '$PSCommandPath'
            }`""
        }
    }
    exit
}
Write-Host $Test1
Write-Host "elevated"
(Get-Item -Path .\).FullName
Start-Sleep -Seconds 10

Let's call the above script test.ps1. In a PowerShell window, I would call it as follows:

PS C:\>& .\test.ps1 -Test1 'SampleScript'

Since I'm passing a string at the time of calling the script, I would like this same string to be passed to the Administrative PowerShell that gets opened. So far, any attempts to pass the parameters have failed.

If possible, it needs to be versatile in that it passes all parameters that a script may have. The example above only has one parameter, but ideally even if the script has two or more all will get passed over to the admin window.


Solution

  • This should work for passing the bound parameters to the elevated script, might not be 100% robust but using an example of parameters (added a second $Test2 param for the example):

    PS ..\pwsh> .\test.ps1 -Test1 123 -Test2 'a string with spaces'
    

    Will output:

    Elevated
    D:\User\Documents\pwsh
    
    Key   Value
    ---   -----
    Test1 123
    Test2 a string with spaces
    

    This is the code for constructing the arguments by enumerating $PSBoundParameters. Note that -NoExit was added for debugging purposes, should be removed later on.

    using namespace System.Management.Automation.Language
    
    [CmdletBinding()]
    param (
        $Test1,
        $Test2
    )
    
    $isAdmin = [System.Security.Principal.WindowsPrincipal]::new(
        [System.Security.Principal.WindowsIdentity]::GetCurrent()).
            IsInRole('Administrators')
    
    if (-not $isAdmin) {
        $pwsh = (Get-Process -Id $PID).Path
    
        # NOTE:
        #   the use of `[CodeGeneration]::EscapeSingleQuotedStringContent(...)` is required to properly escape
        #   possible single quotes `'` in arguments or the path itself
        $boundParams = $PSBoundParameters.GetEnumerator() |
            ForEach-Object { "-$($_.Key)", "'$([CodeGeneration]::EscapeSingleQuotedStringContent($_.Value))'" }
    
        $command = "
            Set-Location -Path '$([CodeGeneration]::EscapeSingleQuotedStringContent($pwd))'
            & '$([CodeGeneration]::EscapeSingleQuotedStringContent($PSCommandPath))' $boundParams
        "
    
        $arg = '-NoProfile -ExecutionPolicy Bypass -NoExit -Command "{0}"' -f $command
        Start-Process $pwsh -Verb RunAs $arg
        return
    }
    
    Write-Host 'Elevated'
    (Get-Item -Path .\).FullName
    $PSBoundParameters | Out-Host