ScorpioTek Solutions
07-filler-left Information about ScorpioTek Services Offered by ScorpioTek Training Courses Offered by ScorpioTek Technology Blog by ScorpioTek Contact us for more information on training/services 08-filler-right
06-message

Archive for April, 2010

How to Execute Remote PowerShell Commands using C#

Sunday, April 11th, 2010

UPDATE: The post below works when you are trying to execute simple commands.  I had issues when trying to execute full scripts.  After searching some more, I found amazing tutorials on how to carry out this procedure, check them out:


Before I start this post that I am urging to write (before I forget all I did), I must clarify that all the code here was from various sites/forums.  All I did was to try and get the combination right, which basically took all of my time yesterday.  Nonetheless, at least it’s working.

So what I am writing at this moment is a C# application that will issue SCVMM commands from any machine in my domain.  SCVMM’s API is 100% PowerShell, so anything you need to run automatically via code in SCVMM, needs to be called through PowerShell somehow.

PowerShell v2.0 includes Remoting, which allows you to issue PowerShell commands remotely on other machines by using Windows Remote Management (WinRM).  So let’s see how on Earth this is done:

Pre-reqs

Before you do any of this, you must run the following command in an admin PowerShell command prompt on the machine where you would like to run the remote command:

winrm quickconfig

The Code References

You need to use the correct version of System.Management.Automation.dll.  If you are running Windows 7 / Windows Server 2008 R2, you must include this reference in your project from the following path:

C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll

Note: When importing the aforementioned DLL, I was having all kinds of issues with Visual Studio 2008.  I tried it with Visual Studio 2010 and it worked right away.

The Namespaces

Include the following namespaces in your code:

using System.Management.Automation; // Windows PowerShell namespace
using System.Management.Automation.Runspaces; // Windows PowerShell namespace
using System.Security; // For the secure password

The Function

This is the function you need to invoke when you want to run a remote command.  The incoming string, scriptText, is the PowerShell command you would like to execute:

public static string RunScript(string scriptText)
        {

           Runspace remoteRunspace = null;
           openRunspace("http://SERVERNAME:5985/wsman",
                "http://schemas.microsoft.com/powershell/Microsoft.PowerShell",
                @"DOMAIN\USERNAME",
                "PASSWORD",
                ref remoteRunspace);

                StringBuilder stringBuilder = new StringBuilder();
                using (PowerShell powershell = PowerShell.Create())
                {
                    powershell.Runspace = remoteRunspace;
                    powershell.AddCommand("get-process");
                    powershell.Invoke();
                    Collection<PSObject> results = powershell.Invoke();
                    remoteRunspace.Close();
                    foreach (PSObject obj in results)
                    {
                        stringBuilder.AppendLine(obj.ToString());
                    }
                }

                return stringBuilder.ToString();
         }

Note that you must replace DOMAIN\USERNAME with your domain and username.  You must also replace PASSWORD with your password.  I really don’t know how to carry this out using my logged-in credentials, so until then, I’ll have to include my password in the code, which is crappy but works.  Also note that you must also replace SERVERNAME with the machine you are trying to connect to.  The port MUST be 5985, if you try the default (80), it will fail to connect (at least, it did in my case).

The magic happens inside the static function openRunspace which is defined as follows:

        public static void  openRunspace(string uri, string schema, string username, string livePass, ref Runspace remoteRunspace)
        {
            System.Security.SecureString password = new System.Security.SecureString();
            foreach (char c in livePass.ToCharArray())
            {
                password.AppendChar(c);
            }
            PSCredential psc = new PSCredential(username, password);
            WSManConnectionInfo rri = new WSManConnectionInfo(new Uri(uri), schema, psc);
            rri.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
            rri.ProxyAuthentication = AuthenticationMechanism.Negotiate;
            remoteRunspace = RunspaceFactory.CreateRunspace(rri);
            remoteRunspace.Open();
        }

Let me know if it works for you in the comments.

Sources: