Если у вашей организации (группы компаний) несколько лесов с несколькими организациями Exchange, то как правило возникает задача иметь контакты всех организаций в единой адресной книге Exchange. Предлагаю вашему вниманию достаточно простое решение с использованием PowerShell, позволяющее собирать учетные записи из удаленных лесов и размещать их в виде контактов в основном лесу.
Причем скрипт отслеживает появление новых учетных записей, блокирование старых, либо изменение тех или иных атрибутов пользователя.
Ранее уже публиковались различные решения для выполнения данной задачи, однако они либо были слишком громоздки и дОроги (например на базе ILM 2007), либо не имели требуемого функционала (ранее опубликованные скрипты на PowerShell). Слева приведена схема тестовой среды – два леса, два домена, собираем инфу о пользователях домена saturn.corp и размещаем в адресной книге домена moon.corp. Хочу сразу заметить, что для полностью автоматической синхронизации необходимо хотя бы одностороннее доверительное отношение между доменами, т.е. учетная запись домена moon.corp, под которой будет выполняться скрипт, должна иметь права на просмотр учетных записей в домене saturn.corp
Общая схема синхронизации:
- Получение с помощью скрипта информации об учетных записях удаленного домена и, в зависимости от их текущего состояния, либо создание новых контактов, либо обновление или удаление существующих в указанном OU;
- Применение политик Exchange к созданным контактам;
- Обновление адресных книг и GAL Exchange
Итак, в первую очередь мы разместим на почтовом сервере в домене moon.corp приведенный ниже скрипт:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#Адрес контроллера домена, к которому подключаемся"; $Domain = 'dc.saturn.corp' Connect-QADService -Service $Domain #Получаем список пользователей удаленного домена $userlist = (Get-QADUser -name [А-Я]* -IncludedProperties DisplayName, title, company, department, mailNickname, Office, PostalCode, l, streetAddress, PhoneNumber, Pager, Mobile, facsimileTelephoneNumber, Email, physicalDeliveryOfficeName, wWWHomePage, AccountIsDisabled ) ForEach ($user in $userlist) { #Пользователь заблокирован? if ($user.AccountIsDisabled -eq $true) { #У пользователя есть почта? if ($user.mail -notlike $NULL) { #Пользователь заблокирован, удаляем существующий контакт Remove-MailContact $user.mail –Confirm:$false } } elseif ($(Get-contact $user.displayname)) { if ($user.mail -notlike $NULL) { #Пользователь не заблокирован, контакт существует, надо обновить Set-Contact $user.email -Company $user.Company -Title $user.Title -Department $user.Department ` -PostalCode $user.PostalCode -StreetAddress $user.StreetAddress ` -MobilePhone $user.mobile -Fax $user.facsimileTelephoneNumber ` -Phone $user.telephoneNumber } } else { if ($user.email -notlike $NULL) { #Пользователь не заблокирован, контакт не существует, надо создать New-MailContact -Name $user.displayname -DisplayName $user.displayname -alias $user.mailnickname ` -OrganizationalUnit moon.corp/Contacts/Saturn -ExternalEmailAddress $user.mail } } } |
Поместим данный скрипт в планировщик и назначим его выполнение, скажем, каждые 1 раз в день в 22 часа. Еще раз обращаю внимание, что учетная запись, под которой будт запускаться задание на выполнение скрипта, должна иметь права на просмотр каталога AD в удаленном домене, а также на запись в OU moon.corp/Contacts/Saturn в целевом.
[tip]Если скрипт расположен в D:\Scripts\SyncContacts\ImportUserAsContactintoAD.ps1, то строка в планировщике (с учетом использования PowerShell 1.0) должна выглядеть так: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command “D:\Scripts\SyncContacts\ImportUserAsContactintoAD.ps1”
[/tip]
Далее нам необходимо создать и применить на сервере Exchange 2007 политику к вновь созданным контактам, исходя из того, что у всех пользователей домена saturn.corp атрибут “Организация” содержит запись “Saturn Corp.“:
1 2 |
New-EmailAddressPolicy -Name "Remote Contacts Policy" -IncludedRecipients MailContact ` -ConditionalCompany "Saturn Corp." -EnabledEmailAddressTemplates "SMTP:%m@moon.ru" |
Последнее, что нам нужно сделать, это периодически (после каждой синхронизации) применять созданную нами политику и обновлять адресные книги, GAL и OAB:
1 2 3 4 |
Update-EmailAddressPolicy -Identity "Remote Contacts Policy" Get-AddressList | Update-AddressList Get-GlobalAddressList | Update-GlobalAddressList Get-OfflineAddressBook | Update-OfflineAddressBook |
Как вы понимаете, данную схему можно использовать для сбора контактов с любого количества удаленных доменов. Все они будут отображаться в адресной книге Outlook, причем со всеми заполненными атрибутами удаленного пользователя.
Успехов!
P.S. Приведенный в статье способ использовался в нашей организации ранее. В настоящее время синхронизация GAL Exchange 2007 осуществляется с помощью бесплатного компонента MIIS 2003 – IIFP. Если вам интересна настройка такой схемы, прошу оставлять свои комментарии – напишу подробную статью.
Все хорошо… только вот есть пользователи скрытые из GAL, а они таким способом все равно будут создаваться…
Вообщем пришел к следующему решению, если кому интересно.
Два скрипта, оба запускаются из Exchange Managment shell
1) на сервере- источнике:
$Mails = get-mailbox | Where-Object -FilterScript {$_.HiddenFromAddressListsEnabled -match “false”} | Select-Object -property “alias”
$filename = “\\Shareserver\share\mailuser_dom1.txt”
del $filename
foreach ($m in $mails) {Get-User $m.alias | Select-Object -property “displayname”,”WindowsEmailAddress”,”Company”,”title”,”pager”,”Phone”,”MobilePhone” >> $Filename }
2) Второй скрипт на сервере- получателе:
$file= gc -Path \\Shareserver\share\mailuser_dom1.txt
foreach ($f in $file) {
if ($f -ne “”){
$s=$f.split(“:”)
$s[1] = $s[1].trimstart()
if ($s[0] -match “displayname”) { $Disp=$s[1]}
if ($s[0] -match “WindowsEmailAddress”) { $Email=$s[1]}
if ($s[0] -match “Company”) {$compa=$s[1]}
if ($s[0] -match “Title”) { $Title=$s[1]}
if ($s[0] -match “Pager”) { $Pager=$s[1]}
if ($s[0] -eq “Phone “) { $Phone=$s[1]}
if ($s[0] -match “MobilePhone”) { $Mobile=$s[1]
$al=$Email.split(“@”)
#Если есть такой контакт
if ((get-mailcontact -identity $Email) -notlike $NULL) {
set-contact -identity $al[0] -company $compa -title $title -pager $pager -phone $phone -mobilephone $mobile -WindowsEmailAddress $EMail -DomainController dc.dom2.com
}
#Если такого контакта еще нeту
if ((get-mailcontact -identity $Email) -like $NULL){
new-mailcontact -name $disp -DisplayName $disp -alias $al[0] -ExternalEmailAddress $Email -PrimarySmtpAddress $Email -OrganizationalUnit “dom2.com/контакты/dom1” -DomainController dc.dom1.com
set-contact -identity $al[0] -company $compa -title $title -pager $pager -phone $phone -mobilephone $mobile -WindowsEmailAddress $EMail -DomainController dc.dom1.com
}
}
}
}
Собственно, запускать на соответствующих Exchange серверах. если ставить в Шедуллер, то powershell надо вызывать примерно так:
C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -PSConsoleFile “C:\Program Files\Microsoft\Exchange Server\bin\exshell.psc1” -command “C:\Script\ScriptName.ps1”
Мобыть кому поможет.
Очень было бы интересно узнать про автоматизацию этого решения с помощью MIIS 2003
Про MIIS 2003 очень интересно было бы почитать
PowerShell это вещь )
Благодарю за интересную и полезную статью.
Попытался применить данный скрипт – ничего не вышло.
Выпадает ошибка:
The term ‘Connect-QADService’ is not recognized as the name of a cmdlet, functi
on, script file, or operable program. Check the spelling of the name, or if a p
ath was included, verify that the path is correct and try again.
At line:1 char:19
+ Connect-QADService <<<< -Service /?
+ CategoryInfo : ObjectNotFound: (Connect-QADService:String) [],
CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Подскажите, где брать данный командлет?
Версия PowerShell установлена последняя.
С уважением, Кобзарев Д. В.
Connect-QADService это командлет powershell от сторонних разработчиков, в данном случае Quest Software http://www.quest.com/powershell/activeroles-server.aspx
Также было бы очень интересно почитать про MIIS 2003.
С уважением, Кобзарев Д. В.
Спасибо, понравилось
Cool!
Добрый день!
А если мне не нужно удалять контакты вообще, мне необходимо просто взять учетки из домена Win 2000 c MS Exchage 2000 и поместить их контакты в новый лес, где стоит Windows Server 2008 R2 и MS Exchange 2010!
Add-Type -Language CSharpVersion3 -TypeDefinition @”
public class UserAccountControl
{
public UserAccountControl(string str)
{
foreach (string s in str.Split(‘,’))
{
switch (s.Trim().ToUpper())
{
case “NORMALACCOUNT”:{
NormalAccount = true;
break;
}
case “DONOTEXPIREPASSWORD”: {
DoNotExpirePassword = true;
break;
}
case “PASSWORDNOTREQUIRED”: {
PasswordNotRequired = true;
break;
}
case “ACCOUNTDISABLED”: {
AccountDisabled = true;
break;
}
}
}
}
public bool NormalAccount { get; set; }
public bool DoNotExpirePassword { get; set; }
public bool PasswordNotRequired { get; set; }
public bool AccountDisabled { get; set; }
}
“@
function RemoveNonExistentContacts($csvObjects)
{
$contacts = Get-MailContact
foreach($contact in $contacts)
{
$IsNotExistentContact = $true
foreach($csvObject in $csvObjects)
{
if ($contact.Alias -eq $csvObject.SamAccountName)
{
$IsNotExistentContact = $false
break
}
}
if ($IsNotExistentContact -eq $true)
{
Remove-MailContact $contact.Alias -confirm:$false
}
}
}
function UpdateContact($UserObject)
{
$userAccountControl = New-Object -TypeName UserAccountControl -ArgumentList $UserObject.UserAccountControl
if ((Get-MailContact $UserObject.SamAccountName) -ne $Null)
{
if ($userAccountControl.AccountDisabled -eq “true”)
{
Remove-MailContact $UserObject.SamAccountName -confirm:$false
}
else
{
if ($UserObject.RecipientType -eq “UserMailbox”)
{
Set-MailContact $UserObject.SamAccountName -ExternalEmailAddress $UserObject.WindowsEmailAddress
-Name $UserObject.Name
Set-Contact $UserObject.SamAccountName -DisplayName $UserObject.DisplayName
-FirstName $UserObject.FirstName -LastName $UserObject.LastName
-City $UserObject.City -Company $UserObject.Company -Department $UserObject.Department
-Office $UserObject.Office -HomePhone $UserObject.HomePhone
-MobilePhone $UserObject.MobilePhone -Phone $UserObject.Phone -Fax $UserObject.Fax
-PostalCode $UserObject.PostalCode -Title $UserObject.Title
}
}
}
elseif (($userAccountControl.AccountDisabled -ne $true) -and ($UserObject.RecipientType -eq “UserMailbox”))
{
New-MailContact -Name $UserObject.Name -Alias $UserObject.SamAccountName
-ExternalEmailAddress $UserObject.WindowsEmailAddress -OrganizationalUnit agidel.ru/EmailContacts
Set-Contact $UserObject.SamAccountName -DisplayName $UserObject.DisplayName
-FirstName $UserObject.FirstName -LastName $UserObject.LastName
-City $UserObject.City -Company $UserObject.Company -Department $UserObject.Department
-Office $UserObject.Office -HomePhone $UserObject.HomePhone
-MobilePhone $UserObject.MobilePhone -Phone $UserObject.Phone -Fax $UserObject.Fax
-PostalCode $UserObject.PostalCode -Title $UserObject.Title
}
}
# #region Раздел выполнения скрипта
$csv = Import-Csv -Delimiter “;” -Path C:\users.csv
#Подключаемся к серверу
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://domain.com/PowerShell/ -Authentication Kerberos
Import-PSSession $Session
#Удаляем контакты, которых не существует в импортируемом файле
RemoveNonExistentContacts($csv)
#Обновляем данные
foreach($object in $csv)
{
UpdateContact -UserObject $object
}
#Закрываем соединение с сервером
Remove-PSSession $Session
# #endregion
Add-Type -Language CSharpVersion3 -TypeDefinition @”
public class UserAccountControl
{
public UserAccountControl(string str)
{
foreach (string s in str.Split(‘,’))
{
switch (s.Trim().ToUpper())
{
case “NORMALACCOUNT”:{
NormalAccount = true;
break;
}
case “DONOTEXPIREPASSWORD”: {
DoNotExpirePassword = true;
break;
}
case “PASSWORDNOTREQUIRED”: {
PasswordNotRequired = true;
break;
}
case “ACCOUNTDISABLED”: {
AccountDisabled = true;
break;
}
}
}
}
public bool NormalAccount { get; set; }
public bool DoNotExpirePassword { get; set; }
public bool PasswordNotRequired { get; set; }
public bool AccountDisabled { get; set; }
}
“@
function RemoveNonExistentContacts($csvObjects)
{
$contacts = Get-MailContact
foreach($contact in $contacts)
{
$IsNotExistentContact = $true
foreach($csvObject in $csvObjects)
{
if ($contact.Alias -eq $csvObject.SamAccountName)
{
$IsNotExistentContact = $false
break
}
}
if ($IsNotExistentContact -eq $true)
{
Remove-MailContact $contact.Alias -confirm:$false
}
}
}
function UpdateContact($UserObject)
{
$userAccountControl = New-Object -TypeName UserAccountControl -ArgumentList $UserObject.UserAccountControl
if ((Get-MailContact $UserObject.SamAccountName) -ne $Null)
{
if ($userAccountControl.AccountDisabled -eq “true”)
{
Remove-MailContact $UserObject.SamAccountName -confirm:$false
}
else
{
if ($UserObject.RecipientType -eq “UserMailbox”)
{
Set-MailContact $UserObject.SamAccountName -ExternalEmailAddress $UserObject.WindowsEmailAddress
-Name $UserObject.Name
Set-Contact $UserObject.SamAccountName -DisplayName $UserObject.DisplayName
-FirstName $UserObject.FirstName -LastName $UserObject.LastName
-City $UserObject.City -Company $UserObject.Company -Department $UserObject.Department
-Office $UserObject.Office -HomePhone $UserObject.HomePhone
-MobilePhone $UserObject.MobilePhone -Phone $UserObject.Phone -Fax $UserObject.Fax
-PostalCode $UserObject.PostalCode -Title $UserObject.Title
}
}
}
elseif (($userAccountControl.AccountDisabled -ne $true) -and ($UserObject.RecipientType -eq “UserMailbox”))
{
New-MailContact -Name $UserObject.Name -Alias $UserObject.SamAccountName
-ExternalEmailAddress $UserObject.WindowsEmailAddress -OrganizationalUnit agidel.ru/EmailContacts
Set-Contact $UserObject.SamAccountName -DisplayName $UserObject.DisplayName
-FirstName $UserObject.FirstName -LastName $UserObject.LastName
-City $UserObject.City -Company $UserObject.Company -Department $UserObject.Department
-Office $UserObject.Office -HomePhone $UserObject.HomePhone
-MobilePhone $UserObject.MobilePhone -Phone $UserObject.Phone -Fax $UserObject.Fax
-PostalCode $UserObject.PostalCode -Title $UserObject.Title
}
}
# #region Раздел выполнения скрипта
$csv = Import-Csv -Delimiter “;” -Path C:\users.csv
#Подключаемся к серверу
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://domain.com/PowerShell/ -Authentication Kerberos
Import-PSSession $Session
#Удаляем контакты, которых не существует в импортируемом файле
RemoveNonExistentContacts($csv)
#Обновляем данные
foreach($object in $csv)
{
UpdateContact -UserObject $object
}
#Закрываем соединение с сервером
Remove-PSSession $Session
# #endregion
Как скопировать атрибат пользователя proxyAddresses ??
Добрый день.
Подскажите пожалуйста, запустил скрип. Скрипт отработал, появилось 48 контактов вместо 400 и куча ошибок такого плана “Get-contact : Не удалось выполнить операцию, поскольку объект ‘Пупкин Василий Васильевич’ не найден в ‘домен контроллер где создаем контакты’.
C:\Scripts\new\gal.ps1:22 знак:12″. Что не так?
вот сам скрипт
#Адрес контроллера домена, к которому подключаемся”;
$Domain = ‘от куда’
Connect-QADService -Service $Domain
#Получаем список пользователей удаленного домена
$userlist = (Get-QADUser -name [А-Я]* -IncludedProperties DisplayName, title, company, department,
mailNickname, Office, PostalCode, l, streetAddress,
PhoneNumber, Pager, Mobile, facsimileTelephoneNumber,
Email, physicalDeliveryOfficeName, wWWHomePage,
AccountIsDisabled )
ForEach ($user in $userlist)
{
#Пользователь заблокирован?
if ($user.AccountIsDisabled -eq $true)
{
#У пользователя есть почта?
if ($user.mail -notlike $NULL)
{
#Пользователь заблокирован, удаляем существующий контакт
Remove-MailContact $user.mail –Confirm:$false
}
}
elseif ($(Get-contact $user.displayname))
{
if ($user.mail -notlike $NULL)
{
#Пользователь не заблокирован, контакт существует, надо обновить
Set-Contact $user.email -Company $user.Company -Title $user.Title -Department $user.Department
-PostalCode $user.PostalCode -StreetAddress $user.StreetAddress
-MobilePhone $user.mobile -Fax $user.facsimileTelephoneNumber
-Phone $user.telephoneNumber
}
}
else
{
if ($user.email -notlike $NULL)
{
#Пользователь не заблокирован, контакт не существует, надо создать
New-MailContact -Name $user.displayname -DisplayName $user.displayname -alias $user.mailnickname
-OrganizationalUnit куда/Contacts/ -ExternalEmailAddress $user.mail
}
}
}
Вопрос довольно срочный.
Заранее спасибо.
У вас не создались контакты, поэтому и ошибки. OU правильно задан?