Execute PowerShell from a ASP.NET Web Application
I had an interesting request come in to automate existing server-side PowerShell processes that have been initiated by server admins thus far locally on the server, using a web interface instead. Turns out, the admins want to offload the work back to the business users so it can be more on demand. Essentially, they want the non-techies to reuse what the techies have been using server side without having to involve 1) the techies, 2) the overhead of logging in to the server, or worse 3) the business users perform PowerShell commands server side (..and therefore running the risk of executing something incorrectly and/or making a security admin’s head explode).
No problem. Here’s a quick demo on how it can be done.
Our plan of attack will be to build a web form project using Visual Studio 2012 and .NET Framework 4.5. It will execute server side and scripts of PowerShell commands using the
framework’s PowerShell class from the System.Management.Automation name space or dll you can find( C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35). The response I’ll handle as a string, and paint it to a Text box
Here we go…
First, create the Empty Web Application called PowerShellExecution.
Next, add a New Item… of Default web form named “Default.aspx” to the solution to align with the demo content below. Now, copy and paste over the default Default.aspx code that’s generated with the below for the quick UI test:
Next, to get the System.Management.Automation name space included in our solution we need to install the System.Mangement.Automation package. To do that, under the Tools menu select Library Package Manager >> Package Manager Console
…within the
Package Manager Console execute “Install-Package System.Management.Automation”
After a successful execution of that package install, you’ll want to add the reference to your Default.aspx.cs file:
I’m going to use a button’s onclick action to invoke the method that will perform the magic of submitting and reading the response from my PowerShell. The remaining item to address, that may vary on your circumstance, is how to handle the response from the PowerShell.
Remember, everything in PowerShell is returned as an object. We need to display our object back on the page as a string for this demo. You’ll need to be mindful of that when executing, as well as how or what you’re wanting your results displayed on the page.
To ensure that happens for me, I am going manage the output of my PowerShell execution by building a string out of the objects returned. I’ll paint those string casted results in a TextBox control called “ResultBox” using the StringBuilder class within in the System.Text name space. Therefore, I need to reference that name space as well in my code behind.
Below is my complete Default.aspx.cs code for the purposes of this demo. It includes the above references I discussed, the default page load, and especially the onclick method to support the execution details submitted to the Input control using the PowerShell class.
I sprinkled in comments for context:
Copy and paste that in and you’re ready to test…
Once rendered in your browser, type into the “PowerShell Command” window Get-Service | Out-String (or your favorite cmdlet for a test).
Remember, port everything to the Out-String cmdlet so it can be handled properly by the StringBuilder class. Otherwise, you’ll get stringbuilder output of the data type details of the objects returned by your cmdlet – one long get-member call, most likely not what you’re after.
If you’re wondering if you can run scripts that are on the local file system of the server, the answer is yes. Simply pass the script location an parameters in string form to the AddScript() method within Default.aspx.cs using double slashes format.
Contents of this test PowerShellScript.ps1 (same as what was used in the Input control):
(I removed the Input box control and test again the solution below)
Note: To run this on a server, the ApplicationPool hosting your application will have to have administrator rights.
There you have it, a quick server side PowerShell web method.
I had an interesting request come in to automate existing server-side PowerShell processes that have been initiated by server admins thus far locally on the server, using a web interface instead. Turns out, the admins want to offload the work back to the business users so it can be more on demand. Essentially, they want the non-techies to reuse what the techies have been using server side without having to involve 1) the techies, 2) the overhead of logging in to the server, or worse 3) the business users perform PowerShell commands server side (..and therefore running the risk of executing something incorrectly and/or making a security admin’s head explode).
No problem. Here’s a quick demo on how it can be done.
Our plan of attack will be to build a web form project using Visual Studio 2012 and .NET Framework 4.5. It will execute server side and scripts of PowerShell commands using the
framework’s PowerShell class from the System.Management.Automation name space or dll you can find( C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35). The response I’ll handle as a string, and paint it to a Text box
Here we go…
First, create the Empty Web Application called PowerShellExecution.
Next, add a New Item… of Default web form named “Default.aspx” to the solution to align with the demo content below. Now, copy and paste over the default Default.aspx code that’s generated with the below for the quick UI test:
<%@ Page Language= "C#" AutoEventWireup= "true" CodeBehind= "Default.aspx.cs" Inherits= "PowerShellExecution.Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" > <html xmlns= "http://www.w3.org/1999/xhtml" > <head id= "Head1" runat= "server" > <title></title> </head> <body> <form id= "form1" runat= "server" > <div> <table> <tr><td> </td><td><h1 align= "left" >PowerShell Command Harness</h1></td></tr> <tr><td> </td><td> </td></tr> <tr><td> </td><td>PowerShell Command</td></tr> <tr><td> <br /> </td><td> <asp:TextBox ID= "Input" runat= "server" TextMode= "MultiLine" Width= "433px" Height= "73px" ></asp:TextBox> </td></tr> <tr><td> </td><td> <asp:Button ID= "ExecuteCode" runat= "server" Text= "Execute" Width= "200" onclick= "ExecuteCode_Click" /> </td></tr> <tr><td> </td><td><h3>Result</h3></td></tr> <tr><td> </td><td> <asp:TextBox ID= "ResultBox" TextMode= "MultiLine" Width= "700" Height= "200" runat= "server" ></asp:TextBox> </td></tr> </table> </div> </form> </body> </html> |
…within the
Package Manager Console execute “Install-Package System.Management.Automation”
After a successful execution of that package install, you’ll want to add the reference to your Default.aspx.cs file:
using System.Management.Automation; |
Remember, everything in PowerShell is returned as an object. We need to display our object back on the page as a string for this demo. You’ll need to be mindful of that when executing, as well as how or what you’re wanting your results displayed on the page.
To ensure that happens for me, I am going manage the output of my PowerShell execution by building a string out of the objects returned. I’ll paint those string casted results in a TextBox control called “ResultBox” using the StringBuilder class within in the System.Text name space. Therefore, I need to reference that name space as well in my code behind.
using System.Text; |
I sprinkled in comments for context:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Management.Automation; using System.Text; namespace PowerShellExecution { public partial class Default : System.Web.UI.Page { protected void Page_Load( object sender, EventArgs e) { } protected void ExecuteCode_Click( object sender, EventArgs e) { // Clean the Result TextBox ResultBox.Text = string .Empty; // Initialize PowerShell engine var shell = PowerShell.Create(); // Add the script to the PowerShell object shell.Commands.AddScript(Input.Text); // Execute the script var results = shell.Invoke(); // display results, with BaseObject converted to string // Note : use |out-string for console-like output if (results.Count > 0) { // We use a string builder ton create our result text var builder = new StringBuilder(); foreach ( var psObject in results) { // Convert the Base Object to a string and append it to the string builder. // Add \r\n for line breaks builder.Append(psObject.BaseObject.ToString() + "\r\n" ); } // Encode the string in HTML (prevent security issue with 'dangerous' caracters like < > ResultBox.Text = Server.HtmlEncode(builder.ToString()); } } } } |
Once rendered in your browser, type into the “PowerShell Command” window Get-Service | Out-String (or your favorite cmdlet for a test).
Remember, port everything to the Out-String cmdlet so it can be handled properly by the StringBuilder class. Otherwise, you’ll get stringbuilder output of the data type details of the objects returned by your cmdlet – one long get-member call, most likely not what you’re after.
If you’re wondering if you can run scripts that are on the local file system of the server, the answer is yes. Simply pass the script location an parameters in string form to the AddScript() method within Default.aspx.cs using double slashes format.
//shell.Commands.AddScript(Input.Text); shell.Commands.AddScript( "C:\\Scripts\\PowerShell\\PowerShellScript.ps1" ); |
Get-Service | Out-String |
Note: To run this on a server, the ApplicationPool hosting your application will have to have administrator rights.
There you have it, a quick server side PowerShell web method.
Hi Mohan,
ReplyDeleteVery nice post, thanks for sharing it. Could you please tell me how I can contact you if i have a question. I dont want to post it in comment section.
Thanks
Saty
This comment has been removed by the author.
ReplyDeleteHi Mohan,
ReplyDeleteit is very usefull tutorial. However, what I would like to do is when I execute a command such as get-service , I can bring these objects into objects of service class for example , to be able to reuse them. is it possible?
Thanks, it was really interesting and it works as baseline to include some other elements.
ReplyDeleteBTW, I test it and the results pane as you mentioned shows everything as object and thus we need to add an " | Out-String". So I use the following to get that output without specified every time in the text box.
shell.Commands.AddScript(Input.Text + " | out-string");
Yes I know this is a baseline and I'm sure you have tested other ways or components.
Thanks you!
I changed the script to execute to shell.Commands.AddScript("D:\\myfolder\\myscript.ps1 | out-string");
ReplyDeleteIt does work as expected but I do not get any output in the results?
I do have these lines in my script
Write-Host "Current State" $appPoolName (Get-WebAppPoolState $appPoolName).Value -ForegroundColor Green
and
Write-Host "----------Deployment Completed View Deployment log $Deploymentlog" -ForegroundColor Yellow
Is it possible becasue I cam creating logs for an email that gets sent out when finished that it is overriding output to the results box?
Apologies for the typos
ReplyDeleteNice article, worked very well. Do you have more such articles which can help understand using powershell in advanced with asp.net at frontend
ReplyDelete