How to Execute Remote PowerShell Commands using C#
Sunday, April 11th, 2010UPDATE: 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:
- http://blogs.msdn.com/adam/archive/2010/03/26/working-with-scvmm-powershell-in-c-invokepowershell-function.aspx
- http://blogs.msdn.com/dditweb/archive/2008/03/17/working-with-virtual-machines-in-powershell-scvmm-and-c.aspx
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:
