Wednesday, April 6, 2011

Testing Out Load Balanced Mailbox Provisioning in Exchange 2010

Exchange 2010 introduces new functionality in which you can automatically assign a database to a user during the provisioning process. If a database name is not provided to the database parameter of the New-Mailbox cmdlet, a database will be selected automatically. Provisioned mailboxes will be created in a "load balanced" manner across all of the available mailbox databases in the same Active Directory site as the provisioning server – spreading the load, so to speak.
Load balanced mailbox provisioning is made possible by the Mailbox Resources Management cmdlet extension agent. The Mailbox Resources Management Agent is enabled by default, and can be managed using the *-CmdletExtensionAgent cmdlets.

Load Balanced Mailbox Provisioning in Action

I wanted to see how evenly the mailboxes are distributed amongst the available databases when importing a batch of new users from a CSV file. In my lab, I have three mailbox databases; DB1, DB2 and DB3. I created a total of 20 new mailboxes from a CSV file using the following steps:
First, create a password for all the accounts:
$password = ConvertTo-SecureString  -AsPlainText "P@ssw0rd01" -Force
Then, create a hashtable that will be used to track the total amount of mailboxes that are created in each database. Next, import the CSV file, create the mailboxes, and increment the database count where the mailbox is created:
$db = @{}

Import-Csv C:\mailboxes.csv | %{
  New-Mailbox -Name $_.name -Alias $_.alias  -Password $password -UserPrincipalName $_.upn | %{
    $db[$_.database] = $db[$_.database] + 1
  }
}

write $db
After the mailboxes are created return the hashtable with the total number of mailboxes created in each database. Here's the output from running the above code:

As you can see in the above screen shot, I provisioned 20 mailboxes that were created evenly across each database – 6 mailboxes were created in DB1, and 7 mailboxes were created in both DB2 and DB3. So, from this example we can tell that this new feature does a very good job of distributing the load when provisioning multiple mailboxes.

Excluding Databases from the Provisioning Process

You can permanently exclude databases from provisioning by setting the IsExcludedFromProvisioning property value to $true on a particular database. For example, to exclude DB2, we would run the following EMS command:
Set-MailboxDatabase DB2 -IsExcludedFromProvisioning:$true

If you want to temporarily exclude the database from provisioning, set the IsSuspendedFromProvisioning property value to $true, for example:
Set-MailboxDatabase DB2 -IsSuspendedFromProvisioning:$true

So, with this in mind, I suspended DB2 from provisioning and used the code from my previous example to create another 20 mailboxes from a CSV import file to see how the new mailboxes are distributed on each available database:

We can see from the output shown above that DB2 was not used, and we've got pretty even distribution of mailboxes across both available databases.
This may seem like a small improvement over Exchange 2007, but it obviously works very well and has already proven to be very useful in my mailbox provisioning scripts

References:
http://www.mikepfeiffer.net/2010/04/testing-out-load-balanced-mailbox-provisioning-in-exchange-2010/
http://www.mikepfeiffer.net/2010/02/provision-exchange-mailboxes-from-csv-using-powershell-advanced-functions/
http://www.mikepfeiffer.net/2010/06/rbac-database-scopes-in-exchange-server-2010-sp1-beta/

Tuesday, April 5, 2011

How to retrieve a list of Computer Accounts from Active Directory


$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
    {$objComputer = $objResult.Properties; $objComputer.name}
Save the script as GetPCNames.ps1 and run it in your Domain.
Now lets create the Computers.txt file that is used by the "Get-Content" cmdlet examples on this site.
.\GetPCNames.ps1 > "C:\MyScripts\Computers.txt"
I was able to create a list of 4,000 computer names within 3 seconds, results may vary…
  

Here is the explanation of how the script works and who better to explain it than "The Scripting Guy!"
We start out simply enough, assigning the value computer to a variable named $strCategory. We then use this line of code to create a new instance of the .NET Framework class System.DirectoryServices.DirectoryEntry. Because we didn’t specify any additional parameters when creating this instance our object reference $objDomain will automatically connect us to the root of our Active Directory domain. Neat, huh?

Incidentally, here’s the line of code we were talking about:

$objDomain = New-Object System.DirectoryServices.DirectoryEntry

After we connect to the domain we then need to create an instance of the System.DirectoryServices.DirectorySearcher class; that’s the object we’ll use to conduct our Active Directory search. After creating the object we then use these two lines of code to assign values to the DirectorySearcher SearchRoot and Filter properties:

$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("(objectCategory=$strCategory)")

As you probably figured out, the SearchRoot property simply tells DirectorySearcher where to begin searching. We want to search the entire domain so we set SearchRoot to the object reference $objDomain. In turn, the Filter property enables us to target a specific subset of the items found in Active Directory. Because the only thing we care about are computers, we tell the Filter property to bring back only those objects where the objectCategory attribute is equal to the variable $strCategory (which, as you recall, is equal to computer.)

Note. A detailed explanation of how to search Active Directory using Windows PowerShell falls way outside the scope of this little column. We will note, however, that the Filter property requires the LDAP search syntax; you can’t use the more familiar SQL-style syntax here. For more information, see the DirectorySearcher documentation on MSDN.

Our next step is to specify which attributes and attribute values we want our search to bring back. (If you don’t specify a so-called “property list” you’ll get back all the attributes and attribute values, which is not only more information than you need, but probably more data than you want to have clogging up your network.) These two lines of code add the name attribute to the list of property values we want back:

$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}

We should point out that property lists need to be constructed as arrays. Why do we care about that? Well, suppose we wanted to get back both the name and the cn. In that case we’d have to assign the two values to $colProplist using syntax like this:

$colProplist = "name", "cn"

And yes, that is an array: in Windows PowerShell you can create an array just by assigning multiple values (with individual values separated by commas) to a variable.

After we’ve created an array our next chore is to use the PropertiesToLoad method to add those properties to our DirectorySearcher. To do that we set up a For Each loop to loop through all the items in the array, adding each item to the DirectorySearcher:

foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}

Note. What’s that? You don’t know how to create a For Each loop in Windows PowerShell? If that’s the case, you should take a look at our Task-Based Introduction to Windows PowerShell.

Now, at long last, we’re ready to search Active Directory. To do that we call the FindAll() method, storing the data the search brings back in a variable named $colResults:

$colResults = $objSearcher.FindAll()

The only thing left to do now is set up a second For Each loop to loop through all the items (computers) in the collection $colResults. For each item (computer) in the collection we use this snippet of code to store the item Properties in a variable named $objComputer; we do that because, even though we asked for only the name, by default we also get back the computer’s ADsPath, something which can make our output difficult to read:

$objComputer = $objResult.Properties

If we don’t mind difficult-to-read output we could just echo back the value of $colResults and call it good:

$colResults

As soon as the computer Properties have been stashed in $objComputer we can then use this command to echo back just the computer name:

$objComputer.name

That’s going to give us output that looks something like this:

atl-dc-01
atl-dc-02
atl-dc-03
atl-dc-04


References:
http://www.powershellpro.com/computernames-activedirectory/149/