Finding Exchange servers and their properties in AD using [ADSI] and [ADSISearcher] type accelerators

Overall Principle

The principle is the following:

  • Using a variable to connect to the AD forest’s Configuration partition $RootADConfigPartition = ([ADSI]'LDAP://RootDSE').ConfigurationNamingContext
  • Using another variable, create the ADSISearcher base object $ADConnection = [ADSISearcher]"" which root is by default the current domain, will be the AD forest’s Configuration partition, loaded on the first variable mentionned above $ADConnection.SearchRoot = "LDAP://$RootADConfigPartition"
  • Then use your filter, aka what do you want to search … can be computers with certain names, or computers of certain type, like Exchange server in this example objectClass=MSExchExchangeServer

Scripts to search for Exchange servers

Script

Pasting the script from the above mentionned repository for quick reading convenience:

$RootADConfigPartition = ([ADSI]'LDAP://RootDSE').configurationNamingContext

Write-host "Rood AD config partition" -BackgroundColor Yellow
$RootADConfigPartition

Write-Host "Creating ADSISearcher:" -BackgroundColor Yellow
Write-Host "LDAP://$RootADConfigPartition"

$ADConnection = [ADSISearcher]""
$ADConnection.SearchRoot = "LDAP://$RootADConfigPartition"

$ADConnection.Filter = "(objectClass=MSExchExchangeServer)"
#NOTE: We also could use a filter on the objectcategory attribute, but that requires a Distinguished Name: "(objectCategory=CN=MS-Exch-Exchange-Server,CN=Schema,$RootADConfigPartition)"
write-host "Search root:" -BackgroundColor Yellow
$ADConnection | select -ExpandProperty SearchRoot


$allresults = $ADConnection.FindAll()

$Coll = @()
Foreach ($item in $allresults) {
    $coll+=[pscustomobject]@{
        Name = $Item.Properties['name']
        Version = $Item.Properties['serialNumber']
        Site = @("$($Item.Properties['msExchServerSite'] -Replace '^CN=|,.*$')")
        RolesNb = $Item.Properties['msExchCurrentServerRoles']
        RolesString = Switch ($Item.Properties['msExchCurrentServerRoles']){
                        2   {"MBX"}
                        4   {"CAS"}
                        16  {"UM"}
                        20  {"CAS, UM" -split ","}
                        32  {"HUB"}
                        36  {"CAS, HUB" -split ","}
                        38  {"CAS, HUB, MBX" -split ","}
                        54  {"MBX"}
                        64  {"Edge"}
                        16385   {"CAS"}
                        16439   {"CAS, HUB, MBX"  -split ","}
                            }
    }
}
$coll | ft


$Exchange2010Count = ($Coll | ? {$_.Version -match "14\."} | measure).count
$Exchange2013Count = ($Coll | ? {$_.Version -match "15\.0"}| measure).count
$Exchange2016Count = ($coll | ? {$_.Version -match "15\.1"}| measure).count

#NOTE: I am using (Object with filter | measure).count instead of (Object with filter).count directly
#because when there is only 1 object matching filter, for some reason the (object with filter).count
#return an empty object ... piping to " measure" which is an alias for Measure-Object, the count
#seems to be always right, even when there is only 1 object in the Object collection matching the filter.

Write-Host "Number of Exchange 2010 servers:    $Exchange2010Count"
Write-Host "Number of Exchange 2013 servers:    $Exchange2013Count"
Write-Host "Number of Exchange 2016 servers:    $Exchange2016Count"

<# msExchCurrentServerRoles values:
    The value here is issued from a bitwise value.
    Single role servers:
        MBX=2,
        CAS=4,
        UM=16,
        HT=32,
        Edge=64 
    multirole servers:
        CAS/HT=36,
        CAS/MBX/HT=38,
        CAS/UM=20,
        E2k13 MBX=54,
        E2K13 CAS=16385,
        E2k13 CAS/MBX=16439

#>