Search code examples
windowspowershellcmdstart-processprocess-elevation

Start a non-elevated instance of "powershell.exe -NoProfile" from an elevated host/console


I'm trying to start a new, non-elevated instance of powershell.exe -NoProfile from an elevated PowerShell/ISE/pwsh/VSCode instance but haven't figured out a way to pass the -NoProfile switch in a way that results in a console that uses the custom colors/layout that it normally would when double-clicking powershell.exe from an Explorer window (e.g. a blue PowerShell console window).



Two popular approaches to lowering elevation from an elevated host/console:

  1. runas.exe /trustlevel:0x20000 powershell.exe
    • ISSUE: The console window doesn't use the custom layout/colors it typically displays with and will usually present as a black console window.
    • There is a SO question on the differences between the blue and black consoles here.
    • Lee Holmes also has a short blog about this.

  2. Taking advantage of explorer.exe's (default) lower trust level and doing something like this:
    Start-Process $env:SystemRoot\explorer.exe $PSHome\powershell.exe
    • ISSUE: If the user has disabled UAC, then explorer.exe will be running with elevation anyways, so this method isn't as reliable as using runas.exe /trustlevel:0x20000 powershell.exe




I've tried:

  • runas.exe /trustlevel:0x20000 "powershell.exe -NoProfile"
    • Works, but opens a black console window.
  • runas.exe /trustlevel:0x20000 "explorer.exe powershell.exe -NoProfile"
    • opens an Explorer window.
  • Various combinations and overloads using [System.Diagnostics.Process]::new() and [System.Diagnostics.ProcessStartInfo]::new() without success.
  • Using various forms of start /b "" xyz without success.



Editing the registry to modify the default console layout/colors isn't impossible, but it's not the most practical solution. Is there another approach that would work better?


Solution

    • It is the startup window title of a console (conhost.exe) window that determines what settings to apply, taken from the registry key [HKEY_CURRENT_USER\Console\<transformed-exe-path>], where <transformed-exe-path> is the full path of the executable with \ characters replaced with _, as detailed in this answer.

    • With runas.exe /trustlevel:0x20000 you'll get a custom windows title, which invariably appends the following to the startup command line: (running as <computer>\<user> with restricted privileges) - and since such a title is unlikely to have a specific subkey in [HKEY_CURRENT_USER\Console], the default console settings apply.

    The simplest way to work around this is to call via cmd /c start in order to start a new window - either relying on the new window's target executable's path to select the right settings or by specifying an explicit startup title:

    runas /trustlevel:0x20000 'cmd /c start powershell.exe -noprofile'
    

    Note:

    • This will create two console windows: a transient one for cmd /c, and the target one created by start - hopefully, the transient one will be too short-lived to have a visual impact.

    • In Windows 11, up to at least version 22H2, there is currently a bug that prevents use of /trustlevel without also providing a /machine argument with the current machine architecture:

      runas "/machine:$($env:PROCESSOR_ARCHITECTURE.ToLower())" /trustlevel:0x20000 'cmd /c start powershell.exe -noprofile'
      
    • If you need to specify an explicit window title for the start-launched process, things get tricky, because that title must be "..." enclosed, which - due to a long-standing PowerShell bug with respect to passing arguments with embedded " chars. to external programs (see this answer) - must manually be escaped as \"; e.g., to set custom title Custom Title:

      # A solution that works in v7.3+ too
      & {
        $PSNativeCommandArgumentPassing = 'Legacy'
        # Note: /machine argument omitted for brevity.
        runas /trustlevel:0x20000 'cmd /c start \"Custom Title\" powershell.exe -noprofile'
      }