Exchange Management Shell в Exchange 2010 запускается следующим скриптом (посмотреть можно в свойствах ярлыка):
... -command ". '.RemoteExchange.ps1'; Connect-ExchangeServer -auto"
Фактически при запуске шелла используется функция Connect-ExchangeServer с ключом auto. Сама функция описана в скрипте ConnectFunctions.ps1, который находится в папке с бинарными файлами (там же где и скрипт RemoteExchange.ps1). Ключ auto в свою очередь обращается к функции _AutoDiscoverAndConnect, которая описана в том же файле:
if ($Auto) { _AutoDiscoverAndConnect $credential $Forest -useWIA:$useWIA }
Теперь самое интересное – при запуске этой функции мы используем функции, которые позволяют определить хост, на котором запускается скрипт, лес и структуру окружающих его сайтов с тем, чтобы подключить клиента к “ближайшему” серверу Exchange. Почему я написал “ближайший” в кавычках станет ясно в конце этой небольшой статьи. Итак, по порядку.
Хост получаем через функцию _GetHostFqdn примерно следующим образом:
[System.Net.Dns]::GetHostByName("LocalHost").HostName
Лес определяем через функцию _GetLocalForest:
[System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain().Forest.Name
Сайты ищем через функцию _GetSites. Функция эта фактически возвращает массив, состоящий из имён сайтов. Первый член массива – текущий сайт, в котором находится сервер, с которого мы запускаем EMS:
$localSite=[System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite()
Следующие – так называемые смежные сайты (AdjacentSites). Здесь мы берём все сайт-линки для нашего текущего сайта (фактически те, в которых он имеется в наличии) и вытаскиваем из них либо первый, либо второй сайт и добавляем его имя в наш массив. Выглядит это следующим образом:
if ($localSite.SiteLinks -ne $null) { foreach ($siteLink in $localSite.SiteLinks) { $siteDN = $null # block going backwords if (($siteLink.Sites[0] -ne $null) -and ($siteLink.Sites[0].Name -ne $localSite.Name)) { $siteDN = $siteLink.Sites[0].GetDirectoryEntry().DistinguishedName } elseif ($siteLink.Sites[1] -ne $null) { $siteDN = $siteLink.Sites[1].GetDirectoryEntry().DistinguishedName } if ($siteDN -ne $null) { [void] $SiteList.Add($siteDN) } } }
Ну и наконец, добавляем в массив символ *, которым будут обозначаться все остальные сайты, не попавшие в смежные сайты:
[void] $SiteList.Add("*")
Тут возникает первый момент, который пока мне остался непонятным. Если у нас сайт-линков несколько, то они попадают все в свойство SiteLinks нашего текущего сайта. Выглядеть это будет примерно так:
[PS] C:Windowssystem32>$localSite | fl SiteLinks SiteLinks : {SITELINK1, SITELINK2, SITELINK3}
То есть фактически это будет массив. Как в нём группируются сайт-линки (в каком порядке перечисляются) – непонятно. А ведь от этого зависит какой сайт-линк будет обрабатываться первым, какой вторым итд. Аналогичный момент возникает с массивом сайтов в сайт-линке:
[PS] C:Windowssystem32>$localSite.SiteLinks[0] | fl Sites Sites : {SITE1, SITE2, SITE3}
Сайтов обычно будет несколько, и в каком порядке они перечисляются тоже не совсем ясно.
Возвращаясь к нашей функции. После получения списка сайтов мы пробуем получить список серверов Exchange в каждом из них через функцию _GetExchangeServersInSite. Функция эта ищет все объекты класса msExchExchangeServer в сайте старше определённой версии. Например, для Exchange Server 2010 SP3 это будет выглядеть так:
$configNC=([ADSI]"LDAP:/$Forest/RootDse").configurationNamingContext $search = new-object DirectoryServices.DirectorySearcher([ADSI]"LDAP:/$Forest/$configNC") $search.Filter = "(&(objectClass=msExchExchangeServer)(versionNumber>=1937801568) (msExchServerSite=$siteDN))" $search.PageSize=1000 $search.PropertiesToLoad.Clear() [void] $search.PropertiesToLoad.Add("msexchcurrentserverroles") [void] $search.PropertiesToLoad.Add("networkaddress") [void] $search.PropertiesToLoad.Add("serialnumber") $search.FindAll()
И наконец со списком серверов доходим до функции _ConnectToAnyServer, которая и занимается созданием удалённого (через New-PSSession) подключения к первому доступному из списка серверу Exchange. Причём, сначала пытаемся подключаться к серверам с ролью CAS, затем ко всем остальным.
К чему я это веду. А к тому что, данная процедура не предусматривает оценки стоимости сайт-линков, а тупо перебирает доступные объекты. В итоге администратор удалённого сайта может попытаться подключаться к серверам Exchange, которые находятся совсем не в ближайшем к нему (по стоимости) сайте, а совсем даже на другой континент, например.
Это к слову о пользе правильной архитектуры сайтов AD.
“Первый член массива – текущий сайт, в котором находится сервер, с которого мы запускаем EMS”
Это может быть ни разу и не сервер. Он какие стоимости смотрит? AD или Exchange?
>Это может быть ни разу и не сервер
Это может быть любое устройство, на которое получилось установить консоль управления Exchange 2010
>Он какие стоимости смотрит? AD или Exchange?
Никакие стоимости не смотрит – в этом-то всё и дело. То есть, если сайт в двух сайт-линках, один со стоимостью 10, а второй 10000, то с точки зрения скрипта RemoteExchange.ps1 это два равноценных пути