Guide: Powershell and ASP.NET (VB)

There’s an application I built some time ago now that served the function of delegating small subsets of administrative privileges to less technically inclined people with a simple to use web based GUI. This was mostly so that they didn’t need to understand any of the underlying technologies or learn new ones they could simply fill in a couple of text fields, some magic happened and then new user accounts or mailboxes or virtual machines or whatever happened to be the task at hand would be created to spec.

Something I found particularly difficult to find information on (at the time) was executing powershell code from an ASP.NET application… turns out it’s pretty dead simple.

First you need to ensure that you have the assemblies loaded into your web.config

<configuration>
	<system.web>
		<compilation>
			<assemblies>
				<add assembly="System.Management.Automation, version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
			</assemblies>
		</compilation>
	</system.web>
</configuration>

I’ve created a simple sample page below that allows you to execute arbitrary powershell commands from a web browser, if you are only interested in the powershell part just skip to the bottom.

<%@ Page Language="vb" debug="true"%>

Imports System.Management.Automation
Imports System.Management.Automation.Runspaces

<html>
<head>
	<style type="text/css">
		.tableHeader {
			color: #666666;
		}

		.formTable {
			color: #666666;
			background-color: #F5F5F5;
			border-style: none;
		}

		.cellLabel {
			width: 130px;
			text-align: right;
			background-color: #E8E8E8;
			font-weight: bold;
		}

		.cellInput {
			width: 220px;
		}

		.inputSize {
			width: 80%;
		}
	</style>
</head>

<body>
<form id="psTestForm">

	<div id="inputField">
		<span class="tableHeader">&nbsp <b>POWERSHELL FUNCTION</b> &nbsp </span>
		<asp:Table CssClass="formTable" runat="server">
			<asp:TableRow>
     				<asp:TableCell CssClass="cellLabel" runat="server">Script: </asp:TableCell>
     				<asp:TableCell CssClass="cellInput" runat="server"><asp:TextBox runat="server" type="text" id="txtPSInput" CssClass="inputSize"/></asp:TableCell>
     				<asp:TableCell runat="server"><asp:Button Text="Run me" id="cmdExecute" runat="server" OnClick="cmdExecutePS"/></asp:TableCell>
    			</asp:TableRow>
   		</asp:Table>
  	</div>

	<div id="outputField">
		<span class="tableHeader">&nbsp <b>POWERSHELL RETURN DATA</b> &nbsp </span>
   		<asp:Table CssClass="formTable" runat="server">
    			<asp:TableRow>
     				<asp:TableCell CssClass="cellLabel" runat="server">Script Output: </asp:TableCell>
     				<asp:TableCell CssClass="cellInput" runat="server"><asp:TextBox runat="server" type="text" id="txtPSOutput" TextMode="multiline" columns="100" rows="10"/></asp:TableCell>
    			</asp:TableRow>
   		</asp:Table>
  	</div>
</form>
</asp:content>

<script runat="server">
private sub cmdExecutePS(sender as object, e as eventargs)
	'Create a new runspace for Powershell pipelines to run in'
  	Dim psRunSpace As Runspace = RunspaceFactory.CreateRunspace()
  	psRunSpace.Open()

	'Create the pipeline for executing the powershell commands'
  	Dim psPipeline As Pipeline = psRunSpace.CreatePipeline()
	'We use AddScript and Add to issue commands, these will be sequentially run and then piped into the next command in the queue'
  	psPipeline.Commands.AddScript(txtPSInput.Text)
  	psPipeline.Commands.Add("Out-String")

	'Execute the commands stored in the pipeline and store the results'
  	Dim psResults As Collection(Of PSObject) = psPipeline.Invoke()

	'Create a StringBuilder and convert the returned objects into a strings'
  	Dim psResultStringBuilder As New StringBuilder()
  	For Each obj As PSObject In psResults
   		psResultStringBuilder.AppendLine(obj.ToString())
  	Next

	'And now we write the results back out to the webpage and close the runspace!'
  	txtPSOutput.Text = psResultStringBuilder.ToString()
  	psRunSpace.Close()
end sub
</script>
</body>
</html>

From a sysadmin point of view there’s some pretty awesome stuff you can do with this like VMware provisioning, Exchange management or a host of other just general powershell powered goodness.

The only thing that I really haven’t covered here because it’s difficult at the best of times (and will probably end up being its own post) is managing the credentials of the powershell runspace. You see, when you start the runspace it will start a new thread for it, however the credentials that your application runs under may not flow on causing the runspace to open as NETWORK-SERVICE rather than the expected application service account… the below snippet of code in web.config purportedly works to resolve this issue though it has rarely worked for me:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<runtime>
		<legacyImpersonationPolicy enabled="false"/>
		<alwaysFlowImpersonationPolicy enabled="true"/>
	</runtime>
</configuration>

Best of luck with the automating!

comments powered by Disqus