Creating an Exchange 2010 Mailbox from a remote C# program

July 28, 2011 at 10:28 am (Computer Science)

On this post, I’ll show you how to create an Exchange Mailbox from a C# program that is not running on the Exchange Server (a client program). What C# is really doing is remotely executing Exchange PowerShell cmdlets.

If this is the first time you’re attempting to run Exchange cmdlets from C#, you probably want to follow this blog post to make sure things are set up properly on the server and that C# can run Exchange cmdlets when running from the server.

This is the client’s code. But it doesn’t “just work”, you need to complete the setup instructions on this post.

using System;
using System.Security;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace PowerShellTest
{
    class Program
    {
        static void Main(string[] args)
        {

            // Prepare the credentials that will be used when connecting
            // to the server. More info on the user to use on the notes
            // below this code snippet.
            string runasUsername = @"username";
            string runasPassword = "password";
            SecureString ssRunasPassword = new SecureString();
            foreach (char x in runasPassword)
                ssRunasPassword.AppendChar(x);
            PSCredential credentials =
                new PSCredential(runasUsername, ssRunasPassword);

            // Prepare the connection
            var connInfo = new WSManConnectionInfo(
                new Uri("http://ServersIpAddress/PowerShell"),
                "http://schemas.microsoft.com/powershell/Microsoft.Exchange",
                credentials);
            connInfo.AuthenticationMechanism =
                AuthenticationMechanism.Basic;

            // Create the runspace where the command will be executed
            var runspace = RunspaceFactory.CreateRunspace(connInfo);

            // generate the command parameters
            var testNumber = 18;
            var firstName = "Test";
            var lastName = "User" + testNumber;
            var username = "tuser" + testNumber;
            var domainName = "pedro.test.local";
            var password = "ActiveDirectoryPassword1234";
            var ssPassword = new SecureString();
            foreach (char c in password)
                ssPassword.AppendChar(c);

            // create the PowerShell command
            var command = new Command("New-Mailbox");
            command.Parameters.Add("Name", firstName + " " + lastName);
            command.Parameters.Add("Alias", username);
            command.Parameters.Add(
                "UserPrincipalName", username + "@" + domainName);
            command.Parameters.Add("SamAccountName", username);
            command.Parameters.Add("FirstName", firstName);
            command.Parameters.Add("LastName", lastName);
            command.Parameters.Add("Password", ssPassword);
            command.Parameters.Add("ResetPasswordOnNextLogon", false);
            command.Parameters.Add(
                "OrganizationalUnit", "NeumontStudents");

            // Add the command to the runspace's pipeline
            runspace.Open();
            var pipeline = runspace.CreatePipeline();
            pipeline.Commands.Add(command);

            // Execute the command
            var results = pipeline.Invoke();

            runspace.Dispose();

            if (results.Count > 0)
                Console.WriteLine("SUCCESS");
            else
                Console.WriteLine("FAIL");

        }
    }
}

Some important things to notice on the code:

  • The “runas” user must:
    1. Belong to a Role Group that has Mail Recipient Creation rights. To do this, make the runas user belong to the “Recipient Management” Role Goup by going to “Exchange Management Console > Microsoft Exchange > Microsoft Exchange On-Premises > Toolbox > Role Based Access Control (RBAC) User Editor”.
    2. Must have Remote PowerShell rights. Do this by going to the Exchange Management Shell and running the following cmdlet: Set-User UserNameHere -RemotePowerShellEnabled:$true
  • It works using Basic authentication, which means that the “runas” credentials are being sent in clear text over the network. This is ok if you trust the network (which is my case because it never leaves the server room) or if you set up SSL.
  • Because of the parameters sent to the New-Mailbox cmdlet, besides creating an Exchange mailbox, I’m also creating an Active Directory user.

You need to configure WinRM on the client to:

  1. Allow unencrypted traffic
  2. Trust the remote machine

The easiest way to to this is via the Local Group Policy user interface. To access it, hit the windows start button, type run, run the “run” program, this opens the famous “run” window (which is wired to the Windows Key + R shortcut), on the run window enter gpedit.msc . You’re now looking at the Local Group Policy user interface. In the tree view go to “Local Computer Policy > Computer Configuration > Administrative Templates > Windows Components > Windows Remote Management (WinRM) > WinRM Client” and configure the previously listed items.

If you don’t configure the WinRM client, you’ll get these exceptions:

System.Management.Automation.Remoting.PSRemotingTransportException was unhandled
  Message=Connecting to remote server failed with the following error message : The WinRM client cannot process the request. Unencrypted traffic is currently disabled in the client configuration. Change the client configuration and try the request again. For more information, see the about_Remote_Troubleshooting Help topic.
  Source=System.Management.Automation
  WasThrownFromThrowStatement=false
  ErrorCode=-2144108322
  TransportMessage=The WinRM client cannot process the request. Unencrypted traffic is currently disabled in the client configuration. Change the client configuration and try the request again.
System.Management.Automation.Remoting.PSRemotingTransportException was unhandled
  Message=Connecting to remote server failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.
  Source=System.Management.Automation
  WasThrownFromThrowStatement=false
  ErrorCode=-2144108316
  TransportMessage=The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config.

On the server, you must configure the PowerShell IIS virtual directory to:

  1. Not Require SSL
  2. Allow Basic Authentication

To not require SSL, you simply go to “IIS Manager > Sites > Default Website > Powershell”, then select the “SSL Settings” feature and make sure “Require SSL” is not checked.

To allow Basic Authentication you go again to “IIS Manager > Sites > Default Website > Powershell”, this time select the “Authentication” feature and enable “Basic Authentication”.

If Basic Authentication is not an option on the Authentication feature page, you need to install it by going to the Server Manager, select the Web Server role, say “Add Role Services”, under the Security node in the treeview, select Basic Authentication.

You’re all set! Running the C# code on a remote machine should work.

About these ads

33 Comments

  1. Running Exchange 2010 Management Shell Commands (PowerShell) with C# « Pedro Liska's Blog said,

    [...] If what you want to do is execute the C# code on a client machine that executes cmdlets on the Exchange server, check out this post. [...]

  2. Nicolas said,

    Hi there,
    very useful post you wrote here. Got one question:

    Can you, and if yes how, create a mailbox on Exchange using an existing Active Directory user?

    Thanks

    • Pedro Liska said,

      I never tested that but I’m sure there is a way. If the New-Mailbox cmdlet does not do it, try Enable-Mailbox .

      • Nicolas said,

        Im testing this now, so fare the Enable-Mailbox fails. Will keep trying and post back.
        Btw, I have to add new permissions to the user:
        New-ManagementRoleAssignment -Role “Mail Recipients” -User username

      • Nicolas said,

        It worked! I guess I had permission issues before, but I was able to use Enable-Mailbox for an existent AD user.

  3. zura said,

    i have this error
    ” Connecting to remote server failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic. “

    • Pedro Liska said,

      please read the “If you don’t configure the WinRM client, you’ll get these exceptions” section of my blog post.

  4. zura said,

    Is written correctly?

    var connInfo = new WSManConnectionInfo(new Uri(“http://server ip address/’C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'”),
    “http://schemas.microsoft.com/powershell/Microsoft.Exchange”, credentials);

    WinRM client I did same as you had in your blog post

    • Pedro Liska said,

      Well, the first parameter when you create the connection does not look right: “http://server ip address/’C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe”. It should be “http://ServersIpAddress/PowerShell” where the ServersIpAddress is the IP address you added to the TrustedHosts .

      The error is clear that this is a problem on the client side so don’t bother tweaking anything on the server until you make this go away.

      I had to play for hours with my client and server configuration to get things working on my environment. Good luck!

  5. Nicolas said,

    Hi Pedro, Ive tried to run the program but im getting this error:
    “””
    Unhandled Exception: System.Management.Automation.RemoteException: The term ‘New-Mailbox’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    at System.Management.Automation.PowerShell.CoreInvoke[TOutput](IEnumerable input, PSDataCollection`1 output, PSInvocationSettings settings)
    at System.Management.Automation.PowerShell.Invoke(IEnumerable input, PSInvocationSettings settings)
    at System.Management.Automation.PowerShell.Invoke(IEnumerable input)
    at System.Management.Automation.RemotePipeline.Invoke(IEnumerable input)
    at System.Management.Automation.Runspaces.Pipeline.Invoke()
    at PowerShell.Program.Main(String[] args) in Program.cs:line 66
    “””

    What can be the problem?

    Thanks

    • Nicolas said,

      got it working, was a permission issue
      solved with:
      New-ManagementRoleAssignment –Role “Mail Recipient Creation” –User domain\user

      • Pedro Liska said,

        Great! And thanks for posting the solution!

  6. sathis said,

    Hi Pedro,
    Great blog awesome. !
    Do you have any sample code to remote c# program exchange 2007 ?.

    • Pedro Liska said,

      I don’t =(. And I think exchange 2007 does not have enough PowerShell support to do things like described on this post.

  7. Chandra said,

    Awesome. Great. Just what I was looking for. I had the code ready but was getting frustrated with the errors. Thanks to your blog, I can sleep well tonight.

    • Sheila Wu said,

      I have done every steps in your post but I still no luck to make it work. It still complains “Connecting to remote server failed with the following error message : The WinRM client cannot process the request. Unencrypted traffic is currently disabled in the client configuration. Change the client configuration and try the request again. For more information, see the about_Remote_Troubleshooting Help topic.”

      Thanks,

      • Pedro Liska said,

        If you have Allow unencrypted traffic and added the remote machine to the trusted hosts, it should work. Are you sure you set up the trusted hosts properly? I know it works if you put the server’s ip address in there.

        One more thing you can do is verify that there are no firewalls blocking the communication. To do this I like to telnet the server from the client on the desired port. I can’t remember which port is used by all of this though. Was it just over port 80?

      • Sheila Wu said,

        I tried to use winrm id -r:”My exchange server ip address”, I got this error:

        WSManFault
        Message = The WinRM client cannot process the request. Default authentication may be used with an IP address under t
        he following conditions: the transport is HTTPS or the destination is in the TrustedHosts list, and explicit credentials
        are provided. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authen
        ticated. For more information on how to set TrustedHosts run the following command: winrm help config.

        Error number: -2144108101 0x803381BB
        The WinRM client cannot process the request. Default authentication may be used with an IP address under the following c
        onditions: the transport is HTTPS or the destination is in the TrustedHosts list, and explicit credentials are provided.
        Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. For m
        ore information on how to set TrustedHosts run the following command: winrm help config.

        Should I do something on the remote server?

        Thanks,

      • Pedro Liska said,

        It sounds like the client is not even sending out the request to the server. I think the problem is on the client side right now. Did you add the server’s IP address to the trusted hosts list? This list is on the client machine.

  8. Sheila Wu said,

    Sorry. I got the error like this:Connecting to remote server failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.

    Thanks,

    • Pedro Liska said,

      Well, try to follow the directions specified in the error. That will probably fix it.

  9. Chandra said,

    Well…I had a similar problem. I later found out that I was not using the correct IP address. Please double check.

    • Sheila Wu said,

      Well. I did check and add the correct server’s ip address. I checked the winrm config and the address looks like:

      5985
      5986

      1.2.3.5

      Any possible those ports have been blocked by firewall in the server?

      Thanks,

      • Pedro Liska said,

        To debug firewall issues I use telnet. I know this is not the purpose of the telnet client but with telnet you receive different responses if ANYTHING is listening on the specified server’s port or if it is blocked by a firewall.

        You usually get a “connection error” if it is blocked by a firewall. And you get a prompt if the connection is successful.

        I would try going to the command prompt on the client and typing “telnet 1.2.3.5 5985″. Also experiment with telnet to see how it behaves with other sites. Enter stuff like “telnet google.com 80″ , on this you get a prompt right away. If you enter “telnet google.com 80888″ it hangs, this means nothing is listening on 80888 or a firewall is blocking you.

      • Sheila Wu said,

        Yes. this port has been blocked by firewall. I will test it again once this port has been opened.

        Thanks,

  10. Sheila Wu said,

    I think the port is opend. Now I get an error like “Connecting to remote server failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.”

    It looks like I don’t have right to access the remote server. I checked the user right and it is a domain administrator and I can this account to login the remote exchange server and no problem at all.

    I checked this account rights: It has Organization management, Recipient management and server management.

    It is working if I use enter-pssession remote server.

    I am not what else I need to do to solve this problem.

    Any help will be appreciated,

    Thanks,

    • Sheila Wu said,

      I finally got everything working. It was a permission problem.

      I used the user name “xxxx” instead of “domain\xxxx”. That’s the problem was.

      Thanks!

      • Pedro Liska said,

        I’m glad it works Sheila!

  11. Ray said,

    Hi, I have used your directions and am executing the code on the exchange server (local) with admin permissions, but I still recieve an error like “serversettings cannot be null”. Any cluse as to what I’m missing?. The only line that I have added apart from your script is command.Parameters.Add( “database”,”ABC-xyz”).
    This DB does exist and is being used.

  12. fredflint said,

    Thank you for putting this code up. I’ve worked through the configuration of the ports, WinRM etc for other tasks so this suited me just fine.

  13. Cory said,

    So when run this way, I’m unable to pipeline exchange 2010 commands into standard powershell commands.

    In other words, “Get-Mailbox foo” works. But I can’t pipeline it into standard powershell cmdlets like “Format-List”:

    The term ‘format-list’ is not recognized as the name of a cmdlet, function, script file, or operable program.

    I could understand if I were just using the standard powershell shell URI and I couldn’t use Exchange cmdlets, but the reverse seems ridiculous.

  14. Karel said,

    Hi, I have checked everything in your post, but when I’m executing the code, I always get the error:
    “Connecting to remote server failed with the following error message : Access is denied. For more information, see the about_Remote_Troubleshooting Help topic.” My runas user is a domain administrator, and i’m using “domain\xxxx” to connect. What is going wrong here?

  15. Clark Devlin said,

    Hi, I tried this code, and followed the requirements.
    the code does not generate any errors, but it doesn’t enable the user-maiblox when i run the code, the result count is 0 when execute the invoke.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: