PowerShell and .NET are very interoperable and can help to save time, when you're performing generic, every day tasks. For example, let's say that I want to create a Date-Time value that's thirty minutes ago and one for right now, we can do this in one fell swoop in Exchange Management Shell (read: PowerShell):
v2: Get-MessageTrackingLog -Start [System.DateTime]::Now.AddMinutes(-30) -End [System.DateTime]::Now
v3: Get-MessageTrackingLog -Start ([System.DateTime]::Now).AddMinutes(-30) -End ([System.DateTime]::Now)
As you can see, the syntax changes slightly but the methods are the same because they derive from the same .NET class.
Below, I demonstrate constructing a DirectorySearcher object for a specific case. The final script will be published on OneScript, shortly, but I wanted to demonstrate that we can use PowerShell + .NET to solve some complex problems in a rather easy way.
============================================================================================================================================
In rare cases, removal of an Exchange Server from the forest doesn't go according to plan and, without Exchange Management Shell (EMS), finding servers via Active Directory might be a bit of a pain-point. Enter DirectorySearcher.
Here is an example, finding Exchange 2013 mailbox servers in the forest:
$colMBX = @()
$CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain()
$ForestName = $CurrentDomain.Forest.Name
$ForestDC = $ForestName.Replace(".",",DC=")
$ForestLDAP = [ADSI]"LDAP://CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=$ForestDC"
$orgName = $ForestLDAP.psbase.children | where {$_.objectClass -eq 'msExchOrganizationContainer'}
$Path = "LDAP://CN=Servers,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=" + $orgName.Name + ",CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=" + $ForestDC
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.Filter = '(&(&(&(objectCategory=msExchExchangeServer)(msExchCurrentServerRoles:1.2.840.113556.1.4.803:=54)(serialNumber=*15*))))'
$Searcher.PageSize = 10000
$Searcher.SearchScope = "OneLevel"
$Searcher.PropertiesToLoad.Add("Name") | Out-Null
$Searcher.SearchRoot = $Path
$ServerResult = $Searcher.FindAll()
foreach ($result in $ServerResult)
{
$colMBX += $result.Properties.name[0]
}
$colMBX
You'll notice that in the filter we're using the numeric value '1.2.840.113556.1.4.803' between the attribute and the value we're seeking. This OID is an Extensible Matching Rule for the bitwise operator AND, which may also be referred to as 'LDAP_MATCHING_RULE_BIT_AND'. It is not required for use in your filter but does follow RFC convention.
In Exchange 2013, for Mailbox servers we can use the value '54' to search and for CAS servers we can use the value '16385'.
To explain the values, we can demonstrate via table:
Server role | Role value |
---|---|
Mailbox role | 2 |
Client Access role (CAS) | 4 |
Unified Messaging role | 16 |
Hub Transport role | 32 |
The Mailbox role now has the previous roles in one server, so 2 + 4 + 16 + 32 = 54.