Remote Powershell & calling Powershell cmdlets from C#
April 27, 2010 Leave a comment
Powershell rocks. Why it’s taken the Windows platform so long to get a decent shell I can’t answer but with Powershell V2, it has arrived. For me, the single most useful feature of Powershell is its remoting capabilities.
A while back I was looking at the rather thorny issue of managing the deployment of an ADERANT Expert solution. Being a service orientated architecture, there are a significant number of services to deploy most likely over a number of servers. Feedback from our consultants who were trying to install all the moving parts was that it was too hard to diagnose what had gone wrong and often the problem was a missing pre-requisite. Examples were:
* a component was not configured correctly, e.g. MSDTC
* a required service was not running, e.g. message queueing
* a component was missing, e.g. the .NET 3.5 framework
We needed a way to check that the target machine we were deploying onto satisfied a set of pre-requisites. At the time Powershell V1 was available but it has no remoting capabilities so I had to look else where. After a couple of days of wading through various approaches such as the remote registry, WMI and hitting road blocks with security, performance and compatibility across different OS versions I changed tact. Everything I wanted to do was relatively easy if I was running on the target server so I wrote a Windows Service that could be deployed onto the server and sent a list of pre-requisites to check. While this wasn’t rocket science, it was far more work than it should have been. As it turns out, installations were also proving troublesome for the Microsoft Developer & Platform Evangelism group who were trying to show off the features of .NET 3.5 through a sample app: DinnerNow.Net. In the end they implemented an installer that performed pre-requisite checking and offered resolutions to any issues found. I drew a lot confidence from the sample that maybe I was on to something.
A couple of years later and Powershell V2 is out, it’s part of the OS and it comes with a comprehensive remoting capability. I no longer need to jump through hoops to run commands remotely, my pre-requisite checking service can be replaced with a few lines of script. From the ISE (Integrated Shell Environment) and the console you can open sessions to any machine that has been enabled for remoting (Enable-PSRemoting) and therefore is running the Windows Remote Management (WinRM) service. Now it is very easy to invoke commands on a remote machine:
PS> invoke-command -ComputerName RemoteMachine -ScriptBlock { powershell command }
Note: if a machine is not part of a domain, you need to add the machines that can remote onto it into the trusted hosts collection.
PS> set-item -path WSMan:\localhost\Client\TrustedHosts -Value "trustedMachine" -Force
A common forum post on MSMQ is ‘How do I create private queues on a remote computer?’. Well, here’s how:
invoke-command -ComputerName RemoteServerName -ScriptBlock { [Reflection.Assembly]::LoadWithPartialName("System.Messaging")[System.Messaging.MessageQueue]::Create(".\Private$\MyNewQueue") }
Powershell is capable of calling .NET classes directly, above I’m using the System.Messaging assembly to create a new MSMQ queue. The code inside the script block is run on the target machine, therefore it is written as if running locally.
The administration API for AppFabric is a Powershell API, therefore it is a simple task to remotely configure AppFabric. We have extended our deployment engine to support automated AppFabric deployment using Powershell.
Calling Powershell from C#
At ADERANT, we have designed and built a declarative deployment engine that we use to install all of our server-side components. One of the techniques we use is to call Powershell cmdlets from C#. The basic pattern for doing this is as follows:
using System.Management.Automation; using System.Management.Automation.Runspaces; Collection results; string script = “get-help”; // whatever you want to execute using (Runspace runspace = RunspaceFactory.CreateRunspace()) { try { runspace.Open(); using (Pipeline pipeline = runspace.CreatePipeline()) { pipeline.Commands.AddScript(script); results = pipeline.Invoke(); } } finally { runspace.Close(); } } // convert the script result into a single string StringBuilder stringBuilder = new StringBuilder(); foreach (PSObject obj in results) { stringBuilder.AppendLine(obj.ToString()); } return stringBuilder.ToString();
The Runspace represents a session, a Pipeline is a sequence of commands to execute. Invoking the pipeline will execute the commands and the results are provided as a collection of Powershell objects. In the example above we simple map the result to a string.
Combine the two techniques together and you can build cmdlets in C# and execute them via Powershell on any machine with Powershell remoting enabled.