Sunday, May 11, 2008

Running PowerSell Scripts from ASP.Net

I have been writing a few powershell scripts for administrative purposes lately and I thought it would be useful if I could run these scripts from a web page.

You could of course re-write the PowerShell scripts in C# or VB.Net, but I think it's quite useful to have a single script, that you can either run from the commandline, or through a web page.

The following example aspx shows how to read a script from file, pass parameters to the script and then run the script, echoing the output from the script to the web page.

To use this you will need:

  1. This aspx in the root of a web application (see below)
  2. A web.config for the ASP.Net config (see below)
  3. If you're application is running in an application pool using Network Service, that context will need read-access to the root of the virtual directory
  4. A powerShell script (or just paste some script to the textbox)
  5. System.Management.Automation in the .\bin directory of the web application

I even tested a PowerShell script that compiles dynamic VB.Net code and calls an API through PInvoke inside the VB.Net, and that PS1 script worked fine.

Note the use of "Out-String" to return the output in string format. I did try and use pipeline.Output, but that didn't return anything when the PS1 was calling write-output. Also, the parameter passing uses $args, as I couldn't get Params() to work as it does when at the top of a PS1 script.



--
Example web.config


<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.net>
<defaultProxy enabled="false"/>
</system.net>
<system.web>
<pages validateRequest="false"/>
<compilation defaultLanguage="c#" debug="false"/>
<customErrors mode="Off"/>
<authentication mode="Windows"/>
<identity impersonate="true"/>
</system.web>
</configuration>


--
Example test.ps1:

if ($args.count -ge 2)
{ $text = $args[1]}
write-output $text

--
Example Test.aspx

<%@ Page language="c#" AutoEventWireup="true" Debug="true" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Management.Automation.Runspaces" %>
<%@ Import Namespace="System.Management.Automation" %>
<%@ Import Namespace="System.Collections.ObjectModel" %>


<script language="C#" runat="server">

// The previous lines use <%...%> to indicate script code, and they specify the namespaces to import. As mentioned earlier, the assemblies must be located in the \Bin subdirectory of the application's starting point.
// http://msdn.microsoft.com/en-us/library/aa309354(VS.71).aspx


private void Button3_Click(object sender, System.EventArgs e)
{
String fp = Server.MapPath(".") + "\\" + tPowerShellScriptName.Text;
StreamReader sr = new StreamReader(fp);
tPowerShellScriptCode.Text = sr.ReadToEnd();
sr.Close();
}

private void Button2_Click(object sender, System.EventArgs e)
{
tPowerShellScriptResult.Text = RunScript(tPowerShellScriptCode.Text);
}

// http://msdn.microsoft.com/en-us/library/ms714635(VS.85).aspx

private string RunScript(string scriptText)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();

// Create a new runspaces.command object of type script
Command cmdScript = new Command(scriptText, true, false);
cmdScript.Parameters.Add("-t", txtInput.Text);
pipeline.Commands.Add(cmdScript);
//You could also use: pipeline.Commands.AddScript(scriptText);

// Re-format all output to strings
pipeline.Commands.Add("Out-String");

// Invoke the pipeline
Collection<PSObject> results = pipeline.Invoke();

//String sresults = pipeline.Output.Count.ToString();
//sresults = sresults + "," + results.Count.ToString();
String sresults = "";

foreach (PSObject obj in results)
{
sresults = sresults + obj.ToString();
}

// close the runspace and set to null
runspace.Close();
runspace = null;

return sresults;
}


</script>

<form id="Form1" method="post" runat="server">
<P> <asp:Label id="Label1" runat="server" Width="104px">Parameter:</asp:Label>
<asp:TextBox id="txtInput" runat="server"></asp:TextBox></P>
<P> <asp:Button id="Button3" runat="server" Text="Load" OnClick="Button3_Click"></asp:Button> </P>
<P> <asp:Button id="Button2" runat="server" Text="Run" OnClick="Button2_Click"></asp:Button> </P>
<P> <asp:Label id="Label2" runat="server" >Relative script name:</asp:Label>
<asp:TextBox id="tPowerShellScriptName" Text="test.ps1" runat="server"></asp:TextBox></P>
<P> <asp:TextBox rows="20" columns="120" TextMode="multiline" id="tPowerShellScriptCode" runat="server"></asp:TextBox></P>
<P> <asp:TextBox rows="8" columns="120" TextMode="multiline" id="tPowerShellScriptResult" runat="server"></asp:TextBox></P>
</form>


Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

No comments:

Post a Comment