К удобству пользования адресной книгой , надеюсь, все привыкли. В классическом случае туда попадают все контакты сотрудников компании. Проблемы начинаются, когда размер компании увеличивается. Но и в этом случае, если домены входят в один лес, и везде используется, к примеру, 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
, , ,


Только 1 комментарий

  1. Ivan Ballard @ 2011-12-19 11:04

    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.

Добавить новый комментарий