Reputation: 135
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
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:
- 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
- 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
- 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:
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
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