К удобству пользования адресной книгой , надеюсь, все привыкли. В классическом случае туда попадают все контакты сотрудников компании. Проблемы начинаются, когда размер компании увеличивается. Но и в этом случае, если домены входят в один лес, и везде используется, к примеру, Exchange 2010, то проблем не будет. А вот в случае если у вас trust`овые отношения между лесами, то приходится выкручиваться сторонними решениями. К примеру, можно использовать продукт компании Microsoft FIM. Его функционал с легкостью перекроет данную задачу.
В своем решении я использовал бесплатные командлеты от компании Quest, которые в разы облегчают работу.
Конечно, уже есть готовые скрипты, написанные людьми, которые столкнулись с такой же проблемой. Но у них были недостатки:
- Нет возможности использовать входной файл с данными, скрипт приходится запускать много раз
- Некоторых пользователей не стоит добавлять по «политическим» соображениям, на основе вхождения в группу
- Не учитывалось, что пользователь может быть скрыт из адресной книги, аналогичное действие должно произойти и у нас
- Пользователь может быть удален, в таком случае у нас мы его скрываем
- Многие скрипты работают просто медленно , по причине того, что постоянно дергают LDAP запросы до удаленных площадок и дожидаются их ответа.
- Необходим был фильтр почтовых адресов, в моем случае .com и .ru
В данном решения я постарался обойти эти ограничения. Использую запросы только нужных полей, запросы пачкой данных, а потом выборку среди них. Что позволяет сократить время обработки каждой записи. На больших масштабах, от пару тысяч человек на каждый домен, эти изменения очень заметны. Я целенаправленно не удаляю контакты, оставляю это на откуп ручной работы. Контакты же просто скрываются. Эдакая защита от ошибок в обработке.
После завершения импорта, на email приходит оповещение о проделанной работе, куда включаются:
- Новые учетные записи
- Обновленные данные в свойствах контакта
- Скрытые из адресной книги учетки (на основе флажка HideExchangeAddress и удаления учетной записи)
Для импорта контактов я использовал входной файл с данными. Со структурой XML.
<!-- RemoteDomain - домен из которого импортируем данные --> <!-- RemotePathOU - Определенная OU из нужного нам домена --> <!-- OurPath - Куда в нашем домене будем сохранять контакты --> <!-- OurComentCompany - Как в коментариях будем называть компанию --> <!-- DontMemberOf - Группа, в которую включены пользователи, которых не надо импортить --> <Paths LocalDomain = "InAdmin.ru"> <Domain RemoteDomain="Sub.IA.local" RemotePathOU="Sub.IA.local" OurPath="InAdmin.ru/contacts/Import-1" OurComentCompany ="Import-1" DontMemberOf = "Not Import users"></Domain> <Domain RemoteDomain="sub-1.Sub.IA.local" RemotePathOU="sub-1.Sub.IA.local" OurPath="InAdmin.ru/contacts/Import-2" OurComentCompany ="Import-1" DontMemberOf = "Not-Import-users"></Domain> <Domain RemoteDomain="sub-1.Sub.IA.local" RemotePathOU="sub-1.Sub.IA.local" OurPath="InAdmin.ru/contacts/Import-3" OurComentCompany ="Import-1" DontMemberOf = "Not_Import_users"></Domain> <Domain RemoteDomain="SubDomain.domain.ru" RemotePathOU="SubDomain.domain.ru" OurPath="InAdmin.ru/contacts/Import-4" OurComentCompany ="Import-2" DontMemberOf = "Dont Import users"></Domain> <Domain RemoteDomain="Domain.contoso.msft" RemotePathOU="Domain.contoso.msft" OurPath="InAdmin.ru/contacts/Import-5" OurComentCompany ="Import-3" DontMemberOf = "Dont_Import_users"></Domain> </Paths> >
Сам скрипт:
cls #Где берем XML данные $path="C:\PS_Script\Import-EmailContacts.xml" #Почтовый сервер $IPMailServer="172.0.0.2" #От кого отправляем $SenderEmail="SyncEmail@InAdmin" #Кому отправляем $RecipientEmail="Backup_report@InAdmin.ru" #=== Get-PSSnapin -Registered *exchange* | Add-PSSnapin Add-PSSnapin quest.activeroles.ADmanagement #Запускаем запись лога $TranscriptLog = $path.Substring(0, $Path.Length -3) + "txt" start-transcript $TranscriptLog #Функция отправки почты function Send-mail ($subj = "VM" ,$body = "Text", $AttachPath = "0") { $SMTPClient = new-object System.Net.Mail.SMTPClient $Msg = new-object System.Net.Mail.MailMessage #Проверка, есть ли вложение if ($AttachPath -ne "0") { $Attach = new-object System.Net.Mail.Attachment($AttachPath) $Msg.Attachments.add($Attach) } $Msg.To.Add($RecipientEmail) $Msg.from=$SenderEmail $Msg.Subject = $subj $Msg.Body= $body $SMTPClient.Host=$IPMailServer $SMTPClient.Send($Msg) if ($AttachPath -ne "0") {$Attach.Dispose()} } #Импортируем данные, как XML Write-Output "### Импортируем данные из XML файла" $Domains = [xml](get-content $Path) #Добаляем и обновляем информацию в нашем домене из данных в удаленном Write-Output "### Добавляем/обновляем информацию в нашем домене" For ($i=0; $i -lt $domains.paths.Domain.Length; $i++) { Write-Output "`t### Начинаем обработку: $($($($domains.paths).Domain[$i]).RemoteDomain)" #Добавляем в конце слеш, что бы поиск работал нормально $TmpSeachRoot = $Domains.paths.Domain[$i].RemotePathOU +"/" #Проверяем существует ли группа для исключений $IsCreatedDontImport = (Get-QADObject -Service $domains.paths.Domain[$i].RemoteDomain ` -Identity $Domains.paths.Domain[$i].DontMemberOf -DontUseDefaultIncludedProperties ` -WarningAction Silentlycontinue -ErrorAction Silentlycontinue) -ne $null #В зависимости от того существуют ли группа контактов, #которые не нужно импортить собираем учетки по разному if ($IsCreatedDontImport -eq $true) { Write-Output "`t### Группа исключений импорта контактов существует" #Перменная с DN для группы,пользоватлей которой нужно исключать. $DontImportGroupDN = (Get-QADGroup -service $domains.paths.Domain[$i].RemoteDomain ` -Identity $Domains.paths.Domain[$i].DontMemberOf).DN #Получаем все учетки, для которых не стоит флаг Hide from Exchange Address List #и не состоят в группе, исключений для импорта. $RemoteDomainUsersArray = Get-QADUser -SizeLimit 10000 -SearchRoot $TmpSeachRoot ` -Service $domains.paths.Domain[$i].RemoteDomain -ObjectAttributes @{msExchHideFromAddressLists = ''} ` | Where-Object {!($_.MemberOf -eq $DontImportGroupDN) -and ($_.PrimarySMTPAddressPrefix -ne $null)` -and (($_.PrimarySMTPAddress -like "*.com") -or ($_.PrimarySMTPAddress -like "*.ru")) } } else { Write-Output "`t### Группы исключений импорта контактов НЕ существует" #Получаем все учетки, для которых не стоит флаг Hide from Exchange Address List $RemoteDomainUsersArray = Get-QADUser -SizeLimit 10000 -SearchRoot $TmpSeachRoot ` -Service $domains.paths.Domain[$i].RemoteDomain -ObjectAttributes @{msExchHideFromAddressLists = ''} ` | Where-Object {($_.PrimarySMTPAddressPrefix -ne $null)` -and (($_.PrimarySMTPAddress -like "*.com") -or ($_.PrimarySMTPAddress -like "*.ru"))} } #Получаем все наши контакты, что бы не запрашивать каждый раз Write-Output "`t### Получаем контакты, которые уже есть в нашем домене" $RemoteContactInOurOU = Get-QADObject -type contact -searchRoot $Domains.paths.Domain[$i].OurPath ` -Service $domains.paths.LocalDomain -SizeLimit 10000 #Определяем есть ли новые учетки $IsNewUsersExist = $false #Запускаем цикл для каждой учетки. В первом проходе мы создаем только mailcontact Write-Output "`t### Начинаем добавление новых контактов" Foreach ( $Contact in $RemoteDomainUsersArray) { #Подрезаем DisplayName если слишком длинный if ($Contact.DisplayName.Length -gt 60) {$DisplName = $Contact.DisplayName.substring(0,60)} else {$DisplName = $Contact.DisplayName} [string]$SMTPAddress = "SMTP:" + $Contact.PrimarySMTPAddress [string]$SMTPAlias = $Contact.PrimarySMTPAddressPrefix #Проверяем существует ли уже такой контакт у нас. Если такого нету, то создаем. Иначе - пропускаем if (!($Contact.PrimarySMTPAddress -eq (($RemoteContactInOurOU | Where-Object ` {$_.PrimarySMTPAddress -eq $Contact.PrimarySMTPAddress}).PrimarySMTPAddress))) { #Задаем, что новый пользователь есть $IsNewUsersExist = $true #Создаем новый контакт New-MailContact -ExternalEmailAddress $SMTPAddress -Name $DisplName ` -Alias $SMTPAlias -FirstName $Contact.FirstName -LastName $Contact.LastName -OrganizationalUnit ` $Domains.paths.Domain[$i].OurPath | Set-MailContact -EmailAddressPolicyEnabled $false -EmailAddresses $SMTPAddress #Сообщаем в консоль с кем мы сейчас работаем Write-Output "`t`t### Создан новый контакт: $($($($domains.paths).Domain[$i]).RemoteDomain) - $($Contact.DisplayName)" } } #Выводим сообщение о том, что не было добавлено новых данных if ($IsNewUsersExist -eq $false) { Write-Output "`t`t### Нет новых данных в: $($($($domains.paths).Domain[$i]).RemoteDomain)" } else { #Ждем синхронизации 20 сек Write-Output "`t### Ждем синхронизации 20 сек." Start-Sleep -Seconds 20 } #Получаем все наши контакты, что бы не запрашивать каждый раз Write-Output "`t### Запрашиваем обновленные данные о существующих контактах в нашем домене" #$RemoteContactInOurOU = Get-QADObject -type contact -searchRoot $Domains.paths.Domain[$i].OurPath ` # -Service $domains.paths.LocalDomain -SizeLimit 10000 $RemoteContactInOurOU = Get-Contact -OrganizationalUnit $Domains.paths.Domain[$i].OurPath -ResultSize unlimited #Определяем есть ли учетки для обновления $IsUsersChanges = $false #Запускаем цикл для каждой учетки. Второй проход. Добавляем информация в созданные контакты. Write-Output "`t### Начинаем обновление данных контактов" Foreach ( $Contact in $RemoteDomainUsersArray) { [string]$SMTPAddress = "SMTP:" + $Contact.PrimarySMTPAddress [string]$SMTPAlias = $Contact.PrimarySMTPAddressPrefix #Если существует, то прописываем данные. Иначе пропускаем контакт. #Проверка происходит на основе получения почт. адреса #Если он есть, то контакт существует if (!((($RemoteContactInOurOU | Where-Object ` {$_.WindowsEmailAddress -eq $Contact.PrimarySMTPAddress}).WindowsEmailAddress) -eq $null)) { #Получаем контакт из нашего домена, с которым сейчас идет работа $OurContact = ($RemoteContactInOurOU | Where-Object {$_.WindowsEmailAddress -eq $Contact.PrimarySMTPAddress}) #Задаем строковые переменные, что бы сравнивать [string]$TmpOurTitle= $OURContact.title [string]$TmpRemoteTitle= $Contact.title [string]$TmpOurDep= $OURContact.Department [string]$TmpRemoteDep= $Contact.Department [string]$TmpOurPhone= $OURContact.Phone [string]$TmpRemotePhone= $Contact.telephoneNumber [string]$TmpOurCompany= $OURContact.Company #Проверка, что произошли изменения If (($TmpOurTitle -ne $TmpRemoteTitle) -or ($TmpOurDep -ne $TmpRemoteDep) -or ($TmpOurPhone -ne $TmpRemotePhone)` -or ($TmpOurCompany -ne $Domains.paths.Domain[$i].OurComentCompany)) { #т.к. есть изменения то изменим переменную $IsUsersChanges = $true #Добавляем контакту свойства Set-Contact -Identity $Contact.PrimarySMTPAddress -Company $Domains.paths.Domain[$i].OurComentCompany ` -Title $Contact.Title -Department $Contact.Department ` -Phone $Contact.telephoneNumber -WarningAction SilentlyContinue #Сообщаем в консоль с кем мы сейчас работаем Write-Output "`t`t### Обновлен контакт: $($($($domains.paths).Domain[$i]).RemoteDomain) - $($Contact.DisplayName)" } } } #Проверяем были ли изменения, если нет, то выводим сообщение if ($IsUsersChanges -eq $false) { Write-Output "`t`t### Контакты уже обновлены в: $($($($domains.paths).Domain[$i]).RemoteDomain)" } Write-Output "`t### ===============================" #Переходим к скрытию/удалению учеток Write-Output "`t### Начинаем скрытие/удаление контактов" #Делаем запрос всех учеток, даже тех что скрыты в адр.книге. if ($IsCreatedDontImport -eq $true) { #Перменная с DN для группы,пользоватлей которой нужно исключать. $DontImportGroupDN = (Get-QADGroup -service $domains.paths.Domain[$i].RemoteDomain ` -Identity $Domains.paths.Domain[$i].DontMemberOf).DN #Получаем все учетки, для которых не стоит флаг Hide from Exchange Address List #и не состоят в группе, исключений для импорта. $RemoteDomainUsersArray = Get-QADUser -SizeLimit 10000 -SearchRoot $TmpSeachRoot ` -Service $domains.paths.Domain[$i].RemoteDomain -IncludedProperties msExchHideFromAddressLists ` | Where-Object {!($_.MemberOf -eq $DontImportGroupDN) -and ($_.PrimarySMTPAddressPrefix -ne $null)` -and (($_.PrimarySMTPAddress -like "*.com") -or ($_.PrimarySMTPAddress -like "*.ru")) } } else { #Получаем все учетки, для которых не стоит флаг Hide from Exchange Address List $RemoteDomainUsersArray = Get-QADUser -SizeLimit 10000 -SearchRoot $TmpSeachRoot ` -Service $domains.paths.Domain[$i].RemoteDomain -IncludedProperties msExchHideFromAddressLists ` | Where-Object {($_.PrimarySMTPAddressPrefix -ne $null)` -and (($_.PrimarySMTPAddress -like "*.com") -or ($_.PrimarySMTPAddress -like "*.ru"))} } #запрашиваем все учетки в нашем домене $RemoteContactInOurOU = Get-Contact -OrganizationalUnit $Domains.paths.Domain[$i].OurPath -ResultSize unlimited #Если изменения в скрытиях учеток $IsContactDisabled = $false #Проверка, что контакта из нашего домена уже нету в удаленном домене. Foreach ($OurContact in $RemoteContactInOurOU) { if ((($RemoteDomainUsersArray | Where-Object ` {$_.PrimarySMTPAddress -eq $OurContact.WindowsEmailAddress}).PrimarySMTPAddress) -eq $null) { #Нет учетки $IsContactDisabled = $true #Скрываем учетку из адресной книги #Временная переменная с email Из другого домена [string]$TmpRemoteEmail = $($OurContact.WindowsEmailAddress) Set-Mailcontact $TmpRemoteEmail -HiddenFromAddressListsEnabled $true -WarningAction SilentlyContinue Write-Output "`t`t### Уч.запись скрыта. Скрыт контакт: $($OurContact.DisplayName)" } else { #Есть уч.запись #Проверяем, отключена ли уже учетка $RemoteContact = ($RemoteContactInOurOU | Where-Object {$_.WindowsEmailAddress -eq $Contact.PrimarySMTPAddress}) if ( $RemoteContact.msExchHideFromAddressLists -eq $true ) { $IsContactDisabled =$true #Временная переменная с email Из другого домена [string]$TmpRemoteEmail = $($OurContact.WindowsEmailAddress) #Скрываем учетку из адресной книги Set-Mailcontact $TmpRemoteEmail -HiddenFromAddressListsEnabled $true -WarningAction SilentlyContinue Write-Output "`t`t### Уч.запись скрыта. Скрыт контакт: $($OurContact.DisplayName)" } } } #Проверяем были ли изменения, если нет, то выводим сообщение if ($IsContactDisabled -eq $false) { Write-Output "`t`t### Нет данных для скрытия: $($($($domains.paths).Domain[$i]).RemoteDomain)" } Write-Output "`t### Закончили обработку: $($($($domains.paths).Domain[$i]).RemoteDomain)" Write-Output "### ===============================" } #Останавливаем запись лога Stop-Transcript #Отправляем почту, используя функцию Send-mail -subj "Sync Email Contacts" -body "Synchronization Log" -AttachPath $TranscriptLog #Удаляем файл лога Remove-Item $TranscriptLog -Force


The web is getting more crowded all the time. If you’d rather not get lost in the crowd, we can help you be a whole lot more visible. The system is easy: Search Engine Optimization is the key. Help us help you get more business.