Construct queries using information gathered directly from AD
| Executive Summary:|
Use Microsoft Windows PowerShell scripting commands to query Active Directory (AD) to learn more about your Microsoft Exchange Server 2003 organization.
In “Manage Exchange Server 2003 Using Windows PowerShell and WMI”
(http://www.exchangeprovip.com/Article/ArticleID/97261/97261.html), I explained how to exploit the strengths of PowerShell and Windows Management Instrumentation (WMI) to manage Exchange Server 2003. I also discussed how to assemble the interactive commands into a PowerShell script (*.ps1) and run the resulting script securely. In this article, I show you how to use PowerShell to query Active Directory (AD) to learn more about your Exchange organization.
Exchange and Active Directory
In “Manage Exchange Server 2003 Using Windows PowerShell and WMI,” we built the following WMI Query Language (WQL) statement, which includes a where clause:
$exmb = Get-WmiObject
-query "select * from
(storagegroupname='First Storage Group')"
The purpose of the where clause is to filter the query so as to limit the search scope based on one or more criteria, thus optimizing the process. Only objects that satisfy the criteria will be returned to the machine executing the query itself. In order for this to function, you must already know the correct names to use to check against the criteria (e.g., exchangeserver01, First Storage Group), or you must have maintained an accurate record of them in the Exchange organization.
Exchange 2003 relies heavily on a stable and functional AD infrastructure to operate properly. The majority of an Exchange organization’s configuration settings are stored in the Enterprise Configuration partition inside AD, more commonly referred to as the Configuration Naming Context (NC). You can find configuration information, such as Exchange connectors, administrative and routing groups, storage group, and Information Store, on a per server level. For more information, see the Microsoft Exchange Server 2003 Technical Documentation Library at http://technet.microsoft.com/en-us/library/bb123872(EXCHG.65).aspx.
Rather than trying to identify the names of administrative groups, servers, storage groups, or database information stores by painstakingly clicking through Exchange System Manager (ESM) manually, you can probe AD for the most up-to-date information. Use the following PowerShell code to query AD for Exchange configuration information:
$root= New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$cfgNC = \[adsi\]("LDAP://" + $root.Get("configurationNamingContext"))
$search = New-Object System.DirectoryServices.DirectorySearcher($cfgNC)
$search.filter = '(objectclass=msExchExchangeServer)'
$exServer = $search.FindAll()
The first line of code instantiates a directory object from the Microsoft .NET Framework class that goes by the name System.DirectoryServices.DirectoryEntry. The parameter in the parentheses indicates that the object instance should connect straight to the root of the AD domain. This parameter is optional and doesn’t affect the result.
Line 2 defines the AD partition that we want to focus our attention on. You identify this partition by obtaining a reference to the configuration naming context that’s prefixed with the “LDAP://” identifier. Casting the resulting string to \[adsi\] causes PowerShell to return an object bound to an Active Directory Service Interfaces (ADSI) instance, which you then assign to the $cfgNC variable. \[adsi\] is a shortcut (or type accelerator) for the .NET type \[System.DirectoryServices.DirectoryEntry\]; the shortcut is created to help reduce the amount of typing necessary. Thus, you can enter \[adsi\] rather than \[System.DirectoryServices.DirectoryEntry\]; both will give the same result. This shortcut, in addition to the underlying ADSI object adapter, is how PowerShell provisions AD support.
The real work of searching requires yet another object instance created from the System.DirectoryServices.DirectorySearcher class, as line 3 shows. You specify the search scope by passing $cfgNC as a parameter.
Rather than sorting through all the returned items that might not be relevant (not to mention incurring an unnecessary network load and performance hit), you can set up a filter to retrieve only items that match particular criteria, as line 4 shows. You’re working with Exchange servers in this case, so the objectclass attribute must be set to msExchExchangeServer. (For more information about objectclass attributes, see Alain Lissoir, Understanding WMI Scripting, Digital Press, 2003; and Leveraging WMI Scripting, Digital Press, 2003.)
Now you’re ready to perform the actual search with the FindAll method. Line 5 accomplishes this task and stores the result in the $exServer variable.
To add another search criteria based on the names of Exchange storage groups, you can create a new statement similar to the one in line 4 but point it to the correct objectclass, as the following code shows:
$search.filter = '(objectclass=msExchStorageGroup)'
$exStorageGroup = $search.FindAll()
You must call the FindAll method again because you’re working with a different objectclass type. Make sure that you capture the result in a new variable for later use. Table 1 contains a list of commonly used attributes.
Bringing It All Together
Now you can rework the original code to be much more flexible. Instead of hard-coding the parameters for Exchange server and storage groups, you can construct the query using information gathered directly from AD.
Listing 1 contains code for dynamically querying mailbox information. Let’s dissect the code to make it less intimidating.
The first two lines send $exServer to the ForEach-Object cmdlet and store a reference to the current object coming down the pipeline (identified by $_). These steps are done for easy reference, to avoid confusion and overwriting other, unrelated $_ commands referenced elsewhere. Lines 3 and 4 perform the same tricks for $exStorageGroup, which describes the storage groups in the Exchange organization.
In line 5, we cast the distinguishedname property to a string type before we can use the built-in split method of this particular \[string\] class. Because we need only specific text characters to build the complete query, we have to do a bit of work to extract each one of them from the distinguished name (DN). The DN is stored internally in the form CN=First Storage Group,CN=InformationStore,CN=CHZUMX01,CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=swissitpro,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=swissitpro,DC=local.
When we find a match to the name of the server that’s queried in line 6, we construct a string to fully describe the WQL that will pull out information pertaining to mailboxes (line 9). The query statement is built with the criteria from the names of matching Exchange server and storage groups and recorded in the $wql variable.
In line 11, the $exmb variable is assigned the result from running the Get-WmiObject cmdlet. Next, you start harvesting mailbox data that matches the criteria specified by the query.
Although running the code in Listing 1 gives you the same results as running the original query, you don’t need prior knowledge of an Exchange organization’s logical setup to use the code in Listing 1. In addition, this code gives you the flexibility to work with the different objectclass types exposed in Exchange through AD. And you can easily expand the code to further map out the profile for your entire messaging environment.
More Objects Properties
Regardless of the size or complexity of a company, it’s common to find an Exchange organization set up with multiple servers dedicated to messaging. To determine how many servers an organization has, you can use the $exServer.count command to access the count property of the server object denoted by $exServer. Then, you would typically iterate through the objects by piping to the ForEach-Object cmdlet, as I discussed previously. A major advantage of this method is that you don’t have to explicitly maintain a separate counter to index into the objects. Because search results are stored in zero-based collections or arrays of objects in PowerShell, you can still access individual elements by using the syntax $exServer\[i\] (i.e., $exServer\[0\] for the first Exchange server, $exServer\[1\] for the second Exchange server, and so on).
Several specific properties (or attributes) are associated with an object that uniquely identify an Exchange server. You can use the object dot notation to work with either the path or properties attribute. The properties attribute is particularly important because it contains key object information, as the output in Figure 1 shows. To actually access an object property, you must preface it with the properties keyword, such as $exServer\[0\].properties.distinguishedname or $exServer\[0\].properties.cn.
Although you see mainly text output on your host’s display, don’t forget that you’re always dealing with objects in PowerShell. Each property is in fact an object in its own right—not just plain text characters. You can verify the real object type representing a property by piping it to the Get-Member cmdlet (or the alias gm). For instance,
$exServer\[0\].properties.whenchanged | Get-Member
reveals that the whenchanged property is of the type System.DateTime, as the output in Figure 2 shows. In addition, the object’s applicable methods and properties are also displayed.
You might wonder why this information is important. Sometimes it’s useful to know the exact object type to facilitate easy handling. The distinguishedname property is actually of the \[string\] type, but the object itself is embedded in the System.DirectoryServices.ResultPropertyValueCollection, which is a collection or array of objects. No split method exists for this array object type, and invocation will fail unless you perform an explicit cast back to the \[string\] type.
You’ve Got the Power
You can use PowerShell commands to query AD to learn more about your Exchange organization. Then, rather than hard-coding the parameters for Exchange server and storage groups, you can construct a query using information gathered directly from AD. The more you work with PowerShell, the more you’ll discover its potential to help you administer your messaging systems.