Executing PowerShell scripts from C#

In today’s post, I will demonstrate the basics of how to execute PowerShell scripts and code from within a C#/.NET applications. I will walk through how to setup your project prerequisites, populate the pipeline with script code and parameters, perform synchronous and asynchronous execution, capture output, and leverage shared namespaces.

Update 8/7/2014: Here is the downloadable solution file.
Update 11/5/2014: Added a section on execution policy behavior.

Prerequisites:

First, ensure that PowerShell 2.0 or later is installed on the system you are developing on. The features used below will not be supported on PowerShell 1.0. Next, start by making a new console application (targeting .NET 4.0) in Visual Studio.

In the solution explorer, add a project reference to the System.Management.Automation assembly * . On my machine (PowerShell 3.0), it was located at C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0. Then, add using statements for the above referenced assembly, along with System.Collections.Objectmodel, which is used later for execution output.

Note: You will need to install the Windows SDK in order to obtain the System.Management.Automation assembly file.
Note: You do not have to distribute the System.Management.Automation assembly file with your application binaries - it is located in the GAC on machines with Windows PowerShell installed and should resolve automatically.

Prepare the Pipeline for execution:

The first step is to create a new instance of the PowerShell class. The static method PowerShell.Create() creates an empty PowerShell pipeline for us to use for execution. The PowerShell class implements IDisposable, so remember to wrap it up in a using block.

     using (PowerShell PowerShellInstance = PowerShell.Create())
    {
    }

Next, we can add scripts or commands to execute. Call the AddScript() and AddCommand() methods to add this content to the execution pipeline. If your script has parameters, adding them is easy with the AddParameter() method. AddParameter() accepts a string parameter name, and object for the parameter value. Feel free to pass in full object instances/types if you want. The scripts and pipeline handle it just fine. No need to limit yourself with only passing in string values.

The string contents supplied to AddScript() can come from anywhere. Good choices may be text loaded from files on disk or embedded resources, or user input. For the purposes of this tutorial, I’m just hard-coding some example script text.

     using (PowerShell PowerShellInstance = PowerShell.Create())
    {
        // use "AddScript" to add the contents of a script file to the end of the execution pipeline.
        // use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
        PowerShellInstance.AddScript("param($param1) $d = get-date; $s = 'test string value'; " +
                "$d; $s; $param1; get-service");

        // use "AddParameter" to add a single parameter to the last command/script on the pipeline.
        PowerShellInstance.AddParameter("param1", "parameter 1 value!");
    }

Script/Command Execution:

So far, we have a PowerShell pipeline populated with script code and parameters. There are two ways we can call PowerShell to execute it: synchronously and asynchronously.

Synchronous execution:

For synchronous execution, we call PowerShell.Invoke() . The caller waits until the script or commands have finished executing completely before returning from the call to Invoke() . If you don’t care about the items in the output stream, the simplest execution looks like this:

     // invoke execution on the pipeline (ignore output)
    PowerShellInstance.Invoke();

For situations where you don’t need to see or monitor the output or results of execution, this may be acceptable. But, in other scenarios you probably need to peek at the results or perform additional processing with the output that comes back from PowerShell. Let’s see what that code looks like:

     // invoke execution on the pipeline (collecting output)
    Collection<PSObject> PSOutput = PowerShellInstance.Invoke();

    // loop through each output object item
    foreach (PSObject outputItem in PSOutput)
    {
        // if null object was dumped to the pipeline during the script then a null
        // object may be present here. check for null to prevent potential NRE.
        if (outputItem != null)
        {
            //TODO: do something with the output item 
            // outputItem.BaseOBject
        }
    }

The return object from Invoke() is a collection of PSObject instances that were written to the output stream during execution. PSObject is a wrapper class that adds PowerShell specific functionality around whatever the base object is. Inside the PSObject is a member called BaseObject, which contains an object reference to the base type you are working with. If nothing was written to the output stream, then the collection of PSObjects will be empty.

Besides the standard output stream, there are also dedicated streams for warnings, errors, debug, progress, and verbose logging. If any cmdlets leverage those streams, or you call them directly (write-error, write-debug, etc.), then those items will appear in the streams collections.

After invoking the script, you can check each of these stream collections to see if items were written to them.

     // invoke execution on the pipeline (collecting output)
    Collection<PSObject> PSOutput = PowerShellInstance.Invoke();

    // check the other output streams (for example, the error stream)
    if (PowerShellInstance.Streams.Error.Count > 0)
    {
        // error records were written to the error stream.
        // do something with the items found.
    }

In my sample script supplied above, I am writing a few manually-created objects to the output stream and calling the get-service cmdlet, which also writes its output to the stream since I didn’t save the output in a variable.

As a test, I write the type name and ToString() on all of the base objects that came back from the sample script. As you can see, accessing the base object allows you to view or manipulate the object directly as needed. The base types that come back from execution are usually standard .NET types you may already be familiar with, just wrapped up in a PSObject.

     // loop through each output object item
    foreach (PSObject outputItem in PSOutput)
    {
        // if null object was dumped to the pipeline during the script then a null
        // object may be present here. check for null to prevent potential NRE.
        if (outputItem != null)
        {
            //TODO: do something with the output item 
            Console.WriteLine(outputItem.BaseObject.GetType().FullName);
            Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
        }
    }

 Asynchronous execution: 

For asynchronous execution, we call PowerShell.BeginInvoke() . BeginInvoke() immediately returns to the caller and the script execution begins in the background so you can perform other work. Unlike the synchronous Invoke() method, the return type of BeginInvoke() is not a collection of PSObject instances from the output stream. The output isn’t ready yet. The caller is given an IAsyncResult object to monitor the status of the execution pipeline. Let’s start with a really simple example:

     using (PowerShell PowerShellInstance = PowerShell.Create())
    {
        // this script has a sleep in it to simulate a long running script
        PowerShellInstance.AddScript("start-sleep -s 7; get-service");

        // begin invoke execution on the pipeline
        IAsyncResult result = PowerShellInstance.BeginInvoke();

        // do something else until execution has completed.
        // this could be sleep/wait, or perhaps some other work
        while (result.IsCompleted == false)
        {
            Console.WriteLine("Waiting for pipeline to finish...");
            Thread.Sleep(1000);

            // might want to place a timeout here...
        }

        Console.WriteLine("Finished!");
    }

Note: If you wrap the PowerShell instance in a using block like the sample above and do not wait for execution to complete, the pipeline will close itself, and will abort script execution, when it reaches the closing brace of the using block. You can avoid this by waiting for completion of the pipeline, or by removing the using block and manually calling Dispose() on the instance at a later time.

In this first asynchronous example, we ignored the output stream. Again, this is only useful if you don’t care about your script results. Fortunately, the BeginInvoke() method has a few overloads that allow us to extend the functionality. Let’s improve the example by adding output collection and event handling for data hitting the pipeline.

First, we will create a new instance of PSDataCollection<PSObject> . This collection is a thread-safe buffer that will store output stream objects as they hit the pipeline. Next, we will subscribe to the DataAdded event on this collection. This event will fire every time an object is written to the output stream. To use this new output buffer, pass it as a parameter to BeginInvoke() .

I added some other functionality and comments to the next code block. First, notice that you can check the state of the pipeline to see its status. The State will equal Completed when your script is done. If State is Failed, it is likely caused by an unhandled exception that occurred in the script, also known as a terminating error. Second, if your scripts utilize write-error, write-debug, write-progress, etc. (all thread-safe collections), you can review these during or after execution to check for items logged there. I have subscribed to the DataAdded event on the Error stream as well to be notified in real time.

     /// <summary>
    /// Sample execution scenario 2: Asynchronous
    /// </summary>
    /// <remarks>
    /// Executes a PowerShell script asynchronously with script output and event handling.
    /// </remarks>
    public void ExecuteAsynchronously()
    {
        using (PowerShell PowerShellInstance = PowerShell.Create())
        {
            // this script has a sleep in it to simulate a long running script
            PowerShellInstance.AddScript("$s1 = 'test1'; $s2 = 'test2'; $s1; write-error 'some error';start-sleep -s 7; $s2");

            // prepare a new collection to store output stream objects
            PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
            outputCollection.DataAdded += outputCollection_DataAdded;

            // the streams (Error, Debug, Progress, etc) are available on the PowerShell instance.
            // we can review them during or after execution.
            // we can also be notified when a new item is written to the stream (like this):
            PowerShellInstance.Streams.Error.DataAdded += Error_DataAdded;

            // begin invoke execution on the pipeline
            // use this overload to specify an output stream buffer
            IAsyncResult result = PowerShellInstance.BeginInvoke<PSObject, PSObject>(null, outputCollection);

            // do something else until execution has completed.
            // this could be sleep/wait, or perhaps some other work
            while (result.IsCompleted == false)
            {
                Console.WriteLine("Waiting for pipeline to finish...");
                Thread.Sleep(1000);

                // might want to place a timeout here...
            }

            Console.WriteLine("Execution has stopped. The pipeline state: " + PowerShellInstance.InvocationStateInfo.State);

            foreach (PSObject outputItem in outputCollection)
            {
                //TODO: handle/process the output items if required
                Console.WriteLine(outputItem.BaseObject.ToString());
            }
        }
    }

    /// <summary>
    /// Event handler for when data is added to the output stream.
    /// </summary>
    /// <param name="sender">Contains the complete PSDataCollection of all output items.</param>
    /// <param name="e">Contains the index ID of the added collection item and the ID of the PowerShell instance this event belongs to.</param>
    void outputCollection_DataAdded(object sender, DataAddedEventArgs e)
    {
        // do something when an object is written to the output stream
        Console.WriteLine("Object added to output.");
    }

    /// <summary>
    /// Event handler for when Data is added to the Error stream.
    /// </summary>
    /// <param name="sender">Contains the complete PSDataCollection of all error output items.</param>
    /// <param name="e">Contains the index ID of the added collection item and the ID of the PowerShell instance this event belongs to.</param>
    void Error_DataAdded(object sender, DataAddedEventArgs e)
    {
        // do something when an error is written to the error stream
        Console.WriteLine("An error was written to the Error stream!");
    }

As you can see above, the complete asynchronous model allows for some pretty interesting scenarios with long-running scripts. We can listen for events like data being added to the output stream. We can monitor the progress stream to see how much longer a task may take and provide feedback to the UI. We can listen for new errors being written to the error stream and react accordingly, instead of waiting until all execution has completed.

Execution Policy:

When you invoke cmdlets through the PowerShell class in C#, the execution policy behavior is subject to the policy restrictions of the machine. This behavior is the same is if you opened up a PowerShell prompt on the machine. You can issue commands just fine, but invoking another script might get blocked if your execution policy is undefined or restricted in some way. An easy way to get around this is to set the execution policy for the scope of the application process. Simply run Set-ExecutionPolicy and specify the scope to be Process. This should allow you to invoke secondary scripts without altering the machine/user policies. For more information about policies and their order of precedence, see here.

Shared Namespace:

To wrap up our discussion, I’d like to share a potentially useful feature that can expand some of the functionality for your scripts. When you use the PowerShell class to invoke a script, that script has access to the classes inside the caller’s namespace, if they were declared public.

In the example code below, we make a public class with a single public property, and a public static class with a single public method. Both of these items exist in the same namespace as the PowerShell executor code. The script we execute creates a new instance of the class, sets the property, and writes it to the pipeline. The static class and method are then called directly from inside the script, saving the results to a string which is then written to the pipeline as well.

 namespace PowerShellExecutionSample
{
    /// <summary>
    /// Test class object to instantiate from inside PowerShell script.
    /// </summary>
    public class TestObject
    {
        /// <summary>
        /// Gets or sets the Name property
        /// </summary>
        public string Name { get; set; }
    }

    /// <summary>
    /// Test static class to invoke from inside PowerShell script.
    /// </summary>
    public static class TestStaticClass
    {
        /// <summary>
        /// Sample static method to call from insider PowerShell script.
        /// </summary>
        /// <returns>String message</returns>
        public static string TestStaticMethod()
        {
            return "Hello, you have called the test static method.";
        }
    }

    /// <summary>
    /// Provides PowerShell script execution examples
    /// </summary>
    class PowerShellExecutor
    {
        /// <summary>
        /// Sample execution scenario 3: Namespace test
        /// </summary>
        /// <remarks>
        /// Executes a PowerShell script synchronously and utilizes classes in the callers namespace.
        /// </remarks>
        public void ExecuteSynchronouslyNamespaceTest()
        {
            using (PowerShell PowerShellInstance = PowerShell.Create())
            {
                 // add a script that creates a new instance of an object from the caller's namespace
                PowerShellInstance.AddScript("$t = new-object PowerShellExecutionSample.TestObject;" + 
                                             "$t.Name = 'created from inside PowerShell script'; $t;" +
                                             "$message = [PowerShellExecutionSample.TestStaticClass]::TestStaticMethod(); $message");

                // invoke execution on the pipeline (collecting output)
                Collection<PSObject> PSOutput = PowerShellInstance.Invoke();

                // loop through each output object item
                foreach (PSObject outputItem in PSOutput)
                {
                    if (outputItem != null)
                    {
                        Console.WriteLine(outputItem.BaseObject.GetType().FullName);

                        if (outputItem.BaseObject is TestObject)
                        {
                            TestObject testObj = outputItem.BaseObject as TestObject;
                            Console.WriteLine(testObj.Name);
                        }
                        else
                        {
                            Console.WriteLine(outputItem.BaseObject.ToString());
                        }
                    }
                }
            }
        }
    }
} 

As we can see from the console output, the public static class and non-static class were both available from inside the script for use. The ability to leverage public members from the caller’s namespace allows you to create some interesting scenarios calling C# code directly from your PowerShell scripts.