Powershell: создадим группы рассылки для Exchange по телефонным номерам
Я думаю, Вы не однократно сталкивались с необходимостью сообщить Вашему контрагенту по телефону Ваш адрес электронной почты. Диктовать вместе “betke” последовательность “Борис-Елена-Тимофей-Константин-Елена” меня уже утомило.
Необходимо найти решение и для людей, не проходивших обучения на военной кафедре Натыкаюсь последнее время на любопытное решение: некоторые контрагенты в качестве lname используют свой внутренний телефонный номер. И в этом есть смысл – сообщить по телефону 3 цифры куда проще, чем кучу латинских букв.
Приступаем к реализации данного подхода на Exchange Server.
Рассмотрим суть предлагаемого решения на моём примере. Мой адрес – sergey.s.betke@novgaro.ru. Мой внутренний телефонный номер – 555. Поэтому по телефону имею желание сообщать адрес 555@novgaro.ru. Можно банально дописать адрес на страницу E-Mail Addresses в ADUC. А что делать, если номер используется несколькими сотрудниками? Чтобы избежать конфликта, решил использовать для данной цели группы распространения. И в моём случае – локальные.
Итак, приступим. P.S. Текущая версия сценария доступна в моём SVN репозитории, доступ на чтение — svn_sysadm, пароль — svn.
Выбираем атрибуты
Планирую использовать для этих целей поле Номер телефона (так его нам ADUC представляет). Запускаем оснастку ADSI и видим, что на самом деле интересующий нас атрибут именуется следующим образом: telephoneNumber
.
Атрибут, который планируем задействовать под внутренний номер, выбрали. Теперь планирую следующие шаги:
- сделаю выгрузку из AD в .csv с тем, чтобы облегчить экспорт в excel и дальнейшее согласование / утверждение телефонной книги;
- реализую загрузку сведений из .csv с соответствующей корректировкой атрибута
telephoneNumber
и формированием журнала изменений; - и создам сценарий powershell, который создаст локальные группы распространения (distribution group), членство в которых будет определяться как раз значением атрибута
telephoneNumber
.
Приступаем к первому этапу.
Выгружаем телефонную книгу в .csv
Реализуем выгрузку по примеру статей “Собственно выгрузка контактных координат сотрудников из AD” и “Корректируем инициалы всех сотрудников в AD средствами powershell”. Если Вы ранее не использовали командлеты ADpowershell, рекомендую предварительно прочитать статью “Установка и первый опыт ADpowershell на Windows Server 2003”.
Выделил сценарий в отдельную статью, чтобы не загромождать эту статью.
Итак, телефонную книгу в .csv мы выгрузили для проверки и корректировки.
Загрузим в AD телефонную книгу из .csv
А теперь реализуем обратную операцию. Может пригодиться, когда Вам присылают откорректированный документ, полученный первоначально в предыдущем параграфе. Сценарий по импорту телефонной книги в статье “PowerShell: загружаем (импортируем) телефонную книгу в AD из .csv”.
Создаём группы распространения
Безусловно, сегодня можно обойтись для этой задачи и без статических групп с помощью Dynamic Distribution Groups, но Exchange 2003 такое удовольствие не поддерживает, поэтому всё-таки пишем скрипт для создания групп.
С созданием групп и OU проблем не возникло. Возникли некоторые сложности с автоматизацией удаления “лишних” членов в этих абонентских группах и добавления только тех, что ещё не являются членами. Кроме того, иной проверки наличия OU и групп, кроме как test-Path, не нашёл. Если Вы сможете предложить в комментарии иные варианты – буду крайне благодарен.
Более серьёзные проблемы возникли с тем, чтобы сделать созданные группы mail-enabled. Для этих целей Exchange 2010 предоставляет замечательную командлету enable-DistributionGroup, но Exchange 2003 её не предоставляет. С помощью enable-DistributionGroup код был бы следующим:
enable-DistributionGroup -Identity $phoneDG -Alias $phone;
Но мы будем вынуждены придумывать некий “костыль” для Exchange 2003. Ранее, при доступе через ADSI можно было воспользоваться методом MailEnable. Приведу фрагмент кода JScript, который эксплуатируется по сей день:
{ // создание группы рассылки { // корректировка реквизита displayName для отображения в адресной книге как ГР Персонал... var displayName = "ГР " + group.sAMAccountName; if ( changeProperty (group, "displayName", displayName) ) group.SetInfo(); }; var DLAddr = "all." + mailboxName; group.mailNickname = DLAddr; group.SetInfo(); if (!group.mail) { group.MailEnable(); WScript.StdOut.Write("!!!создаём группу рас. " + sn + " : " + DLAddr + "\n"); group.SetInfo(); }; };
Посему буду пробовать писать свой обрезанный enable-DistributionGroup, используя ADSI+CDOEXM. Решение выделил в отдельный модуль и отдельную статью. Возникла проблема с авторизацией, которой также посвятил отдельную статью, рекомендации статьи необходимо выполнить до попытки запуска кода.
Опубликую код, весь функционал уже проверен:
<# .Synopsis Создаём группы рассылки по телефонам (telephoneNumber) и включаем в них только те учётные записи, для которых в атрибуте telephoneNumber указан соответствующих телефонный номер. .Description Создаём группы рассылки по телефонам (telephoneNumber) и включаем в них только те учётные записи, для которых в атрибуте telephoneNumber указан соответствующих телефонный номер. .Parameter searchBase OU, в которой будет осуществляться поиск учётных записей для групп рассылок .Parameter filter Фильтр для отбора учётных записей для групп рассылок .Parameter DGOU OU, в которой будут созданы группы рассылки .Parameter rePhone Регулярное выражение, описывающее телефонный номер (для проверки) .Example Создаём / обновляем группы рассылки .\PhonesToDistGroups.ps1 #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Low' )] param ( [Parameter( Mandatory=$false, Position=0, ValueFromPipeline=$false, HelpMessage="OU, с которой будет осуществляться выгрузка телефонной книги в .csv" )] [string] $searchBase = 'OU=Группа Компаний ГАРО,OU=Персонал,OU=Предприятия,OU=iTg,DC=novgaro,DC=ru', [Parameter( Mandatory=$false, ValueFromPipeline=$false, HelpMessage="Фильтр для отбора учётных записей для телефонной книги." )] $filter = {name -notlike 'ПЯ*'}, [Parameter( Mandatory=$false, Position=1, ValueFromPipeline=$false, HelpMessage="OU, в которой будут созданы группы рассылки." )] [string] $DGOU = 'OU=По телефонным номерам,OU=Контакты,OU=iTg,DC=novgaro,DC=ru', [Parameter( Mandatory=$false, ValueFromPipeline=$false, HelpMessage="Регулярное выражение, описывающее телефонный номер (для проверки)." )] [string] $rePhone = '\d{3}' ) import-module ActiveDirectory import-module .\ITG.Exchange\ITG.Exchange.psm1 -force Write-Verbose @" Создаём группы рассылки по телефонам (telephoneNumber) и включаем в них только те учётные записи, для которых в атрибуте telephoneNumber указан соответствующий телефонный номер. OU для выбора учётных записей, для которых будут созданы группы рассылок: $searchBase Фильтр для отбора учётных записей: $filter OU, в которой будут созданы группы рассылки: $DGOU "@ $phones = @(Get-ADUser ` -Filter $filter ` -SearchBase $searchBase ` -SearchScope Subtree ` -Properties ` cn, displayName, telephoneNumber ` | group-object ` -property telephoneNumber ` | sort-object ` -property name ` | where-object { $_.name -match $rePhone } ` ) Write-Verbose @" Обнаружено $($phones.count) телефонных номеров. "@ $phones | % ` -begin { [int]$phoneIndex = 0; write-progress ` -id 1 ` -activity 'Коррректируем / создаём группы рассылок по телефонным номера' ` -status 'Приступаем...' } ` -process { [string] $phone = $_.name; [Microsoft.ActiveDirectory.Management.ADOrganizationalUnit] $phoneOU = $null; if (test-path "AD:\OU=$phone,$DGOU") { $phoneOU = Get-ADOrganizationalUnit "OU=$phone,$DGOU"; } else { if ($pscmdlet.ShouldProcess("AD:\OU=$phone,$DGOU", 'создание')) { $phoneOU = New-ADOrganizationalUnit ` -Name $phone ` -Path $DGOU ` -ProtectedFromAccidentalDeletion $false; } else { return; # обработка этого телефона далее невозможна }; }; [Microsoft.ActiveDirectory.Management.ADGroup] $phoneDG = $null; if (test-path "AD:\CN=Подписка,OU=$phone,$DGOU") { $phoneDG = Get-ADGroup ` -Identity "CN=Подписка,OU=$phone,$DGOU" ` -property mailNickname,msExchHideFromAddressLists,reportToOriginator; } else { if ($pscmdlet.ShouldProcess("AD:\CN=Подписка,OU=$phone,$DGOU", 'создание')) { $phoneDG = New-ADGroup ` -Name 'Подписка' ` -SamAccountName "ГРТН $phone" ` -GroupScope Global ` -GroupCategory Distribution ` -Path "OU=$phone,$DGOU" ` -DisplayName "ГРТН $phone" ` -PassThru; } else { return; # обработка этого телефонного номера далее невозможна }; }; if ( ` ($phoneDG.mailNickname -ne $phone) -or ` (!$phoneDG.msExchHideFromAddressLists) -or ` (!$phoneDG.reportToOriginator) ` ) { Set-ADGroup ` -Identity $phoneDG ` -Description "Автоматически сформированная группа рассылки по абонентам телефонного номера $phone." ` -DisplayName "ГРТН $phone" ` -Replace @{ mailNickname = $phone; msExchHideFromAddressLists = $true; reportToOriginator = $true; }; }; $currentSubscribers = @(Get-ADGroupMember -Identity $phoneDG | ? {$_.objectClass -eq 'user'}); $currentSubscribersGUIDs = @($currentSubscribers | % { $_.objectGUID }); $newSubscribers = @($_.group | ? {$currentSubscribersGUIDs -notcontains $_.objectGUID}); $newSubscribersGUIDs = @($_.group | % { $_.objectGUID }); $obsoleteSubscribers = @($currentSubscribers | ? {$newSubscribersGUIDs -notcontains $_.objectGUID}); if ($newSubscribers.count) { if ($pscmdlet.ShouldProcess($phoneDG, @" Добавление в группу рассылки для телефона $phone следующих учётных записей: $($newSubscribers | % { "`t$($_.displayName)`n"}) "@ )) { Add-ADGroupMember ` -Identity $phoneDG ` -Members $newSubscribers; }; }; if ($obsoleteSubscribers.count) { if ($pscmdlet.ShouldProcess($phoneDG, @" Удаление из группы рассылки для телефона $phone следующих учётных записей: $($obsoleteSubscribers | % { "`t$($_.displayName)`n"}) "@ )) { Remove-ADGroupMember ` -Identity $phoneDG ` -Members $obsoleteSubscribers; }; }; $phoneIndex++; write-progress ` -id 1 ` -activity 'Коррректируем / создаём группы рассылок по телефонным номера' ` -status 'Создаём / корректируем группы распространения' ` -currentOperation "$phone ($($phoneIndex) из $($phones.count))" ` -percentcomplete ($phoneIndex/$phones.count*100); $phoneDG; } ` -end { write-progress ` -id 1 ` -activity 'Переносим файлы в древовидную структуру папок' ` -status 'Завершили...' ` -completed } ` | Enable-DistributionGroup Write-Verbose @" Создание групп рассылки по телефонам завершено. "@
И этот код успешно работает. server-12 – это exchange server 2003 хост.
P.S. Этот код будет успешно работать и для Exchange Server 2007, но предварительно следует убрать наше импорт нашего собственного модуля .\ITG.Exchange\ITG.Exchange.psm1. Кстати, модуль описан здесь.
P.P.S. Вызов Enable-DistributionGroup в конвейере в моём случае обладает существенно более высокой производительностью по одной причине: все группы обрабатываются одной удалённой сессией. Если просто вызывать как функцию для каждой группы отдельно – потребуется дополнительное время на открытие и закрытие удалённое сессии для каждой группы.
Сценарий как командлета powershell
Как Вы уже, надеюсь, заметили, данный сценарий максимально приближен по функциональности к командлете. Он поддерживает параметры –whatIf
, -verbose
, -confirm
. Должен сказать – самому понравилось, очень удобно при работе с AD (когда нужно проверить, что же сценарий сделает перед тем, как он это сделает). Необходимый для такой поддержки код выделен.
Полезности
- Делаем командлет на powershell правильно: oszone.ru, technet.com, снова technet.com
- О том же официально: about_Functions_Advanced, about_Functions_Advanced_Methods, about_Functions_Advanced_Parameters, about_Functions_CmdletBindingAttribute.
RSS комментарии
Обратная ссылка