runposh

Copyright © 2020 by Bill Stewart

Freeware License

This program may be used freely in any environment without payment to the author, with the following conditions:

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Introduction

This program is based on an earlier utility I wrote called ps1exec. I wrote it to address the following challenges of running PowerShell scripts from outside the PowerShell command line:

runposh addresses these limitations by doing the following:

runposh requires at least Windows PowerShell version 2. (Windows PowerShell version 2 is included with Windows 7/Server 2008 R2. Later OS versions include newer Windows PowerShell versions.)

runposh is provided in 64-bit (x64) and 32-bit (x86) versions.

Usage

The program's command-line syntax is as follows:

runposh [options] ["scriptfile" [-- parameters [...]]]

General options:

Option Description
--core[="path"] Runs PowerShell Core instead of Windows PowerShell. Specify a path and filename to pwsh.exe (optional) to run PowerShell Core from a specific path. If you don't specify a filename, runposh tries to find PowerShell Core by reading the registry. The quote marks (") are required if the path contains whitespace. See PowerShell Core for more information.
--elevate Requests elevation for the PowerShell process (i.e., Run as administrator).
--help Displays a dialog box containing help information.
--quiet Suppresses error message dialogs.
--startdir="path" Specifies a starting directory for the PowerShell process. The quote marks (") are required if the directory name contains whitespace. If you specify --elevate, this parameter is ignored.
--windowstyle=style Sets the window style for the PowerShell process. style Must be one of the following: Normal, Minimized, Maximized, Hidden, NormalNotActive, or MinimizedNotActive.
--windowtitle="title" Specifies a title for the PowerShell console window. The quote marks (") are required if the title contains whitespace.
--configurationname="name" Specifies a configuration endpoint in which PowerShell is run.
--outputformat=format Determines how output from PowerShell is formatted. format must be either Text (text strings) XML (serialized CLIXML format).

If you specify a script filename on the command line, the following options apply:

Option Description
--loadprofile Loads PowerShell profile(s) before running the script. Normally, runposh uses the -NoProfile parameter on the PowerShell command line when running a script; if you specify --loadprofile, runposh omits the -NoProfile parameter when running a script.
--noninteractive Specifies the script should run in non-interactive mode (i.e., this parameter adds the -NonInteractive parameter to PowerShell's command line).
--pause Pauses the console window after the script completes with a Press ENTER to continue: prompt.
--wait Waits for the script to complete and returns the exit code of the PowerShell process. If you omit --wait, runposh simply runs the script using PowerShell and does not wait for PowerShell to exit.
"scriptfile" Specifes the path and filename of the PowerShell script you want to run. The quote marks (") are required if the path or filename contains whitespace.
-- Anything after the -- parameter is command-line parameters for the script.

If you are not running a PowerShell script, the following options apply:

Option Description
--noprofile Do not load PowerShell profile(s) for the interactive console (i.e., runposh adds the -NoProfile parameter to the PowerShell command line).

IMPORTANT: All command-line options are case-sensitive (e.g., you must specify --startdir, not --StartDir or --STARTDIR).

Elevation Notes

The --elevate parameter requests elevation for the PowerShell process. It is a shortcut for the following manual steps:

  1. Right-click the PowerShell icon and choose Run as administrator.
  2. Disable the execution policy.
  3. (If script filename specified) Run the script.

IMPORTANT: The --elevate parameter only provokes the elevation prompt; it cannot automatically enter credentials or bypass the elevation prompt.

If the current process is already elevated, the --elevate parameter does nothing.

Windows automatically sets the starting directory for an elevated process to the System32 directory (e.g., C:\Windows\System32). This is by design for security reasons. This means that, when you use --elevate, runposh ignores the --startdir parameter. Also, if you run a script, you will need to specify the full path and filename of the script.

Do not use --elevate when scheduling a script for unattended use because there's no one to answer the elevation prompt (i.e., runposh will simply hang indefinitely).

PowerShell Core

runposh supports both Windows PowerShell and PowerShell Core. If you don't specify --core, runposh defaults to Windows PowerShell.

In either case, runposh tries to find PowerShell on your system two ways:

  1. by reading the App Paths key in the registry, and
  2. by Searching the Path environment variable

Alternatively, you can specify a path and filename to a specific PowerShell Core executable using the --core parameter (see Usage for more information).

Execution Policy

It is important to understand that PowerShell's execution policy is an administrator safety feature; it is not a security boundary. (Think seatbelt, not door lock.)

It is possible to configure the execution policy to prevent scripts from running, and some domain administrators have configured this option because they are under the impression that it increases security.

This impression is not correct, because PowerShell running a script does not enable the user to bypass security. The user's account is the security boundary. When a user runs PowerShell, the PowerShell process runs as that user. Another way to say this: Running a PowerShell script does not somehow give the user more rights than they had previously.

Preventing script execution does not increase security. The PowerShell execution policy is designed to prevent users from accidentally running unsigned scripts; it should not be (ab)used to try to prevent users from running scripts altogether.

Using PowerShell to automate tasks is not going away; preventing scripts from running breaks many administrative functions and prevents many users from doing their jobs.

Accordingly, runposh disables the execution policy for the PowerShell process it starts (see Technical Details).

Examples

The following table lists some sample commands. (The script names are made up.)

Command Description
runposh Opens a Windows PowerShell console.
runposh --core Opens a PowerShell Core console.
runposh --core="C:\Program Files\PowerShell\Test\pwsh.exe" Opens a PowerShell Core console using the specified PowerShell Core executable.
runposh --elevate Opens an elevated Windows PowerShell console.
runposh --core --noprofile Opens a PowerShell Core console but does not load the PowerShell profile(s).
runposh "C:\Dir Name\Script Name.ps1" Runs a PowerShell script using Windows PowerShell.
runposh --windowstyle=hidden "C:\Script Files\AddUser.ps1" -- "Ken O'Dell" Runs a PowerShell script using Windows PowerShell and some parameters in a hidden window.
runposh --core --elevate --windowstyle=minimized C:\Scripts\MyAdminScript.ps1 Runs an elevated PowerShell script using PowerShell Core in a minimized window.

Technical Details

runposh builds a command string and uses PowerShell's -EncodedCommand parameter to run it. The command string is built using the components listed in the following table:

Command String Description
Disable-ExecutionPolicy Disables the execution policy for the process (see below)
$Host.UI.RawUI.WindowTitle='title' (If -t specified) Sets the PowerShell console window title
& 'scriptfile' [parameters ...] (If running a script) Runs the script file using the invocation/call operator (&)
Read-Host "Press ENTER to continue" (If running a script and --pause specified) Pauses the console window after the script runs
exit $LASTEXITCODE (If running a script and --wait specified) Sets the exit code for the PowerShell process

The Disable-ExecutionPolicy function contains the following code:

function Disable-ExecutionPolicy {
  ($c = $ExecutionContext.GetType().GetField("_context",
  "nonpublic,instance").GetValue($ExecutionContext)).GetType().GetField("authorizationmanager",
  "nonpublic,instance").SetValue($c,
  (New-Object Management.Automation.AuthorizationManager "Microsoft.PowerShell"))
}

The authorizationmanager name depends on whether you are running Windows PowerShell or PowerShell Core. In Windows PowerShell, the authorization manager is named _authorizationManager; in PowerShell Core, it is named <AuthorizationManager>k__BackingField.

runposh constructs the PowerShell command line as follows:

path [-NoExit] [other parameters] -EncodedCommand encodedcommand

Notes:

Once runposh has constructed the command line, it executes PowerShell using the ShellExecuteEx Windows API function. If elevation was requested (--elevate), runposh uses the runas verb; otherwise, it uses the open verb.

If --wait is used, runposh waits for the PowerShell process to terminate. Once PowerShell terminates, runposh exits with PowerShell's exit code.

Version History

1.1 (2020 Mar 05)

1.0 (2020 Feb 24)