Работая в течении полугода с Microsoft Exchange Server 2016 в компании, где более 500 сотрудников использует корпоративную почту, я столкнулся с проблемой полноценного удаления информации о пользователях, отключенных в Active Directory.
Задачи, которые мы хотим автоматизировать, после отключения учетки пользователя в AD:
- Экспорт всех писем из основого и архивного ящика в .pst файл;
- Полная блокировка почтового ящика после экспорта писем;
- Очистка всех списков рассылки от «мертвых пользователей» (автоматически не очищается);
- Обновление Global Address List и Offline Address Book, чтобы активные пользователи не видели отключенных.
Испытывая полнейшую нелюбовь к ручной работе, было принято решение максимально автоматизировать все эти задачи с помощью PowerShell.
Подготовка:
Подключаем библиотеку Exchange Management PowerShell:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn;
Получаем список всех отключенных в Active Directory пользователей и исключаем некоторые служебные записи:
$DisableUsers = get-user -Filter {(UserAccountControl -eq 'AccountDisabled, NormalAccount') -and (RecipientType -eq 'UserMailbox')} | ? {($_.SamAccountName -ne 'krbtgt') -and ($_.SamAccountName -ne 'SM_2013a5b0c2bd4ca2a') -and ($_.SamAccountName -ne 'testvc')}
Объявляем переменные:
# Объявляем переменную для объединения нескольких запросов на экспорт.
$BatchName = 'MassRequest'
# Создаем пути для экспорта
$CMounth = (Get-Date).month
$CYear = (Get-Date).year
$CurrentDate = "$CYear.$CMounth" # Получаем будущее имя папки вида Год.Месяц
$MainDir = "\\%Ваш путь%"
$ExportPath = $MainDir + $CurrentDate + "\"
Обработка:
Чтобы было удобнее найти .pst архив уволенного пользователя, было принято решение создавать папку вида Год.Месяц. Так, все уволенные пользователи в апреле 2017 года попадут в папку 2017.4, уволенные в мае в папку 2017.5 и тд.
# Проверяем, есть ли уже папка Год.Месяц, если нет, то создаем.
if ((Test-Path $ExportPath -PathType Container) -eq $false){
New-Item -Path $MainDir -Name $CurrentDate -ItemType "directory"
}
В цикле по отключенным пользователям выгружаем их почту в .pst файл из основного и архивного почтовых ящиков и сохраняем в папку Год.Месяц.
С помощью параметра -BatchName объединяем запросы под одним именем, для возможности отслеживать статус всей выгрузки сразу, а не каждый запрос отдельно.
foreach($User in $DisableUsers){
$PrimaryPath = $ExportPath + $User.SamAccountName + ".pst"
$ArhivePath = $ExportPath + $User.SamAccountName + "_Archive.pst"
New-MailboxExportRequest -Mailbox $User.SamAccountName -BatchName $BatchName -FilePath $PrimaryPath
New-MailboxExportRequest -Mailbox $User.SamAccountName -BatchName $BatchName -FilePath $ArhivePath -IsArchive
}
Ждем, пока скрипт закончит работу. Ждать нужно обязательно, т.к. дальше мы переводим ящики в статус Disable и хотим быть уверены, что перед этим выгрузка почты закончилась.
# Ждем, пока скрипт закончит работу
$i=1;
while ((Get-MailboxExportRequest -BatchName $BatchName | Where {($_.Status -eq “Queued”) -or ($_.Status -eq “InProgress”)})) {
sleep 60
Write-Host "Скрипт работает $i минут. Ожидаем завершения.."
$i=$i+1
}
После завершения экспорта удаляем все запросы, которые получили статус Completed
# После завершения экспорта удаляем все запросы
Get-MailboxExportRequest -Status Completed | Remove-MailboxExportRequest -Confirm:$false
Первую часть сделали, начинаем чистить списки рассылки. Для начала получаем массив всех списков:
# Начинаем чистить списки рассылок. Сначала получаем их полный список.
$DistribList = Get-DistributionGroup
В цикле пробегаемся по всем спискам рассылки и удаляем отключенных пользователей:
# В цикле удаляем отключенных пользователей из всех списков
foreach($List in $DistribList){
foreach($User in $DisableUsers){
Remove-DistributionGroupMember -Identity $List -Member $User -Confirm:$false -ErrorAction Ignore
}
}
Предпоследний этап: отключение почтовых ящиков. Из учетки пользователя в AD пропадает E-mail, а сам почтовый ящик удаляется. Теперь его только в течении некоторого времени можно восстановить стандартными средствами Exchange.
# Начинаем отключать почтовые ящики, после чего они пропадут из адресной книги
foreach($User in $DisableUsers){
Disable-Mailbox -Identity $User.SamAccountName -Archive -Confirm:$false
Disable-Mailbox -Identity $User.SamAccountName -Confirm:$false
}
Обновляем GAL и OAB, чтобы пользователи увидели изменения как можно быстрее.
# Обновляем Global Adress List, чтобы клиенты увидели изменения в адресной книге
Get-GlobalAddressList | Update-GlobalAddressList
Get-OfflineAddressBook | Update-OfflineAddressBook
Get-AddressList | Update-AddressList
Небольшой комментарий:
В своей фирме мы прицепили эту обработку к кастомной кнопке в 1С. Отдел кадров в профиле сотрудника выставляет ему статус «Уволен» и скрипт начинает работать.
Тем самым отключенных пользователей в адресной книге практически невозможно увидеть, а уволенный сотрудник сразу теряет доступ к почте. (Если только выключить учетку в Active Directory, то зайти в почту сотрудник все равно может, что по нашей корпоративной политике недопустимо).
Надеюсь кому-то скрипт будет полезен. Спасибо!
Комментарии (13)
Openmsk
18.05.2017 20:38Отличная тема! Спасибо!
Может быть всю вашу задумку я не буду использовать, но частично обязательно реализую!
brainfair
19.05.2017 04:13Можно так же использовать отрубание функционала для пользователя:
set-casmailbox -owaenabled $false -activesyncenabled $false -popenabled $false -imapenabled $false -mapienabled $false -owafordevicesenabled $false
Так же если используете линк, то обязательно делать disable юзеру, потому что даже если его заблочить, сменить пароль, еще с неделю он сможет использовать линк.
get-csuser %username% | disable-csuser
ildarz
19.05.2017 18:21+1Процедурных вопросов касаться не буду, хотя там есть странности (например, зачем вообще оставлять окно между откючением учетки и удалением ящика, если уж вы так боитесь, что злобный вредитель зайдет в свою почту).
Чисто по самому скрипту:
Отдел кадров в профиле сотрудника выставляет ему статус «Уволен» и скрипт начинает работать.
… работать, и еще раз работать, перелопачивая ВСЕ (!) отключенные учетки вместо того, чтобы работать только с теми, для которых выставлена галка.
Проверяем, есть ли уже папка Год.Месяц, если нет, то создаем.
Зачем вы вообще делаете эту проверку, если в дальнейшем всё равно нет никакой обработки ошибок? А вот не создался у вас путь по какой-то причине, что будет? Если вы только создаете файл — это ещё не так страшно. А если бы удаляли, да по маске?
Ждем, пока скрипт закончит работу. Ждать нужно обязательно, т.к. дальше мы переводим ящики в статус Disable и хотим быть уверены, что перед этим выгрузка почты закончилась.
А как вы можете быть в этом уверены, если вы даже статус задачи не проверяете по её окончании? А если там не Completed, а Failed?
Get-MailboxExportRequest -Status Completed | Remove-MailboxExportRequest -Confirm:$false
Снова тот же косяк, что в самом начале — не ограничивается область действия, удаляются ВСЕ запросы с подобным статусом, а не только созданные самим скриптом на предыдущих этапах.
В своей фирме мы прицепили эту обработку к кастомной кнопке в 1С.
А вы как-то учитывали, что у скрипта время исполнения, мягко говоря, ненулевое? Продумывали, что будет, если несколько инстансов скрипта окажутся запущены параллельно?
В общем, тут принцип примерно тот же, что и бэкапами — либо на чужом опыте учитесь обрабатывать ошибки и ограничивать область действия скриптов, либо в один прекрасный момент на горьком своём. :) Хотя, конечно, есть везучие люди, которым и бэкапы ни разу в жизни не понадобились, и все команды в скриптах всегда отрабатывают штатно...
whiplash
23.05.2017 17:12Молодцы, расширили мою идею)
Не могу не добавить, что до 24 часов у сотрудника скорее всего будет доступ в почту через OWA/ActiveSync.
Чтобы этого избежать — нужно делать reload APP пулов в IIS CAS Exchange.vesper-bot
23.05.2017 17:34То есть вот откуда эта засада по доступности OWA после блокирования учетной записи… Надо учесть, потому что такая реализация на IIS это бред редкостный и дыра в безопасности. Но помимо сброса всех IIS'ов обойти эту дыру можно, оторвав OWA и ActiveSync:
set-mailbox $user -activesyncenabled:$false -owaenabled:$false
Источник. Если посмотрите, в ветке выше эта проблема была заявлена.
HomeDimoN
25.05.2017 12:41+1Может пригодиться:
Хранение настроек скрипта в файлеХранение настроек скрипта в файле, после можно подписать файл скрипта, что бы избежать несанкционированного изменения скрипта…
#region Get setting from config $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path #$myDir = "$pwd\" If (!(Test-Path $MyDir\Settings.xml)) {write-host 'Setting file ' $MyDir\Settings.xml ' not found..' -ForegroundColor Red ; exit} [xml]$ConfigFile = Get-Content "$MyDir\Settings.xml" if ($ConfigFile -eq $null) {write-host 'Setting file ' $MyDir\Settings.xml ' has error...' -ForegroundColor Red ; exit} $global:Path2Export = $ConfigFile.Settings.Path2Export $global:SecondsToSleep = $ConfigFile.Settings.SecondsToSleep $global:ExportGroupName = $ConfigFile.Settings.ExportGroupName $global:ExchangeServer = $ConfigFile.Settings.ExchangeServer #endregion
Пример XML
<?xml version="1.0"?> <Settings> <Path2Export>\\FileServer.contoso.com\BackupPst$\</Path2Export> <ExportGroupName>ExportDisableUser2PST</ExportGroupName> <SecondsToSleep>30</SecondsToSleep> <ExchangeServer>ExchangeServer.contoso.com</ExchangeServer> </Settings>
vesper-bot
> Если только выключить учетку в Active Directory, то зайти в почту сотрудник все равно может, что по нашей корпоративной политике недопустимо
Вот этот кусок сильно смущает. Как сотрудник может зайти в почту, если его учетка disabled? Или проблема в репликации AD?
kotorr
Авторизацию в почту идет по логину и паролю из домена. Если мы заблокировали учетку пользователя в AD, то он не может авторизоваться на рабочей станции и запустить Outlook.
Но если с другого утройства он зайдет на веб-морду почтовика, то под своими, уже заблокированными учетными данными, он все равно сможет в почту войти.
Вроде как отправлять почту он не сможет, но всю переписку может прибить или скопировать что-то важное.
С архивной почтой это тоже работает.
vesper-bot
Вот реально не верю. Чтобы проходила авторизация на веб-морде (owa) под заблокированными учетными данными, либо должна быть ситуация, при которой owa при проверке получает ответ «не заблокировано», либо есть баг в Exchange, от которого надо избавляться любой ценой (то есть запросить Микрософт и выяснить, почему это у вас такое позволительно). Одно дело если это кэшированная сессия подключенного мобильного устройства (ActiveSync) — тогда надо после блокирования пользователя делать
(можно даже сделать clear-mobiledevice, если политикой требуется), в этом случае все сессии activesync будут разорваны, и устройство, попытавшись получить доступ под сохраненными учетными данными, получит ошибку account disabled и не сможет ничего получить, тем более удалить из ящика. Но если реально после репликации AD заход с логином/паролем на owa даст пользователю доступ — это глюк.gotch
Просто CAS кеширует учетные данные, есть несколько статей на эту тему вроде https://support.microsoft.com/en-us/help/267568/an-old-password-still-works-after-you-change-it-in-outlook-on-the-web. Кто-то, кто заморачивается, отключает таким учеткам OWA.
Но там кэш всего на 15 минут, вроде это ни для кого не было критичным. Если время менять, то OWA может начать притормаживать.
vesper-bot
Интересно, и в принципе логично, однако это работает только если пользователь сразу перед тем, как его заблокировали, авторизовался на OWA. Тогда он, узнав, что его заблочили, может с веб-морды что-то удалить. А обойти такой косяк тоже можно, если запихать CAS-сервера в отдельный сайт, рядом к ним поставить DC/RODC и его посадить в тот же сайт. После чего уменьшить кэширование токенов на IIS на скажем три минуты (это может привести к тому, что пользователи начнут получать диалог «войдите заново», тогда лучше так все-таки не делать — но по описанию токен кэшируется, т.е. IIS будет воссоздавать токен без запроса логина/пароля, так как сессия авторизации пользователя на ПО не истекла, и хотя производительность и просядет слегка, пользователи не будут получать диалог повторной авторизации, но если их заблочили, то будут блокироваться быстрее). А если паранойя, то правильно будет именно оторвать доступ к owa при отключении пользователя, причем ещё до того, как экспортировать его почтовый ящик. Экспорт может занять довольно много времени, если ящик большой, а канал внезапно узкий.
gotch
Сложно всегда побороться с штатной работой продукта.
А побыстрее отбивать, думаю получится через Disable-Mailbox, Update-StoreMailboxState, Connect-Mailbox к временной учетке и New-MailboxExportRequest.
kotorr
Я не хочу разводить тут спор, но мы проводили тесты в продакшене. Доступ у заблокированной учетки остается до 24 часов и кэш тут не причем. Exchange получает инфу о отключении учетки только во время maintenance mode, что может длиться довольно долго.
Поэтому мы предпочли ручками отключать эту mailbox.