Search Wiki:

PowerShellTunnel Reference

There is a single Visual Studio 2005 solution (PowerShellTunnel.sln) containing two projects.

The first project is the main PowerShellTunnel.dll project which consists of: a) the server (tunnel host) PowerShell cmdlets (Host folder); b) the client (tunnel) PowerShell cmdlets (Client folder); and c) an example embedded PowerShell runspace class for use within a .NET application (Embeddable folder).

The second project (PowerShellTunnel.Embeddable.Example.exe) is an example of a .NET application (a console app) hosting an embedded runspace which you can communicate to from an ordinary PowerShell console window (using a tunnel).

Note that there are assembly references to System.Management.Automation.dll which is a PowerShell SDK assembly - ref the Vista SDK.


PowerShell console or runspace - a PowerShell console is an ordinary PowerShell shell windos, a PowerShell runspace is a non-visual PowerShell 'console'. (A PowerShell console contains a PowerShell runspace.)

Tunnel Host - a (WCF) service host listening for requests to run a script (allowing objects to be piped in) and returning any piped output objects. A Tunnel Host is instantiated by a PowerShell console or runspace against which any arriving scripts are invoked.

Tunnel - a (WCF implemented) connection to a Tunnel Host (local or remote) through which scripts can be sent (along with any objects to be piped in) and receiving any response (incliuding any output piped objects).

PowerShellTunnel Assembly

PowerShellTunnel.dll contains the host and client PowerShell cmdlets and an example embedded PowerShell runspace class.
Before using any of the PowerShell cmdlets you need to:
  1. First: install PowerShellTunnel (see PowerShellTunnel How to Install) - this is a one-off step.
  2. Secondly: each time you open a console (or runspace) you need to add the snapin:
=add-pssnapin PowerShellTunnel

Tunnel Hosting Cmdlets

Tunnel hosting cmdlets are run from a PowerShell console or runspace to add (instantiate) or remove a tunnel host. Each tunnel host is tied to a WCF endpoint address from which it receives each incoming script request (on a .NET thread pool thread) and invokes the script on the PowerShell console or runspace and manages the serialization (using standard serialization to and from a byte array so the types must be known at each end) of objects piped into or out of the invocation.


Only one parameter is input (which may be a pipeline input), the base address to listen on.
A tunnel host (a WCF service host: System.ServiceModel.ServiceHost) instance is returned.
In the current code only Http is used but any WCF-supported transport is possible.
Refer to PowerShellTunnel How to Install for help on any setup configuration you need to do.
Any security or configurability we can defer entrely to WCF.

$tunnelhost = Add-TunnelHost "http://localhost:8000/PowerShellTunnel/Host1"


An added tunnel host should clean itself up when the PowerShell console or runspace's process is closed. Remove-TunnelHost can be used to do this beforehand.

Only one parameter is input (which may be a pipeline input), the tunnel host (WCF service host) to remove (close).

Remove-TunnelHost $tunnelhost

Tunnel cmdlets

Tunnel cmdlets are run from a PowerShell console or runspace to make a connection to an already existing local or remote tunnel host (local or remote). Each tunnel is a WCF connection to one tunnel host. A script can be invoked on the tunnel (Invoke-Tunnel) whereby it is executed on the tunnel hosts' PowerShellconsole or runspace. The tunnel handles serialization (using standard serialization to and from a byte array so types must be known at each end) of objects piped into or out of the script invocation (see PowerShellTunnel Serialization).


There are three parameters:
  1. HostAddress (mandatory) - the address (e.g. "http://...") of the tunnel host.
  2. Binding - optional Binding object if not using the default WsHttpBinding.
  3. NoSelect - optional switch to prevent the new tunnel from automatically being selected as the default (i.e. bypass doing an automatic Select-Tunnel).

$tunnel = Add-Tunnel "http://localhost:8000/PowerShellTunnel/Host1"

A new tunnel instance is returned (if you only ever plan on using one tunnel in the session you don't need to retain this value).

Unless "-NoSelect" is explicitly specified, the newly created tunnel will be 'selected' - see Select-Tunnel below.


Select-Tunnel $tunnel

If you use Add-Tunnel without specifying "-NoSelect" then the newly added tunnel be be automatically 'selected'.

'Selecting' a tunnel has two effects:
  1. Redirect tab-expansion - pressing TAB will normally call function:tabexpansion, Select-Tunnel renames this and substitutes code to pass tab expansion onto the tunnel host instead.
  2. Default tunnel for Invoke-Tunnel - when calling Invoke-Tunnel (see below) a tunnel does not need to be explicitly specified, instead the 'selected' tunnel is used.

If switching between multiple tunnels you could use Select-Tunnel each time in order to redirect tab expansion each time.


Invokes a script on the tunnel host of the optionally specified tunnel. If no tunnel is specified, the currently 'selected' tunnel (as specified by the last call to Select-Tunnel or Add-Tunnel without specifying the -NoSelect switch) is used.

There are three parameters:
  1. ScriptBlock (mandatory) - the script to execute on the tunnel host's PowerShell console or runspace..
  2. Tunnel - optional tunnel instance on which to invoke the script (defaults to the current'y 'selected' tunnel).
  3. -PipeOutput (optional switch) - return serialized (if possible) output to the client. If not provided (the default) then any output will be first formatted on the host (using Out-String -stream) and the resulting strings output to the client.

invoke-tunnel { $x }
invoke-tunnel { $y.Name }
$yLocal = invoke-tunnel -p { $y }; $yLocal # $yLocal is set to a local (serialized & deserialized) copy of $y
invoke-tunnel { $z = "This variable is set on the host!" } 

Example piping objects in and out:
"write line #1", "write line #2", ("write", "line", "#3" ) | invoke-tunnel -p { $input | foreach { write-host $_; "$_".Length } }

Example sending a script file:
'$z = 4; $z = $z*$z; write-host "On Host: $z"; "Return to client: $z" ' > test.ps1
type test.ps1 | invoke-tunnel { invoke-expression $input }
del test.ps1


An added tunnel should clean itself up when the PowerShell console or runspace's process is closed. Remove-Tunnel can be used to do this beforehand.

Only one parameter is input (which may be a pipeline input), the tunnel to remove (close).

Remove-Tunnel $tunnel


The EmbeddableRunspace folder in the PowerShellTunnel project contains several classes implementing an example hidden or embeddable PowerShell runspace EmbeddableRunspace (with no console input or output - though it could be readily extended to log output). There is nothing special in PowerShellTunnel's implementation of these classes except that there is a useful EmbeddableRunspaceExecute helper class which allows you to easily host a runspace on its own thread and specify what application objects to expose (make available to the embedded PowerShell runspace as ordinary PowerShell variables). See the example PowerShellTunnel.Embeddable.Example project to see how this can be done in practice.


Use Startup() to start a thread hosting the the embeddable runspace.


Use ExposeObject() for each object you want to 'publish' to the hosted runspace, e.g.

ExposeObject("myvar1", obj)

which will set a runspace variable $myvar1 to obj where it will be available for any tunnels.


Use AddTunnelHost() to effectively do an Add-TunnelHost (to allow client tunnels to connect to the runspace).

Use another PowerShell console to tunnel (connect) into the embedded runspace using add-tunnel, and then invoke-tunnel.


Use Shutdown() to close the embedded runspace and join the thread.


As we are using WCF Http by default, we assume that access has been granted (from an admin console), e.g. (note that this only needs to be done once).

netsh http add urlacl url="...your host address..." user="YOURMACHINENAME\YOURUSERNAME"

Note that this embeddable runspace example application requires access to PowerShellTunnel.dll but it does not require that it be installed (it does not use any PowerShellTunnel cmdlets).

Install (SnapIn.cs)

SnapIn.cs provides the necessary hooks to allow PowerShell to detect the included cmdlets.


PowerShellTunnel.Embeddable.Example.exe is built by compiling the PowerShellTunnel.Embeddable.Example project in PowerShellTunnel.sln. It is an ordinary Windows console application. Running the application writes some instructions to the console window and stops by waiting on a Console.ReadLine(). PowerShellTunnel.Embeddable.Example embeds an instance of EmbeddableRunspace and exposes three application objects as PowerShell variables:

$myvar1 = a StringBuilder containing the text 'hi there'
$myvar2 = a DateTime of 2008/1/3
$myvar3 = 45.6 as a decimal data type
A tunnel host is hosted at a default address (change this accordingly).

Note that hosting the EmbeddableRunspace does not need to be on a new thread. Whether or not you use a separate thread, be aware of threading issues of incoming tunnel client requests (scripts) operating on your exposed objects. Note that incoming tunnel client requests (scripts) run on WCF threads - normally .NET thread pool worker threads.

You can then open an ordinary PowerShell console window and Add-Tunnel to connect a running PowerShellTunnel.Embeddable.Example.exe instance's runspace's tunnel host and then Invoke-Tunnel scripts on the application's exposed objects, e.g.

invoke-tunnel { $myvar1 }
invoke-tunnel { $myvar2 }
invoke-tunnel { $myvar3 } 
You can set or change variables on the hosted runspace as well as call methods on the objects, and pipe in or out objects in the usual PowerShell way.
Last edited Mar 3 2008 at 7:09 AM  by MatHobbs, version 29
Page view tracker