PaaP: Windows PowerShell as a Platform – Part 2

PaaP: Windows PowerShell as a Platform – Part 2

  • Comments 2

In the previous post for this series, we saw how System.Management.Automation.PowerShell class can be used to run PowerShell commands in a C# application. In all those cases, the runspace that we used was created in a default manner. In this post, we'll look at ways to customize the runspace that we create to execute the commands. Why do we need to customize the runspace? For example, there could be scenarios where you don’t want the user to be able to run a particular command that could change the state of the machine. In that case, you can create a runspace without that command and now, the user does not have access to that command.  Let’s see how we can achieve this. 

There are a whole slew of types that can be used when hosting Windows PowerShell. The most important types (for basic hosting scenarios) that are used in Windows PowerShell hosting are as follows:

Let us take a very simple example of hosting Windows PowerShell.

PowerShell ps = PowerShell.Create(); 
ps.AddCommand("Get-Command")
  .AddParameter("CommandType","Cmdlet")
  .AddParameter("ListImported","true");
ps.Invoke();
 

This example creates a PowerShell object that runs commands in a runspace. It's not evident, but when you use the Create() method of PowerShell to create a runspace, by default, it runs a command to load all of the built-in cmdlets into the runspace. Running the above command (which lists all the commands in the runspace) on a Windows 8 machine adds 259 commands. This is not an efficient use of memory.

In a lot of scenarios, people want to use a runspace that includes only a specific set of commands/language elements. Runspaces that expose a limited set of commands are called constrained runspaces. They are covered in more detail here - http://msdn.microsoft.com/en-us/library/windows/desktop/ee706589(v=vs.85).aspx. To create a constrained runspace, we need to create an InitialSessionState that restricts what is available to the user.

There are 4 steps to creating a constrained runspace for hosting Windows PowerShell:

  1. Create an InitialSessionState. (An InitialSessionState can be thought of as a container that holds the commands that are available when your runspace is opened.)
  2. Add commands (that you want) to the InitialSessionState.
  3. Create a runspace for the initial session state. (A runspace is the environment in which Windows PowerShell commands are executed. )
  4. Open the runspace. This makes the runspace ready for command execution.
  5. Create a PowerShell object against this runspace, add commands to it and execute the commands. (This is what we dealt with in the previous blog post)

Create an InitialSessionState with all built-in core commands

Here's an example of using the constrained runspace technique to create an initial session state with all of the core commands. Use the CreateDefault() method to create an initial session state. Then, create a runspace with that initial session state, open the runspace, and then run commands in the runspace. The commands in Step 4 use the Runspace property of the PowerShell object (ps) to specify the custom runspace (rs). (In this example, we are skipping Step 2 (adding the commands that we want). I will explain that part in the next example.)

Step 1

InitialSessionState iss = InitialSessionState.CreateDefault();

Step 2

Runspace rs = RunspaceFactory.CreateRunspace(iss);

Step 3

rs.Open();

Step 4

PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
ps.AddCommand("Get-Command");
ps.Invoke();

InitialSessionState has three different methods to create a container that holds commands

  • Create - Creates an empty container. No commands are added to this container.
  • CreateDefault - Creates a session state that includes all of the built-in Windows PowerShell commands on the machine. When using this API, all the built-in PowerShell commands are loaded as snapins.
  • CreateDefault2 - Creates a session state that includes only the minimal set of commands needed to host Windows PowerShell. When using this API, only one snapin – Microsoft.PowerShell.Core - is loaded.

The InitialSessionState created in the previous example loads all of the built-in Windows PowerShell commands by default. We can constrain it even further by creating an empty InitialSessionState and then adding only the commands we want.

Create an empty InitialSessionState and add commands to it

In this example, we create an empty initial session state. Then, we use the  SessionStateCmdletEntry class to define cmdlets for this initial session state. Using this class, we create cmdlet entries for the Get-Command and Import-Module cmdlets and add the entries to the initial session state. This enables those cmdlets to run in pipelines in our sessions.

The results show that the session includes only the Get-Command and Import-Module cmdlets.

Step 1 - Create an empty InitialSessionState

InitialSessionState iss = InitialSessionState.Create();

Step 2 - Add commands to it

SessionStateCmdletEntry getCommand = new SessionStateCmdletEntry(
        "Get-Command", typeof(Microsoft.PowerShell.Commands.GetCommandCommand), "");
SessionStateCmdletEntry importModule = new SessionStateCmdletEntry(
        "Import-Module", typeof(Microsoft.PowerShell.Commands.ImportModuleCommand), "");
iss.Commands.Add(getCommand); 
iss.Commands.Add(importModule); 

Step 3 - Create Runspace

Runspace rs = RunspaceFactory.CreateRunspace(iss);

Step 4 – Open the runspace

rs.Open();

Step 5 – Execute Command

PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
ps.AddCommand("Get-Command");
Collection<CommandInfo> result = ps.Invoke<CommandInfo>();
foreach (var entry in result)
{
       Console.WriteLine(entry.Name);
}

Output

Get-Command
Import-Module

The runspace we created here has only two commands, Get-Command and Import-Module. Creating an empty InitialSessionState and adding commands to it makes the application use memory very efficiently.

In the previous example, we can also do away with explicitly creating a Runspace. The System.Management.Automation.PowerShell class has a public API Create that takes the InitialSessionState as parameter. Using this version, Step 3, 4 and 5 can replaced by these set of commands

PowerShell ps = PowerShell.Create(iss);
ps.AddCommand("Get-Command");
Collection<CommandInfo> result = ps.Invoke<CommandInfo>();
foreach (var entry in result)
{
        Console.WriteLine(entry.Name);
}

Here is a chart showing the time taken to create a runspace using the three different methods of InitialSessionState.

clip_image002[3]

As can be seen in these examples, InitialSessionState provides an excellent mechanism to set up a runspace that contains only the commands that you want to expose.

The Windows PowerShell SDK also has a lot of examples for hosting Windows PowerShell. For more information about the Windows PowerShell SDK, see http://msdn.microsoft.com/en-us/library/windows/desktop/ff458115(v=vs.85).aspx.

Indhu Sivaramakrishnan
Windows PowerShell Developer
Microsoft Corporation

Leave a Comment
  • Please add 7 and 6 and type the answer here:
  • Post
Page 1 of 1 (2 items)