How to call Exchange 2010 cmdlet’s using Remote Powershell in code


I have seen this question posed a lot so I decided that I would post a quick code sample here and the complete sample on my samples site.


In my sample I created a managed client and a native client which both call into a managed DLL that runs the Get-ExchangeServer cmdlet using Remote Powershell.  Here is the method that actually does the work if you don’t want to download the entire sample:


        private const string SHELL_URI = “http://schemas.microsoft.com/powershell/Microsoft.Exchange“;
        public void GetExchangeServers(string serverName, string userName, string password, out string[] servers)
        {


            System.Uri serverUri = new Uri(String.Format(“http://{0}/powershell?serializationLevel=Full“, serverName));
            string[] retVal = null;
            System.Security.SecureString securePassword = new System.Security.SecureString();
           
            foreach (char c in password.ToCharArray())
            {
                securePassword.AppendChar(c);
            }


            System.Management.Automation.PSCredential creds = new System.Management.Automation.PSCredential(userName, securePassword);
           
            RunspaceConfiguration rc = RunspaceConfiguration.Create();
            WSManConnectionInfo wsManInfo = new WSManConnectionInfo(serverUri, SHELL_URI, creds);


            using (Runspace rs = RunspaceFactory.CreateRunspace(wsManInfo))
            {
                rs.Open();
                PowerShell psh = PowerShell.Create();
                psh.Runspace = rs;
                psh.AddCommand(“Get-ExchangeServer”);


                Collection<PSObject> results = psh.Invoke();


                if (psh.Streams.Error.Count > 0)
                {
                    foreach (ErrorRecord err in psh.Streams.Error)
                    {
                        // handle the error some how
                    }
                    rs.Close();
                    servers = null;
                    return;
                }
                retVal = new string[1];


                foreach (PSObject result in results)
                {
                    if (null == retVal[0] || retVal[0].Length == 0)
                    {
                        retVal[0] = result.Members[“Name”].Value as string;
                    }
                    else
                    {
                        string[] newVal = new string[retVal.Length + 1];
                        retVal.CopyTo(newVal, 0);
                        newVal[retVal.Length] = result.Members[“Name”].Value as string;
                        retVal = newVal;
                    }
                }
                rs.Close();
            }
            rc = null;
            servers = retVal;
        }

Comments (13)

  1. Dan says:

    The account being used needs to be in an appropriate RBAC role under 2010 in order to have the appropriate permissions.  The links below cover some information in this area which you may find helpful.

    Understanding Role Based Access Control

    technet.microsoft.com/…/dd298183.aspx

    Understanding Management Roles

    technet.microsoft.com/…/dd298116.aspx

  2. VS6 says:

    Hilariously its easier in VS6

    One line:

    system("c:windowssysnativeWindowsPowershellv1.0powershell.exe -command '& youcommand'");

  3. @VS6 – On the surface that may be true, however if you want to interact with the returned results in an object oriented way I don't think your command will do.

  4. Thanks for the sample!

    I downloaded the complete sample but it won't compile because the type WSManConnectionInfo is not found in the 64-bit version of the reference assembly System.Management.Automation.dll (C:Program FilesReference AssembliesMicrosoftWindowsPowerShellv1.0System.Management.Automation.dll).

    If I change the reference to the 32-bit version of the assembly (C:Program Files (x86)Reference AssembliesMicrosoftWindowsPowerShellv1.0System.Management.Automation.dll), it compiles and runs. However, my process is now running as 32-bit (as indicated by DebugDiag v1.2), and I would like it to be a 64-bit process.

    Any ideas?

    Regards,

    MarkG

  5. Hi Mark,

    It should be available in the 64 bit version.  I will check to confirm; in the meantime I would suggest pulling the automation DLL from the GAC. Look for the 64 bit version in there and be sure that you have RTM version of Powershell 2.0 installed.

  6. Chris Mangiapane says:

    The correct 64-bit (only) 2,0 version of System.Management.Automation is located at:

    C:WindowsassemblyGAC_MSILSystem.Management.Automation1.0.0.0__31bf3856ad364e35System.Management.Automation.dll.

    Check out this post, I found it very helpful:  scorpiotek.com/blog

  7. Petri X says:

    Good example, but how to do this by impersonating the person who is running the code, instead of using hardcoded user ID and password?

  8. Petri X says:

    Thanks Dave, but does your example means that you need to have own login screen on your application? And you cannot use e.g. Windows Integrated authentication? I see the Windows Integrated authentication is the better way to go forward, or what do you think?

  9. The link above to my other article shows you how to use Windows Integrated Authentication which means that it will impersonate the user who is logged into the application.  Therefore, you wouldn't need a login screen provided that you are using Windows Authentication for your application.

  10. Petri X says:

    Ah, just realize it was windows application, I'm looking for an web app application instead. But let see if I can find some useful lines from your code, thanks,

  11. Kim dae jong says:

    Hi ~

    i read your code. i think this code that i found .

    it very useful to me. so i want download your complete code.

    thank you very much.  

  12. Hi Kim dae jong,

    The samples were pulled from the archive site. I am working on finding a new location for them and will update the blog once that is found.

    Dave