none
Exchange 2013 C# Create Mailboxes RRS feed

  • Question

  • Hello All,

    We are prepping for a 2013 upgrade. I am reviewing code for our account creation programs that I wrote in preparation for 2013. Here is a link I found but they have stated that is not the preferred or supported method for mailbox creation for Exchange 2013.

    Here

    Here is the code I am using on our 2007 Implementation that we will be moving away from: based on what I can see it looks like it will work, but it's essentially the same as what's described above.

    public void CreateUserMailbox(string identity, string alias)
            {
                //StreamWriter errorLog = new StreamWriter(@"c:\scripts\logs\AutoAddUsersLog.txt", true);
                ArrayList database = new ArrayList();
                ICollection<PSObject> results;
    
                // Create a runspace. We can't use the RunspaceInvoke class this time
                // because we need to get at the underlying runspace to explicitly
                // add the commands.
                RunspaceConfiguration rc = RunspaceConfiguration.Create();
                PSSnapInException snapEx = null;
                PSSnapInInfo info = rc.AddPSSnapIn(
                    "Microsoft.Exchange.Management.PowerShell.Admin",
                    out snapEx);
                Runspace myRunSpace = RunspaceFactory.CreateRunspace(rc);
                myRunSpace.Open();
    
                // Create a pipeline...
                Pipeline pipeLine = myRunSpace.CreatePipeline();
                Pipeline pipeLine2 = myRunSpace.CreatePipeline();
                Pipeline pipeLine3 = myRunSpace.CreatePipeline();
                Pipeline pipeLine4 = myRunSpace.CreatePipeline();
    
                using (pipeLine)
                {
                    // Create a command object so we can set parameters for selecting the database.
                    Command getDatabase = new Command(@"c:\scripts\ADScripts\createusers\Exchange\database.ps1");
                    pipeLine.Commands.Add(getDatabase);
                    pipeLine.Invoke();
                    pipeLine.Commands.Clear();
                    // Create new StreamReader to read the file filled with users to the ArrayList
                    StreamReader readTxt = new StreamReader(@"c:\temp\db.txt");
                    int i = 0;
                    while (readTxt.EndOfStream != true)
                    {
                        database.Add(readTxt.ReadLine());
                    }
                    readTxt.Close();
                }
                string[] databaseInfo = database[3].ToString().Replace(@"\", ",").Split(',');
                string databaseServer = databaseInfo[0];
                string databaseName = databaseInfo[1] + @"\" + databaseInfo[2];
                databaseServer = databaseServer.Trim();
                databaseName = databaseName.Trim();
                using (pipeLine2)
                {
                    // Create a command object so we can set some parameters
                    // for this command.
                    Command newMbx = new Command("Enable-Mailbox");
                    newMbx.Parameters.Add("Identity", @identity);
                    newMbx.Parameters.Add("Alias", alias);
                    newMbx.Parameters.Add("Database", @databaseServer + @"\" + databaseName);
    
                    // Add the command we've constructed
                    pipeLine2.Commands.Add(newMbx);
    
                    // Execute the pipeline and save the objects returned.
                    results = pipeLine2.Invoke();
    
                    // Print out any errors in the pipeline execution
                    // NOTE: These error are NOT thrown as exceptions!
                    // Be sure to check this to ensure that no errors
                    // happened while executing the command.
                    if (pipeLine2.Error != null && pipeLine2.Error.Count > 0)
                    {
                        errorLog2.WriteLine("ERROR: There were pipeline errors...\n");
                        Trace.WriteLine("ERROR: There were pipeline errors...\n");
                        foreach (object item in pipeLine2.Error.ReadToEnd())
                        {
                            errorLog2.WriteLine("Error: " + item.ToString() + Environment.NewLine);
                            Trace.WriteLine("Error: " + item.ToString() + Environment.NewLine);
                        }
                    }
    
                    // Print out the results of the pipeline execution
                    if (results != null && results.Count > 0)
                    {
                        errorLog2.WriteLine("MAILBOXES CREATED: Created the following      users...\n");
                        Trace.WriteLine("MAILBOXES CREATED: Created the following      users...\n");
                        foreach (PSObject ps in results)
                        {
                            if (ps.Members["UserPrincipalName"].Value != null)
                            {
                                errorLog2.WriteLine("UserPrincipalName: " + ps.Members["UserPrincipalName"].Value + Environment.NewLine);
                                Trace.WriteLine("UserPrincipalName: " + ps.Members["UserPrincipalName"].Value + Environment.NewLine);
                            }
                        }
                    }
                }
                Thread.Sleep(10000);
                using (pipeLine3)
                {
                    // Modify Mailbox
                    pipeLine3.Commands.Clear();
                    Command set1 = new Command("Set-CASMailbox");
                    set1.Parameters.Add("Identity", identity);
                    set1.Parameters.Add("IMAPEnabled", false);
                    set1.Parameters.Add("POPEnabled", false);
                    pipeLine3.Commands.Add(set1);
                    pipeLine3.Invoke();
                }
                Thread.Sleep(10000);
                using (pipeLine4)
                {
                    // Set Retention Policy
                    pipeLine4.Commands.Clear();
                    Command mailRetention = new Command("Set-Mailbox");
                    mailRetention.Parameters.Add("Identity", identity);
                    mailRetention.Parameters.Add("ManagedFolderMailboxPolicy", @"Mailbox Retention Policy");
                    mailRetention.Parameters.Add("ManagedFolderMailboxPolicyAllowed");
                    pipeLine4.Commands.Add(mailRetention);
                    pipeLine4.Invoke();
                }
                pipeLine = null;
                pipeLine2 = null;
                pipeLine3 = null;
                pipeLine4 = null;
                myRunSpace.Close();
                myRunSpace = null;
            }

    The code above may not be the prettiest or most efficient implementation -- I'm not a programmer by trade, I'm a Windows/Citrix/Storage/VMware admin, who happens to write code.

    My question is, what is the supported method for doing this programmatically, if there is one, and if there isn't what are other people doing to accomplish this? I have to have a way to do this via a program as this code replaced two FTEs on our User Provisioning team and has essentially accomplished a 100% reduction in human error (assuming what is placed in Lawson is correct).

    Thanks!

    • Edited by Allen J. George Thursday, April 10, 2014 6:17 PM More information
    Thursday, April 10, 2014 6:12 PM

Answers

All replies

  • The only supported method is to use Remote PowerShell (so in your example you using local PowerShell and loading the Snap-in). There is a decent remote PowerShell sample simular to yours on http://blogs.msdn.com/b/emeamsgdev/archive/2011/08/31/enable-mailbox-against-exchange-2010-from-a-web-application.aspx?Redirected=true

    Cheers
    Glen

    Friday, April 11, 2014 6:08 AM
  • Glen,

    Thanks for the information. Based on what you posted, I'm going to assume all I really need to do is to make my code use a connection the exchange servers using remote powershell instead of importing the module and running the command from my script server. As you probably guessed, the method I pasted above is part of a much larger program that takes input from the Lawson database, processes it, reads template files, creates AD accounts, creates network share storage in various locations, provisions a personal storage location and other goodies. I will work to get this updated and start testing it as soon as we have 2013 ready to roll.

    Thanks again!

    Friday, April 11, 2014 5:42 PM
  • Glen,

    Since you seem to know a bunch about these things -- I currently have this script that I am running in a managed powershell window to obtain information about the exchange databases that is used in the code snippet above. The idea is to find the smallest database by size -- i think the code is clunky and i HATE that there aren't any direct APIs for any of this from MS. Do you know of a better way to find this information or handle this for account creations?

    Get-MailboxDatabase | foreach-object {add-member -inputobject $_ -membertype noteproperty -name mailboxdbsizeinGB -value ([math]::Round(([int64](get-wmiobject cim_datafile -computername $_.server -filter ('name=''' + $_.edbfilepath.pathname.replace("\","\\") + '''')).filesize / 1GB),2)) -passthru} | Sort-Object mailboxdbsizeinGB | format-table -AutoSize identity|out-file "c:\temp\db.txt"


    Monday, April 21, 2014 6:32 PM
  • Is this code still for 2007 or 2013 ?

    In Exchange 2010 the ability to get the Databasesize was added to the Get-MailboxDatabase cmdlet eg

    Get-MailboxDatabase -Status |Select Name, DatabaseSize,AvailableNewMailboxSpace

    The AvailableNewMailboxSpace is an interesting value to keep in mind when provisioning mailboxes http://blogs.technet.com/b/rmilne/archive/2013/08/20/how-to-check-database-white-space-in-exchange.aspx

    On 2007 using WMI like you are is as good as any method that I know.

    Cheers
    Glen

    Tuesday, April 22, 2014 5:06 AM
  • Glen,

    That code was for 07. I am running into serious issues getting some things to work. I see this warning: 

    http://technet.microsoft.com/en-us/library/jj150489.aspx  --< Excerpt below

    Loading the Microsoft.Exchange.Management.PowerShell.SnapIn Windows PowerShell snap-in and running cmdlets other than the *-TransportAgent cmdlets is not supported and may result in irreparable damage to your Exchange deployment.
    You must be a local Administrator on the Client Access server where you want to install, uninstall, or manage transport agents. We do not support the modification of access control lists (ACLs) on Exchange files, directories, or Active Directory objects.

    My question is the following:

    The command Get-MailboxDatabase -Status will not work and return the database size in any test i've tried.

    Supposedly, this will work: http://devblog.rayonnant.net/2011/05/c-remote-powershell-lookup-smallest.html

    Is that method of running powershell in the remoting session supported and kosher? I have to test this all in my production environment (all of IT is in the '13 environment, so blowing it up is not ok).

    Here's the stuff i've been trying that doesn't work or doesn't return the database size.

     private WSManConnectionInfo ConnectionInfo()
            {
                string ConnectionUri = "http://myexchange2013server/PowerShell";
                string sPassword = "removed";
                SecureString sSPassword = new SecureString();
    
                foreach (char X in sPassword)
                    sSPassword.AppendChar(X);
    
                PSCredential Credential = new PSCredential("domainname\\serviceaccount", sSPassword);
    
    
                // Set the connection info
                WSManConnectionInfo ConInfo = new WSManConnectionInfo((new Uri(ConnectionUri)), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", Credential);
                ConInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
                return ConInfo;
            }
            
    private string FindMailboxDatabase()
            {
                // Find the smallest database so that we try to keep the mailbox database sizes balanced
    
                string mailboxDatabase;
    
                // Create a remote runspace
                using (Runspace FindMailboxDatabaseRunspace = RunspaceFactory.CreateRunspace(ConnectionInfo()))
                {
                    FindMailboxDatabaseRunspace.Open();
                    //FindMailboxDatabaseRunspace.RunspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.Powershell.SnapIn");
                    // Create a pipeline to process the commands
                    using (Pipeline FindMailboxDatabasePipeline = FindMailboxDatabaseRunspace.CreatePipeline())
                    {
                        Command findMailboxDatabaseCommand = new Command("Get-MailboxDatabase");
                        //findMailboxDatabaseCommand.AddScript("Get-MailboxDatabase -Status | Select-Object Name, DatabaseSize");
                        findMailboxDatabaseCommand.Parameters.Add(new CommandParameter("Status", null));
                        FindMailboxDatabasePipeline.Commands.Add(findMailboxDatabaseCommand);
                        //Command findMailboxDatabaseCommand2 = new Command("Where-Object");
                        //findMailboxDatabaseCommand2.Parameters.Add("{$_.Name -notlike '*archdb*'}");
                        //FindMailboxDatabasePipeline.Commands.Add(findMailboxDatabaseCommand2);
                        //Command findMailboxDatabaseCommand2 = new Command("Select-Object");
                        //findMailboxDatabaseCommand2.Parameters.Add("Property", "Name, DatabaseSize");
                        //FindMailboxDatabasePipeline.Commands.Add(findMailboxDatabaseCommand2);
                        Collection<PSObject> databases = null;
                        databases = FindMailboxDatabasePipeline.Invoke();
    
                        // Check for errors and report them
                        PipelineReader<object> Errors = FindMailboxDatabasePipeline.Error;
                        if (Errors.Count > 0)
                        {
                            string sError = "Error(s) occurred";
                            foreach (object Error in (IEnumerable)Errors.Read())
                            {
                                sError += Error.ToString() + "";
                            }
                            errorLog2.Write(sError);
                        }
                        foreach (PSObject info in databases)
                        {
                            MessageBox.Show(info.Properties["Name"].Value.ToString());
                            MessageBox.Show(info.Properties["DatabaseSize"].Value.ToString());
                        }
                        mailboxDatabase = databases[0].ToString();
                    }
                }
    
                return mailboxDatabase;
            }
    
    

    Wednesday, May 28, 2014 11:47 PM
  • In regards to the warning you should not be trying to load or use the Snapin on 2010 or 2013 you need to use remote powershell.

    With you remote powershell code I wouldn't go about the way your trying, if you want to filter then use the Filter parameter if its availble (which for Get-MailboxDatabase it isn't) eg http://blogs.msdn.com/b/akashb/archive/2012/08/02/using-filter-select-object-foreach-object-in-c-exchange-powershell-automation.aspx .

    Something like this works okay for me

                    String PSServerName = "gsex2010dev/PowerShell";
                    ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                    String AdminUserName = "user@domain.local";
                    String Password = "password";
    
                    System.Security.SecureString secureString = new System.Security.SecureString();
                    foreach (char c in Password)
                        secureString.AppendChar(c);
                    PSCredential credential = new PSCredential(AdminUserName, secureString);
                    WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://" + PSServerName), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
                    connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
                    connectionInfo.SkipCACheck = true;
                    connectionInfo.SkipCNCheck = true;
    
                    connectionInfo.MaximumConnectionRedirectionCount = 4;
                    Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
                    runspace.Open();
    
                    Pipeline plPipleLine = runspace.CreatePipeline();
                    Command gDataBase = new Command("Get-MailboxDatabase");
                    SwitchParameter swPara = new SwitchParameter(true);
                    gDataBase.Parameters.Add("Status", swPara);
                    plPipleLine.Commands.Add(gDataBase);
                    Collection<PSObject> RsResultsresults = plPipleLine.Invoke();
                    foreach (PSObject obj in RsResultsresults)
                    {
                        Console.WriteLine(obj.Properties["Name"].Value);
                        Console.WriteLine(GetSize(obj.Properties["DatabaseSize"].Value.ToString()));
                        Console.WriteLine(GetSize(obj.Properties["AvailableNewMailboxSpace"].Value.ToString()));
                    }
                    
            }
            private static Int64 GetSize(String rpSize) {
                String Sizeval = rpSize;
                String numItempattern = @"(?=\().*(?=bytes)";
                MatchCollection matchedItemsNumber = Regex.Matches(Sizeval, numItempattern);
                return Int64.Parse(matchedItemsNumber[0].Value.Replace("(", "").Replace(",", ""));
            }
    Cheers
    Glen

    Thursday, May 29, 2014 5:46 AM
  • Glen,

    Thanks for the help once again. Do you have any explanation as to why you have to define a switch parameter and use that against the "Get-MailboxDatabase -Status" to get it to return the DatabaseSize property? Regardless, I'll file that away in my bag of tricks.

    Thanks!

    Thursday, May 29, 2014 3:17 PM
  • That's the way the Get-MailboxDatabase cmdlet has been coded, because using the -Status switch forces the cmdlet to get the disk information which is an extra time consuming task which normally you may not want it to do when using the cmdlet. Its just a way cmdlet designers can add extra functionality without the need to create a seperate cmdlet to return the information.

    Cheers
    Glen

    Friday, May 30, 2014 4:27 AM
  • Hi to all and sorry for my english,

    following Glen suggested link i have completed a web page to (sequentially) create UPN for domain @test.it, create OU for user of @test.it, create user test@test.it and finally (with code suggested) i would mail enable the user test@test.it

    UPN, OU and user creation are implemented with WindowsImpersonationContext and work fine.

    Code regarding mailbox enabling, always report:

    [Server=SERVERNAME,RequestId=a5a9e4b0-9c0b-414e-b1af-66a4b941e77d,TimeStamp=16/09/2014 07:42:22] Access is denied

    User used in connection parameters is Domain Admin and Enterprise Admin and is the same user used in impersonate, therefore enabled to "manage" the system.

    Webpage run in IIS of Exchange therefore for it (i think) Exchange host is localhost. 

    There is a way to receive more details about "who" denied access?

    Exchange 2013 on Windows 2008 R2. Developed with VS 2013.

    Thanks in advanced for any help.

    Kind regards.

    Massimo


    • Edited by xmaximox Tuesday, September 16, 2014 8:04 AM
    Tuesday, September 16, 2014 7:57 AM
  • It sounds like you probably have an issue with Kerberos  Delegation you shouldn't use LocalHost use the FQDN of the Server and make sure you have SPN's setup for the server see http://blogs.msdn.com/b/emeamsgdev/archive/2012/11/05/exchange-web-services-from-a-web-application-using-windows-authentication.aspx and also http://blogs.technet.com/b/askds/archive/2008/11/25/fun-with-the-kerberos-delegation-web-site.aspx has good debug tips.

    Cheers
    Glen

    Wednesday, September 17, 2014 5:32 AM
  • Hi Glen and thanks for your reply,

    I run setspn for FQDN name and for NETBIOS name.

    In AD i change delegation for kerberos only, in IIS/Exchange server object

    Now, when i run script. i receive (translate from italian)

    [Server=SERVERNAME,RequestId=fe42ad0e-99d4-40e1-a420-639528dc6f77,TimeStamp=18/09/2014 17:04:11] user "mydomain.com/myOU/myuser" has not been assigned any management role

    If is relevant, please, note that my asp page run in a different web site than default website where run powershell folder. Same server, same iis but different website.

    Here my code.

    Thanks for your help

            private string strOUBase = "myou";
            private string strDomainNetBIOS = "mydomain";
            private string strPathDominio= "DC=mydomain,DC=com";
            private string strAuthUsernameFull = "mydomain\\myuser";
            private string strAuthUsername = "myuser";
            private string strAuthPassword = "mypassword";
            private string strConnectionURI = "http://myserver/powershell?serializationLevel=Full";
    
            private WSManConnectionInfo ConnectionInfo()
            {
                string ConnectionUri = strConnectionURI;
                string sPassword = strAuthPassword;
                SecureString sSPassword = new SecureString();
    
                foreach (char X in sPassword)
                    sSPassword.AppendChar(X);
    
                PSCredential Credential = new PSCredential(strAuthUsernameFull, sSPassword);
    
                // Set the connection info
                WSManConnectionInfo ConInfo = new WSManConnectionInfo((new Uri(ConnectionUri)), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", Credential);
                ConInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
    
                //Il certificato non è "green" quindi salto tutti i controlli che lo riguardano
                ConInfo.SkipCACheck = true;
                ConInfo.SkipCNCheck = true;
    
                return ConInfo;
            }
    
            private void EnableMailbox(string mailbox, string mailboxDatabase)
        {
            // Check that user is not already enabled
            if (MailboxEnabled(mailbox)) return;
    
            // Create a remote runspace
            using (Runspace EnableMailboxRunspace = RunspaceFactory.CreateRunspace(ConnectionInfo()))
            {
                EnableMailboxRunspace.Open();
    
                // Create a pipeline to process the commands
                using (Pipeline EnableMailboxPipeline=EnableMailboxRunspace.CreatePipeline())
                {
                    Command cmdEnableMailbox=new Command("Enable-Mailbox");
                    cmdEnableMailbox.Parameters.Add("Identity", @mailbox);
                    cmdEnableMailbox.Parameters.Add("Database", @mailboxDatabase);
                    EnableMailboxPipeline.Commands.Add(cmdEnableMailbox);
                    EnableMailboxPipeline.Invoke();
    
                    // Check for errors and report them
                    PipelineReader<object> Errors=EnableMailboxPipeline.Error;
                    if (Errors.Count>0)
                    {
                        string sError="Error(s) occurred";
                        foreach (object Error in (IEnumerable)Errors.Read())
                        {
                            sError+=Error.ToString() + "";
                        }
                        Response.Write(sError);
    
                    }
                }
            }
        }
    
            private bool MailboxEnabled(string mailbox)
        {
            // Check if the user is mail-enabled already
    
            // Create a remote runspace
            using (Runspace MailboxEnabledRunspace = RunspaceFactory.CreateRunspace(ConnectionInfo()))
            {
                MailboxEnabledRunspace.Open();
    
                // Create a pipeline to process the commands
                using (Pipeline MailboxEnabledPipeline = MailboxEnabledRunspace.CreatePipeline())
                {
                    Command cmdEnableMailbox = new Command("Get-User");
                    cmdEnableMailbox.Parameters.Add("Identity", @mailbox);
                    MailboxEnabledPipeline.Commands.Add(cmdEnableMailbox);
                    Collection<PSObject> user = null;
                    user=MailboxEnabledPipeline.Invoke();
    
                    // Check for errors and report them
                    PipelineReader<object> Errors = MailboxEnabledPipeline.Error;
                    if (Errors.Count > 0)
                    {
                        string sError = "Error(s) occurred";
                        foreach (object Error in (IEnumerable)Errors.Read())
                        {
                            sError += Error.ToString() + "";
                        }
                        Response.Write(sError);
                    }
                    foreach (PSMemberInfo info in user[0].Properties)
                    {
                        if (info.Name == "RecipientType")
                        {
                            if (string.Equals(info.Value.ToString(), "UserMailbox", StringComparison.OrdinalIgnoreCase))
                            {
                                Response.Write("User is already mail-enabled");
                                return true;
                            }
                        }
                    }
                }
            }
    
            Response.Write("User is not mail-enabled");
            return false;
        }



    • Edited by xmaximox Thursday, September 18, 2014 5:27 PM
    Thursday, September 18, 2014 5:24 PM
  • The error is telling you what is wrong the "mydomain.com/myOU/myuser" user your using in

     PSCredential Credential = new PSCredential(strAuthUsernameFull, sSPassword);

    Hasn't been delegated Exchange Admin Rights. To use Enable-Mailbox you will need to grant the myuser the Mail Recipients Role http://technet.microsoft.com/en-us/library/dd876911(v=exchg.150).aspx .

    Cheers
    Glen

    • Proposed as answer by xmaximox Monday, September 29, 2014 5:59 AM
    Monday, September 22, 2014 4:46 AM
  • Hi Glen,

    great! Now script work fine. Mailbox has been created and need only a little of settings adjustments.

    Very thanks for your fundamental help.

    Kind regards and thanks another time.

    Massimo

    Monday, September 29, 2014 6:02 AM