DaveM
DaveM

Reputation: 135

Trying to dynamically connect in Powershell to nearest Exchange Server

New-PSSession wants a -ConnectionURI of an explict Exchange server. I don't want to hardcode a name in the script (we have 32 servers), and furthermore I want it to select an exchange that is in the same datacenter.

I want a solution similar to Get-ADDomainController -Discover -GetClosestSite But it seems I'm hoping for too much.

I suppose I can pull the members of cn=Exchange Install Domain Servers and do some site dependent ranking on them.

Looking for best practices.

Update Edit: 9/26 I have achieved a solution. It may be site specific, but I'll share below in an answer to show the final code. The answer provided by postanote provided pointers that helped me move forward.

Upvotes: 0

Views: 822

Answers (2)

postanote
postanote

Reputation: 16116

There is no official documented best practices for PowerShell in general (there are too many variables in the mix, but some have put their thoughts in the topic , for example, this one - https://github.com/PoshCode/PowerShellPracticeAndStyle ) or for what you are asking for from Microsoft.

As for you point here:

I suppose I can pull the members of cn=Exchange Install Domain Servers and do some site dependent ranking on them.

This is not something new, or tricky, so you can do this.

I have code in my personal library that I use that does this as well as for other doing resources, so I never have to hardcode server names for Exchange, SQL, DC, etc.

There are several blogs (that have been out there for a while now) on the topic with sample code to use as is or tweak as needed, which is why I asked what you've searched for.

One of those blog examples of how to do this is here:

https://use-powershell.blogspot.com/2012/12/find-exchange-servers-in-domain.html

The examples provided:

  1. an active directory user with mailbox will have 2 attributes (msExchHomeServerName and homemdb) that will contain the name of the mailbox server that has his mailbox - once conected to one server you can use exchange console to find the rest of them;
Get-ADUser samaccountname -Properties msExchHomeServerName, homemdb |Select-Object msExchHomeServerName, homemdb |Format-List
  1. active directory computer type objects contain "exchange" word in servicePrincipalName attribute; you can use only your organizational unit that contain your servers if you have one to narrow your search:
Get-ADComputer -Filter * -SearchBase 'OU= SERVERS, DC=domain_name,DC=net' -Properties * | Where-Object {$_.serviceprincipalname -like '*exchange*'} |select-object  name
  1. active directory configuration partition contain information about exchange servers in domain; you can search for objects of class msExchExchangeServer:
Get-ADObject -LDAPFilter "(objectClass=msExchExchangeServer)" –SearchBase "CN=Configuration,DC=domainname,DC=net" | Select-Object  name

or you can list all objects from "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=INTERNAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domainname,DC=net" using powershell or ADSI Edit console;

Get-ADObject -Filter * -SearchBase "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,CN=INTERNAL,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=domainname,DC=net" -SearchScope onelevel 

Or this post:

https://social.technet.microsoft.com/Forums/ie/en-US/94d89161-9dfb-48fc-b307-2f0e1320c9dc/how-to-find-file-servers-and-exchange-servers-in-ad-

Example:

dsquery * "cn=Configuration,dc=MyDomain,dc=com" -Filter "(objectCategory=msExchExchangeServer)"

Or if you are really trying to get an Exchange server in given site, then this to already exists. See this GitHub source:

https://github.com/mikepfeiffer/PowerShell/blob/master/Get-ExchangeServerInSite.ps1

The sample provided is:

function Get-ExchangeServerInSite {
    $ADSite = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]
    $siteDN = $ADSite::GetComputerSite().GetDirectoryEntry().distinguishedName
    $configNC=([ADSI]"LDAP://RootDse").configurationNamingContext
    $search = new-object DirectoryServices.DirectorySearcher([ADSI]"LDAP://$configNC")
    $objectClass = "objectClass=msExchExchangeServer"
    $version = "versionNumber>=1937801568"
    $site = "msExchServerSite=$siteDN"
    $search.Filter = "(&($objectClass)($version)($site))"
    $search.PageSize=1000
    [void] $search.PropertiesToLoad.Add("name")
    [void] $search.PropertiesToLoad.Add("msexchcurrentserverroles")
    [void] $search.PropertiesToLoad.Add("networkaddress")
    $search.FindAll() | %{
        New-Object PSObject -Property @{
            Name = $_.Properties.name[0]
            FQDN = $_.Properties.networkaddress |
                %{if ($_ -match "ncacn_ip_tcp") {$_.split(":")[1]}}
            Roles = $_.Properties.msexchcurrentserverroles[0]
        }
    }
}

Upvotes: 1

DaveM
DaveM

Reputation: 135

I'm accepting postanote's answer as being most helpful.

In the end, I created a solution that may be site and Exchange install specific, but it does illustrate another technique.

My constraints that were different than most other solutions I found and include; that the given user did not currently have a mailbox on the system. Hence the code beyond this snippet will gather some data, then select a specific database.

I discovered that the Exchange server version we have installed, creates some records in "CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects," In particular, the Members attribute has a list of servers. I extracted that list, sorted it by Site (local to front), and then resolved the FQDN to form a URI to connect.

Function Connect-Exchange {
     #  Find servers list, then sort by name given what site we are running in:  SITE1 = Ascending , SITE2 = Descending
     $ADSite = (Get-ADDomainController).Site
     if ($ADSite -like "SITE1*") { $descOrder =  $true } else { $descOrder = $false }
     $exchSession = $null                                     
     $ExchServersDN = "CN=Exchange Install Domain Servers,CN=Microsoft Exchange System Objects,DC=example,DC=com”
     $ExchServers = (Get-ADObject -Identity $($ExchServersDN) -Properties Member).Member | Sort-Object -Descending:$descOrder 

     # Iterate through Exchange server list until connection succeeds
     $i = 0;
     while ((-Not $exchSession) -and ($i -lt $ExchServers.Count)) { 
        $ExchServerURI = "http://" + (Get-ADObject -Identity $ExchServers[$i] -Properties dNSHostName).dnsHostName + "/Powershell"
        $exchSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI $ExchServerURI  -ErrorAction SilentlyContinue
        If (-Not $exchSession)  { $i++ }
        else {                                                                 
            Import-PSSession $exchSession -DisableNameChecking | Out-Null
        }
     }
     return $exchSession
}

Upvotes: 0

Related Questions