Making the EPiServer scheduler run your Windows PowerShell scripts

Some time ago I wrote a post about retrieving and updating the MaxMind GeoIP database using Windows PowerShell scripts, and suggested the possibility to have the Windows Task Scheduler automating the process. Mostly for my own amusement, I decided to have a go at making the EPiServer scheduler do the work as well; perhaps there would also be scenarios when web administrators would need to run scripts as well, even though they have no access to the server. What I ended up with can be found at the bottom of this post.

RunPowerShellScripts.cs

using System.Management.Automation;
using System.Management.Automation.Runspaces;

Before we begin, you will need to add a reference to the System.Management.Automation assembly to your project for this to work. Your basic installation of Windows PowerShell will not contain this, so go ahead and download the Windows SDK for Windows Server 2008 and .NET Framework 3.5 or the Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1 from the Microsoft website. You will find the binary in your %ProgramFiles%\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 directory after installing it.

RunPowerShellScripts.cs

[EPiServer.PlugIn.ScheduledPlugIn(
    DisplayName = "Run a PowerShell script",
    Description = "A scheduled job showing how to run a PowerShell script on regular intervals.",
    SortIndex = 1
)] 
public class RunPowerShellScript
{
    public static string Execute()
    {
        var message = string.Empty;
        using(var runspace = RunspaceFactory.CreateRunspace())
        {
            message = RunScript(runspace);
        }
        return message;
    }

The Runspace object (17) acts as an operating environment for the command pipeline that we will use to execute our script; so, create one, open it and get the pipeline. In order to execute anything other than cmdlets if you are on default settings (Retricted), you must first set the proper execution policy. This is done using a RunspaceInvoke object (29-30).

private static string RunScript(Runspace runspace)
{
    runspace.Open();
    var pipeline = runspace.CreatePipeline();

    var runspaceInvoke = new RunspaceInvoke(runspace);
    runspaceInvoke.Invoke("Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force");

There are various parameters which you may use (see this short summary for explanations), but the ones above is well suited for this example. Set the ExecutionPolicy to Unrestricted since our script is trusted, and make sure that the setting will only last throughout the Scope of this Process. Force will suppress all warnings that the policy is being changed.

    pipeline.Commands.AddScript(@"D:\Projekt\EPiServer\SampleCode\CodeSample\ScheduledJobs\SampleScript.ps1");
    pipeline.Commands.Add("Out-String");

    var stringBuilder = new StringBuilder();
    foreach (var psObject in pipeline.Invoke())
    {
        stringBuilder.AppendLine(psObject.ToString());
    }
    return stringBuilder.ToString();
}

After adding the script command to the command pipeline (32) we add another command to the collection as well (33); the Out-String command. This will make sure that the output will be delivered as formatted strings rather than the actual objects returned by the script itself. Invoking the pipeline (36) will result in a collection of PSObject which are returned as output of the EPiServer job.

SampleScript.ps1

echo "This message was echoed from the PowerShell script.<br />"
echo "Content of sample directory: "
& ls D:\Projekt\EPiServer\SampleCode\CodeSample\ScheduledJobs\

When triggering the job, the SampleScript.ps1 would give one of the following two outputs depending on whether or not you inserted the Out-String command on line 33.

EPiServer job PowerShell script run success

Source code

Source code: EPiServerPowerShellJob.zip
GitHub-link: https://github.com/matkun/Blog/tree/master/SamplesAndExamples/EPiServerJob_RunPowerShellScript