Миграция Active Directory – относительно редкая задача, которая чаще всего возникает в связи с реструктуризацией бизнеса.
В статье будет рассмотрена Cross-forest миграция, как более комплексная задача, разобравшись с которой миграция между доменами одного леса (более распространенный сценарий) не вызовет затруднений.
Переносить будем основные объекты Active Directory – Пользователей, Группы и Компьютеры, кроме того, рассмотрим миграцию почтовых ящиков Exchange.
В своей статье я сделаю акцент на выполнении массовых операций.
Основное условие – со стороны пользователя миграция должна пройти максимально предсказуемо и с минимальными потерями рабочего времени.
Вернувшись за рабочее место пользователь сможет ввести свой существующий логин и пароль и использовать существующей профиль со всеми данными и индивидуальными настройками.
Доступ ко всем корпоративным ресурсам (ERP, Fileshare, Email, Printers) останется без изменений вне зависимости от того «переехали» эти ресурсы в новый лес или остаются в старом.
Единственное что будет изменено – это имя домена, но можно предложить использование в качестве логина Email адреса в новом лесу – это достаточно удобно с точки зрения пользователя который привык вводить личный Email в качестве логина в социальных сетях и т.п.
Лабораторная среда
Для написания этой статьи я развернул такую лабораторную среду:
Server | OS | Software / Roles | IP /28 | RAM |
EDGE | WS 2008R2 SP1 | Exchange 2010 SP3 | 192.168.0.2 | 1 |
DC1 | WS 2008R2 SP1 | AD DS, DNS, AD CS | 192.168.1.2 | 1 |
MAIL1 | WS 2008R2 SP1 | Exchange 2010 SP3 | 192.168.1.3 | 4 |
FS | WS 2008R2 SP1 | Fileserver | 192.168.1.5 | 1 |
WS1 | Windows 7 SP1 | Office 2010 SP2 | 192.168.1.6 | 1 |
WS2 | Windows 7 SP1 | Office 2010 SP2 | 192.168.1.7 | 1 |
WS3 | Windows 8.1 | Office 2013 SP1 | 192.168.1.8 | 1 |
DC2 | WS 2012R2 | AD DS, DNS | 192.168.2.2 | 1 |
MAIL2 | WS 2012R2 | Exchange 2013 SP1 | 192.168.2.3 | 12 |
SRV1 | WS 2012R2 | ADMT 3.2 | 192.168.2.4 | 1 |
SRV2 | WS 2012R2 | SQL 2014 | 192.168.2.5 | 1 |
SRV3 | WS 2008R2 SP1 | PES 3.2 | 192.168.1.4 | 1 |
Как видите, подобную инфраструктуру можно развернуть практически на любом хосте виртуализации с достаточным количеством оперативной памяти и дискового пространства (у меня было занято менее 512Gb).
Я привожу в примере 2008R2 и 2012R2, но материал может быть использован и для других версий Windows Server.
Для эмуляции «содержимого» которое будет переноситься я использовал:
User | Email /UPN | Security Group | Distribution Group | PC |
user1 | [email protected] | sg1 | [email protected] | WS1 |
user2 | [email protected] | sg1 | [email protected] | WS2 |
user3 | [email protected] | sg2 | [email protected] | WS3 |
Перед началом миграции у вас должна корректно функционировать сеть и среда виртуализации в которой будет развернут новый лес Active Directory.
Настройка доверительных отношений между лесами
В зависимости от целей и условий миграции двухсторонние доверительные отношения между Source и Target лесами могут быть недопустимы, но в моем случае я считаю их уместными (подробнее о том на каком этапе миграции кто кому должен доверять описано в ADMT Guide).
Для корректного разрешения имен между лесами существует несколько способов, например я буду использовать Conditional Forwarders:
Не важно какой способ выберете вы, но в результате имена должны разрешаться корректно:
Когда это условие выполнено, на Source DC откроем mmc Active Directory Domains and Trusts, запустим мастер в котором создадим и подтвердим такую конфигурацию:
Я не описываю процесс нажимания Next => Next => OK, если у вас это вызывает затруднения – и Сети достаточное количество материалов по пошаговой настройке доверительных отношений.
После завершения работы Мастера убедимся, что в Target DC также был создан траст:
DNS Suffix
Предполагая, что пользователи будут (временно или постоянно) использовать ресурсы обеих лесов и зная что использование srvshare вызывает меньше проблем чем srv.dom2.locshare создадим и применим такие групповые политики в Source и Target лесах соответственно:
В результате применения политики суффиксы будут выглядеть так (для Source domain):
Проверить результат достаточно просто – я запрошу резолв привычного пользователям ресурса который находится в Source из Target леса:
PKI Trust
Предположим, что в новом лесу развернут новая PKI, а в PKI из старого леса вносить изменения не будем.
Пользователи доверяют сертификатам того ЦС, с которым находятся в одном лесу, но не ЦС доверенного леса, в котором находится часть ресурсов, которые они используют.
Для решения этой ситуации создадим и применим такие групповые политики в Source и Target лесах соответственно:
В результате, ресурсы доверенных лесов будут выглядеть корректно (разумеется, точки распространения CRL должны быть доступны):
Builtin объекты
Builtin объекты везде имеют одинаковые SIDы, поэтому при миграции с помощью ADMT мигрированы они не будут.
Нужно проверить членство в этих группах, и членство этих объектов в других группах (вообще, рекомендую Builtin использовать как можно меньше).
Учетная запись для ADMT
ADMT будет установлена в Target лесу, соответственно учетную запись для нее создадим тоже там.
В ADMT Guide есть детальное описание необходимых прав для этой учетной записи на разных этапах миграции, но я не буду кривляться и включу ее в группу Domain admins в Target forest и в группу Administrators в Source Domain:
При миграции объектов типа Computer у учетной записи ADMT должны быть права локального администратора на этих объектах. Для этого будем использовать групповую политику, которой добавим группу DOM2Domain admins (в которую входит учетная запись dom2ADMT) в группу Локальных администраторов на компьютерах Source леса:
Проверим результат на рабочей станции:
Правила Брандмауэра
Для работы агента ADMT создадим исключения в брандмауэре с помощью групповой политики в обеих лесах (я разрешил доступ только с сервера на котором будет работать ADMT):
Active Directory Audit, SID Filter & SID history
Миграция атрибута SID history является ключевым моментом, ведь именно благодаря SID history пользователь находясь в Target лесу сможет получать доступ к ресурсам которые остались в Source лесу.
Обратите внимание, у пользователя может быть несколько SID в SID history.
Первым делом создадим и применим политики аудита в Source и Target лесах:
Затем выключим фильтрацию SID в обеих доменах:
netdom trust TrustingDomainName /domain: TrustedDomainName /quarantine:no
… и включим SID history также в обеих доменах:
netdom trust TrustingDomainName /domain: TrustedDomainName /enableSIDhistory:yes
Вот как это выглядит на dc1.dom1.loc и dc2.dom2.loc соответственно:
Обратите внимание: у учетной записи ADMT должны быть права «Migrate SID History» в Source domain. Т.к. я включил учетную запись в группу Administrators эти права есть, но если назначать права вручную, то делать это удобно с помощью Delegation Control Wizard:
Если на Компьютерах которые планируется мигрировать есть ОС Windows Vista и ниже, необходимо ключу HKLMSystemCurrentControlSetServicesNetlogonParametersAllowNT4Crypto задать значение 1 в Source и Target доменах на КД, которые являются эмулятором PDC.
Миграция OU
При миграции содержимого Active Directory в новый лес часто будет уместно создать новую структуру OU, но если нужно перенести существующую, то сделать это можно так:
Сделаем экспорт:
ldifde -f c:sourceOUs.ldf -r "(objectClass=organizationalUnit)" –l objectClass,distinguishedName,ou,description
В полученном файле заменим DC=dom1,DC=loc на DC=dom2,DC=loc и удалим те OU, которые уже есть в Target домене:
Скопируем отредактированный файл на Target контроллер домена и импортируем его:
ldifde -i -f c:sourceOUs.ldf
Проверим результат:
Get-ADOrganizationalUnit -Filter 'Name -like "*"' | FT Name, DistinguishedName -A
Миграция GPO
Вопрос миграции GPO похож на вопрос миграции OU: во многих случаях будет уместно создать новые политики, а не переносить треш.
Но если нужно перенести политики, делается это так:
Сделаем backup политики из Source domain и скопируем его в Target domain:
В Target domain создадим пустую политику и запустим Import settings (мастер предложит сделать backup существующей политики, но смысла в этом нет т.к. она пустая).
Обратите внимание, при импорте нужно указывать папку в которой находится backup (например C:shareGPimport) а не папку с backup (например C:shareGPimport{%uid%})
После импорта, политику можно произвольным образом переименовать, убедиться, что все опции были перенесены корректно и применить ее к соответствующим OU в Target domain:
Установка ADMT
Т.к. я описываю сценарий, в котором буду использовать массовые операции через CLI, утилита ADMT должна быть установлена в Target domain на контроллере домена (я буду использовать временный контроллер домена srv1.dom2.loc, который будет выведен из эксплуатации после окончания миграции).
В этом случае лучше использовать выделенный SQL сервер на выделенном сервере – я буду использовать srv2.dom2.loc
Утилиту PES (ADMT password migration DLL) установим на контроллере домена (который тоже будет выведен из эксплуатации после окончания миграции) в Source domain, в моем случае это сервер srv3.dom1.loc
Установим SQL Server на srv2.dom2.loc в такой конфигурации:
После установки необходимо опубликовать SQL в локальном брандмауэре srv2.dom2.loc разрешив доступ к sqlservr.exe , открыв порты tcp 80, 135, 139, 443, 445, 1433, 63419 (порт инстанса который будет использовать ADMT) и udp 123, 137, 138, 445, 1025, 1434.
На сервере srv1.dom2.loc скачаем версию ADMT 3.2 с Microsoft Connect (версия в Download Center 2010 года и несовместима с новыми версиями Windows Server и SQL).
Установим ADMT указав имя сервера БД и инстанс для ADMT:
Создадим ключ для PES:
ADMT Key /Option:Create /SourceDomain:dom1.loc /KeyFile:C:key.pes /KeyPassword:P@$$w0rd
Скачаем PES x64 c Microsoft Connect и установим его на сервер srv3.dom1.loc который, напомню, является временным контроллером домена в Source лесу.
В процессе установки, укажем путь и пароль к созданному ранее ключу, а в качестве учётной записи для службы будем использовать DOM2admt
После перезагрузки, откроем оснастку services.msc и запустим службу Password Export Server Service
После окончания (или во время перерыва) миграции из соображений безопасности службу рекомендуется останавливать.
Обратите внимание, при миграции будут скопированы и установлены даже те пароли, которые не соответствуют политике Target домена (например, пустые или «123»).
Для проверки возможности миграции SID в Target domain создадим пустую Domain local группу %NETBIOSNAME%$$$.
Опции по-умолчанию
В ходе работ я буду показывать как создать свои конфигурации процессов миграции, и если не указать какую-то опцию в явном виде она получит параметр который указан в C:WindowsADMTtemplatescript.vbs
Разумеется, этот файл можно редактировать по всякому, но лучше для каждого процесса использовать свой Option file, что я и буду делать.
Обратите внимание, синтаксис для Option File, Script и логов отличается.
Управление исключаемыми атрибутами ADMT
По-умолчанию ADMT исключает набор атрибутов во время миграции, посмотреть этот список можно создав и выполнив скрипт DisplayExclusionList.vbs :
Set o = CreateObject("ADMT.Migration") WScript.Echo o.SystemPropertiesToExclude
.cscript.exe C:DisplayExclusionList.vbs
Чтобы изменить список исключаемых атрибутов можно использовать скрипт ExclusionList.vbs :
Set o = CreateObject("ADMT.Migration") o.SystemPropertiesToExclude = "<Attribute1>,<Attribute2>,<Attribute3>"
.cscript.exe C:ExclusionList.vbs
В примере я добавил в исключения созданный мною атрибут “aaa” :
Если версия схемы в Source и Target разная (у меня, например, в Source установлен Exchange 2010, а в Target Exchange 2013) в каждом логе будут присутствовать дополнительно исключенные атрибуты (на скриншете видно примерно четверть) и Unspecified error 0x80004005 :
Отключение требования смены пароля при следующем входе Пользователя
После миграции Пользователя атрибут pwdLastSet будет присвоено значение 0 , а это значит что при первом входе пользователю будет предложено сменить пароль.
С точки зрения безопасности — это разумный шаг, но с точки зрения процесса миграции будет лучше если пользователи сменят пароли спустя какое-то время.
В любом случае решить этот вопрос стоит на этапе подготовки, а не в процессе миграции.
Т.к. пользователей мы будем переносить массово по OU, наиболее удобный способ отключить требование смены пароля при следующем входе таков:
Get-ADUser -Filter * -SearchBase "%OU Path%" | Set-ADUser -ChangePasswordAtLogon $False
Включение Exchange MRS Proxy
На Source Exchange сервере включим Exchange MRS Proxy:
Set-WebServicesVirtualDirectory -Identity "EWS (Default Web Site)" -MRSProxyEnabled $true
Уменьшить таймаут можно указав в файе C:Program FIlesMicrosoftExchange Serverv14ClientAccessexchwebewsweb.config значение DataImportTimeout 00:10:00 (что равно 10 минутам) и выполнив iisreset.
Подробнее о MRS Proxy – http://technet.microsoft.com/en-us/library/ee732395.aspx
Выгрузка Групп, Пользователей и Компьютеров в .csv
Для массовой миграции объектов Active Directory ADMT использует так называемые include files в которых указаны списки того, что будет мигрироваться – «пачки» Групп, Пользователей и Компьютеров.
Для каждого типа объектов необходим свой include file.
ADMT умеет работать с форматами .txt и .csv, и т.к. последний удобнее в обработке, использовать будем именно его.
Как я уже говорил, я буду мигрировать «пачки» по OU, из Source AD их можно выгрузить так:
Get-ADGroup -Filter {ObjectClass -eq "Group"} -SearchBase "%OU path%" | select sAMAccountName | Export-csv C:%filename%.csv
Get-ADUser -Filter {ObjectClass -eq "User"} -SearchBase "%OU path%" | select sAMAccountName | Export-csv C:%filename%.csv
Get-ADComputer -Filter {ObjectClass -eq "Computer"} -SearchBase "%OU path%" | select sAMAccountName | Export-csv C:%filename%.csv
Результат выглядит следующим образом (обратите внимание, к объектам типа “Computer” знак $ добавляется автоматически):
Если Вы хотите переименовать пользователей при миграции можно создать расширенный Include file в формате .csv или .txt:
Перед началом миграции вы должны сделать экспорт всех объектов которые будут мигрированы, и поделить на «пачки» согласно плану миграции.
Эти объекты крайне желательно не изменять на всем протяжении миграции.
Миграция одной Группы
Я не буду подробно описывать каждый шаг мастера ADMT т.к. это достаточно подробно описано в ADMT Guide, так что перейдем к практике.
В первую очередь мигрируем группу DOM1$$$ с миграцией SID
…и убедимся, что все прошло штатно:
В Target domain был успешно создан новый объект и записан атрибут SID history:
Массовая миграция Групп
В первую очередь нужно мигрировать все Группы потому что при миграции Пользователей и Компьютеров их членство в группах должно быть восстановлено автоматически.
Обратите внимание, после окончания миграции Пользователей и Компьютеров необходимо будет выполнить повторную миграцию Групп в режиме Merge + Fix members of Group для того чтобы полностью убедится в том, что членство в группах в Target = членству в группах Source.
Итак, запустим Мастер миграции Групп и будем использовать список групп из Include файла, а не указывать группы вручную:
Укажем путь к Include файлу и OU в Source domain в котором находятся группы из Include файла.
Обратите внимание, первые строчки .csv не являются группами, поэтому будут исключены.
Вместе с группой не будем переносить Пользователей которые в нее входят, но SID запишем в SID history обязательно т.к. это позволит членам группы получать доступ к ресурсам которые доступны одноименной группе в Source лесу.
Если случилось так, что группа была создана в Target лесу до начала миграции, за счет опции Merge будет выполнено слияние (фактически, в существующую группу будут дописаны атрибуты и изменено членство):
После окончания каждого этапа миграции рекомендую внимательно просматривать лог операции. Как видите, в моем случае имеется предупреждение, связанное с адресной книгой Exchange.
Но на данном этапе главное, что группы были созданы в target домене и атрибут SID history записан.
Если мигрировать большое количество «пачек» вручную с помощью Мастера имеет место риск человеческой ошибки, чтобы его минимизировать можно использовать Option file такого содержания:
[Migration] IntraForest=No SourceDomain=dom1.loc SourceOu=Branch office/Groups TargetDomain=dom2.loc TargetOu=Branch office/Groups ConflictOptions=Merge [Group] MigrateSIDs=Yes UpdatePreviouslyMigratedObjects=No FixGroupMembership=No UpdateGroupRights=No MigrateMembers=No
Обратите внимание на синтаксис параметра TargetOu – этот параметр нужно будет менять при миграции новой “пачки” если она мигрируется в другой OU.
Для запуска миграции с использованием Option и Include файлов выполним:
ADMT GROUP /O: "C:MigrationOptFlGroup.txt" /F: "C:MigrationBranchOfficeGroups.csv"
Убедимся что миграция прошла предсказуемо:
Миграция одного Пользователя
Т.к. я выполняю Cross-forest сценарий, будет выполнена не миграция, а создание аналогичных объектов типа «Пользователь» в Target domain.
Это необходимо для того, чтобы сервисы которые будут мигрироваться, до окончательной миграции пользователей могли работать уже с новыми аккаунтами. Например, Exchange создаст этим пользователям почтовые ящики и в них будет поступать свежая почта в то время пока будет выполняться копирования содержимого ящика из Source организации.
В случае миграции Active Directory совместно с Exchange командлет Prepare–MoveRequest создаст пользователей в Target domain, так что этот этап можно было бы пропустить, но я покажу как выполнить «первичную» миграцию пользователей на случай если вы мигрируете только AD .
Будем мигрировать пользователя user1 в аналогичный OU в Target domain:
На этом этапе будем генерировать новые пароли для Пользователей, а при повторной миграции скопируем пароли из Source домена:
Аккаунты в Source оставим включенными, а в Target выключенными. SIDы мигрировать не будем.
Возобновим членство в группах т.к. Группы у нас уже смигрированны.
В Target домене еще нету Пользователей из Source домена, так что опция Merge будет сугубо номинальной.
Результат миграции посмотрим в логе:
Отключим необходимость смены пароля пользователем при следующем входе:
Get-ADUser -Identity $%username%| Set-ADUser -ChangePasswordAtLogon:$false
Проверим членство в группах:
Get-ADPrincipalGroupMembership %username% | select name
Проверим SID history:
Get-ADUser %user% -Property sIDHistory
Массовая миграция Пользователей
Для массовой миграции Пользователей будем использовать Option file :
[Migration] IntraForest=No SourceDomain=dom1.loc TargetDomain=dom2.loc TargetOu=Branch office/Users PasswordOption=Complex PasswordServer="" ConflictOptions=Merge [User] DisableOption=DisableTarget SourceExpiration=None MigrateSIDs=No TranslateRoamingProfile=No UpdateUserRights=Yes MigrateGroups=No UpdatePreviouslyMigratedObjects=No FixGroupMembership=Yes MigrateServiceAccounts=No UpdateGroupRights=No
Пример выполнения массовой миграции (которая у меня состояла из user2 и user3):
ADMT USER /O: "C:MigrationOptFlUsers.txt" /F: " C:MigrationBranchOfficeUsers.csv"
Миграция одного почтового ящика Exchange
Откроем Exchange Management Shell в Target domain и введём учётные данные Target domain:
$localCredentials = Get-Credential
.. и учетные данные Source Domain:
$RemoteCredentials = Get-Credential
Перейдём в папку со скриптами Exchange:
cd "C:Program FilesMicrosoftExchange ServerV15Scripts"
Выполним подготовку:
.Prepare-MoveRequest.Ps1 -Identity %EmailAddress% -RemoteForestDomainController %FQDN of Source DC% -RemoteForestCredential $RemoteCredentials -LocalForestDomainController %FQDN of Target Forest DC% -LocalForestCredential $LocalCredentials -TargetMailUserOU “Distinguished name of OU in TargetForest” –UseLocalObject -Verbose
В Target AD будет создан новый Пользователь в состоянии Disable:
В дальнейшем, когда будем выполнить повторную миграцию пользователей в режиме Merge этот Пользователь будет дополнен атрибутами, включен в группы и ему будет установлен пароль такой же, как в Source домене.
Выполним миграцию почтового ящика для созданного пользователя:
New-MoveRequest –Identity '%EmailAddress%' –Remote –Remotehostname ‘%Source Exchange FQDN%’ -RemoteCredential $RemoteCredentials –TargetDeliverydomain '%mail domain%'
Выглядит миграция ящика Exchange как на скриншете ниже и занимает определенное время, в зависимости от размера ящика:
При следующем запуске Outlook запросит перезапуск:
.. И после этого будет работать с новым сервером:
Массовая миграция почтовых ящиков
Массовая миграция почтовых ящиков так же проста, как и миграция одного почтового ящика, но в файле со списком пользователей нужно заменить заголовок sAMAccountName на Identity
Затем укажем «Include файл» (у меня он состоит из единственного ящика [email protected]) в уже знакомые командлеты:
Import-Csv C:%file with users%.csv | .Prepare-MoveRequest.Ps1 -RemoteForestDomainController %FQDN of Source DC% -RemoteForestCredential $RemoteCredentials -LocalForestDomainController %FQDN of Target Forest DC% -LocalForestCredential $LocalCredentials -TargetMailUserOU “Distinguished name of OU in TargetForest” –UseLocalObject -Verbose
Import-Csv C:%file with users%.csv | New-MoveRequest –Remote –Remotehostname ‘%Source Exchange FQDN%’ -RemoteCredential $RemoteCredentials –TargetDeliverydomain '%mail domain%'
Миграция одного Компьютера и Профиля
Миграция Компьютеров и Профилей технически сложнее и, соответственно, вызывает больше проблем чем миграция Пользователей.
На момент запуска миграции компьютеры должны быть подключены в сеть, но пользователи должны сделать log off для того чтобы не было заблокированных пользователями файлов.
После миграции компьютер будет автоматически перезагружен.
Обратите внимание, после миграции в Target domain у Компьютера в группе локальных администраторов останутся только локальные учетные записи, поэтому нужно заранее применить на OU в который будет выполняться миграция, политику которая будет использовать Restricted Groups:
Запустим Мастер миграции Компьютеров (опции, которые я устанавливал будет видно в логе).
Больше всего проблем вызывает миграция Профилей, бывает так что даже в лабораторной среде она проходит успешно только со второго раза (компьютер придется вручную переводить в Source domain и также вручную удалять созданные профили %user%.%Target Domain NetBIOS name%)
Вот как выглядит результат успешной миграции Компьютера и Профиля:
Лог миграции Компьютера:
Лог агента переноса Профилей:
Еще раз заостряю ваше внимание – миграция профилей может быть большой проблемой с низкой предсказуемостью, так что предусмотрите достаточное количество времени на этот этап.
При корректном переносе профиля пользователя сохраняются файлы, папки, настройки рабочего стола, браузера, подключенные принтеры и т.п.:
Проверим logon server
[System.Environment]::GetEnvironmentVariable("logonserver")
… и канал между Компьютером и доменом:
Test-ComputerSecureChannel
Но если случилось так, что после миграции Компьютера и Профиля пользователь при логине получает новый, «пустой» профиль быстро исправить ситуацию можно так:
- Логинимся на ПК с правами Администратора
- Открываем Regedit и переходим в HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionProfileList
- Выбираем SID пользователя из Target домена и в параметре ProfileImagePath меняем C:Users%username%.%Target domain NetBIOS name% на C:Users%username%
- Профиль C:Users%username%.%Target domain NetBIOS name% можно удалить.
- Пользователю из Source домена в профиле можно ничего не менять т.к. в процессе миграции он будет заблокирован.
- Перезагружаем Компьютер и у Пользователя при входе в Target domain будет тот же профиль который был в Source домене.
Массовая миграция Компьютеров и Профилей
Массовая миграция Компьютеров и Профилей практически аналогична массовой миграции Групп, которая была рассмотрена ранее, но из Include file нужно убрать знак $ в именах компьютеров.
Если «пачек» с компьютерами много, можно использовать Option файл такого содержания:
[Migration] IntraForest=No SourceDomain=dom1.loc SourceOu=Branch office/Computers TargetDomain=dom2.loc TargetOu=Branch office/Computers ConflictOptions=Merge [Computer] UpdateAllManagedServiceAccounts=No RestartDelay=0 TranslateOption=Replace TranslateFilesAndFolders=Yes TranslateLocalGroups=Yes TranslatePrinters=Yes TranslateRegistry=Yes TranslateShares=Yes TranslateUserProfiles=Yes TranslateUserRights=Yes
Еще раз обратите внимание на синтаксис параметра TargetOu – этот параметр нужно будет менять при миграции новой “пачки” если она мигрируется в другой OU.
Вот пример использования Option и Include файлов:
ADMT COMPUTER /O: C:MigrationOptFlComputer.txt /F: C:MigrationBranchOfficeComputers.csv
Теперь массово смигрируем профили:
[Migration] IntraForest=No SourceDomain=dom1.loc TargetDomain=dom2.loc [Security] TranslateOption=Replace TranslateUserProfiles=Yes TranslateUserRights=Yes
ADMT SECURITY /O: C:MigrationOptFlProfile.txt /F: C:MigrationBranchOfficeComputers.csv
Повторная массовая миграция Пользователей
В процессе повторной миграции Пользователей будет перенесен пароль, отключен Source и включен Target аккаунт, записан атрибут SID history, и еще раз восстановлено членство в группах.
Обратите внимание, будут скопированы и установлены даже те пароли, которые не соответствуют политике Target домена (например, пустые или «123»).
Пример Option file:
[Migration] IntraForest=No SourceDomain=dom1.loc TargetDomain=dom2.loc TargetOu=Branch office/Users PasswordOption=Copy PasswordServer="srv3.dom1.loc" ConflictOptions=Merge [User] DisableOption=EnableTarget SourceExpiration=0 MigrateSIDs=Yes TranslateRoamingProfile=No UpdateUserRights=Yes MigrateGroups=No UpdatePreviouslyMigratedObjects=Yes FixGroupMembership=Yes MigrateServiceAccounts=No UpdateGroupRights=No
…и пример выполнения повторной миграции
ADMT USER /O: "C:MigrationOptFlUsers.txt" /F: "C:MigrationBranchOfficeUsers.csv"
Теперь, когда Пользователи полностью смигрированы и включены убедимся, что содержимое почтовых ящиков перенесено, и доставка почты работает корректно (я не описываю миграцию адресных книг Exchange с помощью FIM и настройку EDGE подписок я опишу в отдельной статье):
После того, как будет выполнена миграция всех «пачек» Пользователей и Компьютеров желательно выполнить повторную миграцию Групп в режиме Merge + Fix members of Group для того чтобы полностью убедится в том, что членство в группах в Target = членству в группах Source.
Вообще, во время миграции не следует производить какие-либо операции в AD, а особенно изменять членство в группах.
Но предположим Пользователя который находится в Source домене добавили в группу которая находится в Target домене а спустя некоторое время смигрировали пользователя из Source в Target domain.
В этом случае его членство в группе Target домена будет утеряно. Чтобы решить эту проблему, можно использовать
[spoiler title=’Скрипт’ collapse_link=’true’]
# # Версия 12.09.2012 для публичного доступа # PowerShell Script для "Fix Group Membership" для мигрировавших пользователей, имеющих "хвост" в Foreign Security Principals. # Этот случай возникает, когда пользователь из исходного лесадомена добавляется в группу в целевом лесудомене, а затем сам мигрирует в целевой лесдомен и ему необходимо восстановить членство в группе в целевом лесу. # 2012, И.Мамышев # Использование: <scriptname> | <scriptname> YES # Параметр YES разрешает скрипту выполнять изменения в Active Directory. По-умолчанию скрипт выполняется в режиме только чтения. # # Скрипт производит поиск "пересечения" Users и Foreign Security Principals (FSP) в домене, затем определяет членство в группах для отобранных FSP и интерактивно предлагает администратору добавить пользователей (из списка "пересечения") домена в эти группы безопасности. # # Скрипт использует командлеты для работы с Active Directory из состава операционной системы Windows Server 2008 R2 (тестировалось только на Windows Server 2008 R2). # ### $ErrorActionPreference = "SilentlyContinue" ######################################################### # === !!! НИЖЕ ДАННОЙ СТРОКИ СКРИПТ НЕ ИСПРАВЛЯТЬ !!! === ######################################################### $Argument0 = $args[0] $Log1 = @(); # Migrated ADUsers $Log2 = @(); # ADUsers, которым требуется FixGroupMembership и соответствующие Security Group $Log2_ = @(); $Log3 = @(); # Users и Groups, в которые был добавлен User $Count1 = 1; $Count2 = 1; $Count2_ = 1; $Count3 = 1; $dt = Get-Date -Format "dd.MM.yyyy HH:mm:ss" $dt2 = Get-Date -Format "dd.MM.yyyy HH.mm.ss" $CurrentDir = $MyInvocation.MyCommand.Definition | split-path -parent # Каталог, в котором размещён файл исполняемого в данный момент скрипта. New-Item ($CurrentDir + "log") -type Directory -Force > $null $LogFile = $CurrentDir + "log" + $MyInvocation.MyCommand.Name + "_$dt2.html" # Имя файла журнала $Wellcome = "И.Мамышев, 2012 " + $MyInvocation.MyCommand.Name ######################################################### # Импорт необходимых модулей и добавление необходимых остасток Import-Module ActiveDirectory Clear-Host ## Write-Host $Wellcome -ForegroundColor Green $Wellcome >> $LogFile Write-Host '"Fix Group Membership" для мигрировавших пользователей, имеющих "хвост" в Foreign Security Principals' -ForegroundColor Green if ($Argument0 -ne "YES") { Write-Host 'ВНИМАНИЕ! Параметр YES не указан.' -ForegroundColor Yellow Write-Host "Скрипт выполняется в режиме только чтения." -ForegroundColor Yellow } Write-Host "" ## Определение имени контроллера домена в Целевом лесу (для будущего использования ниже) $DestinationForestPDC = (Get-ADDomain -Current LoggedOnUser -ErrorAction $ErrorActionPreference).PDCEmulator # Получаем список объектов класса "user" $ADUsers = @(Get-ADObject -Filter {objectClass -eq "user" -and objectClass -ne "computer"} -Properties * -SearchScope Subtree) # Перебираем ADUsers, чьи SIDHistory есть у ForeignSecurityPrincipals в качестве Name $ADUsers | ForEach-Object -Begin { if($ADUsers.Count -le 0) {$Cnt=1}else{$Cnt=$ADUsers.Count}; $percent=$Cnt/100; $c = 0; #"Будет обработано $Cnt записей" } -Process { $c++; [int]$a = $c/$percent; Write-Progress -Activity "Идет поиск Foreign Security Principals по SIDHistory мигрировавших ADUsers..." -PercentComplete $a -CurrentOperation "$a% завершено" -Status "Пожалуйста подождите."; #Счетчик - процент выполнения if ($_.sIDHistory.Value.Value) { # Если атрибут sIDHistory заполнен, то пытаемся найти его "хвост" в Foreign Security Principals $FSP = Get-AdObject -Filter { (objectClass -eq "foreignSecurityPrincipal") -and (Name -eq $_.sIDHistory.Value.Value) } -Properties * -ErrorAction $ErrorActionPreference if ($FSP) { $Log1 += (new-object psobject | add-member noteproperty "№" "$Count1" -passthru | add-member noteproperty "sAMAccountName" $_.sAMAccountName -passthru | add-member noteproperty "DisplayName" $_.DisplayName -passthru | add-member noteproperty "ObjectSID" $_.ObjectSID.Value -passthru | add-member noteproperty "sIDHistory" $_.sIDHistory.Value -passthru | add-member noteproperty "FSPObjectGUID" $FSP.ObjectGUID -passthru | add-member noteproperty "CreateTimeStamp" $_.CreateTimeStamp -passthru) $Count1++ } } } # Готовим текст в лог if ($Log1.Length -gt 0) { $Text += "<br>---------------------------------------------------------------------</br>" $Text += "<b><font color=black>Всего записей типа Foreign Security Principal: " + $ForeignSecurityPrincipals.Count + "</font></b></br>" $Text += "<b><font color=red>Из них пользователей леса " + (Get-ADDomain -Current LoggedOnUser).Forest + ", ВОЗМОЖНО требующих Fix Group Membership:</font></b>" $Text += ($Log1 | ConvertTo-Html) $Text >> $LogFile } "Выполнено: "+(Get-Date -Format "dd.MM.yyyy HH:mm:ss") >> $LogFile Write-Progress -Activity "Working..." -Completed -Status "All done." #Отображение индикатора выполнения завершим. Пропадет с экрана. #================================================================================== # Перебираем $Log1 и получаем список Security Group, в которых необходимо выполнить Fix Group Membership $Log1 | ForEach-Object -Begin { if($Log1.Count -le 0) {$Cnt=1}else{$Cnt=$Log1.Count}; $percent=$Cnt/100; $c = 0; #"Будет обработано $Cnt записей" } -Process { $c++; [int]$a = $c/$percent; Write-Progress -Activity "Идет получение списка Security Group for ForeignSecurityPrincipals=ADUsers..." -PercentComplete $a -CurrentOperation "$a% завершено" -Status "Пожалуйста подождите."; #Счетчик - процент выполнения # Получаем список групп, членом которых является FSP $SID = $_.sIDHistory $FSP = Get-ADObject -Filter {cn -eq $SID} -Properties * -ErrorAction $ErrorActionPreference $ADUserGroupForFixGroupMembership = @($FSP.MemberOf) if ($ADUserGroupForFixGroupMembership.Count -ne 0) { $ADUsersAMAccountName = $_.sAMAccountName $ADUserSID = $_.ObjectSID $ADUserCreateTimeStamp = $_.CreateTimeStamp $ADUserGroupForFixGroupMembership | ForEach-Object { # Перечисляем группы, т.к. FSP можеь быть членом нуля, одной или нескольких групп # Получим и сохраним SID группы (MemberOf возвращает значения в формате DN) $groupSID = (Get-ADObject -Filter {distinguishedName -eq $_} -Properties * -ErrorAction $ErrorActionPreference).ObjectSID.Value; $groupSamAccountName = (Get-ADObject -Filter {distinguishedName -eq $_} -Properties * -ErrorAction $ErrorActionPreference).SamAccountName; # Проверяем, не является ли уже ADUser членом группы $IsMember = $False; (Get-ADObject -Filter {objectSID -eq $ADUserSID} -Properties * -ErrorAction $ErrorActionPreference).MemberOf | ForEach-Object { if ($groupSID -eq (Get-ADObject -Filter {distinguishedName -eq $_} -Properties * -ErrorAction $ErrorActionPreference).ObjectSID.Value) { # Если есть совпадение, то выставляем флаг $IsMember = $True; } } if ($IsMember -eq $False) { # Включаем пользователя в список нуждающихся $Log2 += (new-object psobject | add-member noteproperty "№" "$Count2" -passthru | add-member noteproperty "ADUsersAMAccountName" $ADUsersAMAccountName -passthru | add-member noteproperty "ADUserSID" $ADUserSID -passthru | add-member noteproperty "ADUserGroupSamAccountName" $groupSamAccountName -passthru | add-member noteproperty "ADUserGroupSID" $groupSID -passthru | add-member noteproperty "ADUserCreateTimeStamp" $ADUserCreateTimeStamp -passthru) $Count2++ } } } } # Готовим текст в лог if ($Log2.Length -gt 0) { $Text2 += "<br>---------------------------------------------------------------------</br>" $Text2 += "<b><font color=green>Список пользователей лесадомена Active Directory " + (Get-ADDomain -Current LoggedOnUser).Forest + ", которым требуется Fix Group Membership в перечисленных Security Group:</font></b>" $Text2 += ($Log2 | ConvertTo-Html) $Text2 >> $LogFile } "Выполнено: "+(Get-Date -Format "dd.MM.yyyy HH:mm:ss") >> $LogFile Write-Progress -Activity "Working..." -Completed -Status "All done." #Отображение индикатора выполнения завершим. Пропадет с экрана. #================================================================================== IF ($Log2.Count -ne 0) { # Если список не пуст, то выполняем Write-Host "Список пользователей лесадомена Active Directory " (Get-ADDomain -Current LoggedOnUser).Forest ", которым требуется Fix Group Membership в перечисленных Security Group:" -ForegroundColor Yellow $Log2 = ($Log2 | Sort-Object -Property ADUserCreateTimeStamp -Descending) $Log2 | ft ADUsersAMAccountName, ADUserGroupSamAccountName, ADUserCreateTimeStamp -autosize # Перебираем $Log2 и интерактивно спрашиваем Администратора по каждому пользователю - нужно ли его добавлять в соответствующую группу Write-Host "Будет задано" $Log2.Count "вопросов." -ForegroundColor Green $Log2 | ForEach-Object { if ($PrevInput -eq "A") { # Если введено "A", то остальные все записи добавляем без вопросов $Log2_ += (new-object psobject | add-member noteproperty "№" "$Count2_" -passthru | add-member noteproperty "ADUsersAMAccountName" $_.ADUsersAMAccountName -passthru | add-member noteproperty "ADUserSID" $_.ADUserSID -passthru | add-member noteproperty "ADUserGroupSamAccountName" $_.ADUserGroupSamAccountName -passthru | add-member noteproperty "ADUserGroupSID" $_.ADUserGroupSID -passthru) $Count2_++ Return } # Если введено "R", то остальные все записи пропускаем if ($PrevInput -eq "R") {Return;} Write-Host "Добавить " -NoNewLine Write-Host $_.ADUsersAMAccountName -NoNewLine -ForegroundColor Yellow Write-Host " в группу " -NoNewLine Write-Host $_.ADUserGroupSamAccountName -NoNewLine -ForegroundColor Yellow Write-Host "?" -NoNewLine $Input = Read-Host (" [Y - Yes, A - Yes for All, N - No, R - No for All]") While (!($Input -eq "Y" -or $Input -eq "A" -or $Input -eq "N" -or $Input -eq "R")) { Write-Host "Данные введены некорректно. Повторите ввод." -ForegroundColor Red $Input = Read-Host } $PrevInput = $Input if ($Input -eq "Y" -or $Input -eq "A") { $Log2_ += (new-object psobject | add-member noteproperty "№" "$Count2_" -passthru | add-member noteproperty "ADUsersAMAccountName" $_.ADUsersAMAccountName -passthru | add-member noteproperty "ADUserSID" $_.ADUserSID -passthru | add-member noteproperty "ADUserGroupSamAccountName" $_.ADUserGroupSamAccountName -passthru | add-member noteproperty "ADUserGroupSID" $_.ADUserGroupSID -passthru) $Count2_++ } } # Готовим текст в лог if ($Log2_.Length -gt 0) { $Text2_ += "<br>---------------------------------------------------------------------</br>" $Text2_ += "<b><font color=orange>Список пользователей лесадомена Active Directory " + (Get-ADDomain -Current LoggedOnUser).Forest + ", которым Администратор разрешил выполнить Fix Group Membership в соответствующих Security Group:</font></b>" $Text2_ += ($Log2_ | ConvertTo-Html) $Text2_ >> $LogFile } "Выполнено: "+(Get-Date -Format "dd.MM.yyyy HH:mm:ss") >> $LogFile $Log2 = $Log2_ #================================================================================== # Перебираем $Log2 и добавляем ADUsers в Secirity Groups, согласно данных из таблицы $Log2 $Log2 | ForEach-Object -Begin { if($Log2.Count -le 0) {$Cnt=1}else{$Cnt=$Log2.Count}; $percent=$Cnt/100; $c = 0; #"Будет обработано $Cnt записей" } -Process { $c++; [int]$a = $c/$percent; Write-Progress -Activity "Идет добавление ADUsers в Security Group..." -PercentComplete $a -CurrentOperation "$a% завершено" -Status "Пожалуйста подождите."; #Счетчик - процент выполнения if ($Argument0 -eq "YES") { # Если в качетсве параметра в скрипт передано слово "YES", то выполняем командлет в "боевом" режиме $Result = Add-ADGroupMember -Identity $_.ADUserGroupSID -Members $_.ADUserSID } else { $Result = Add-ADGroupMember -Identity $_.ADUserGroupSID -Members $_.ADUserSID -WhatIf } if ($true) { # Если добавление произведено успешно, то добавляем запись в отчёт $Log3 += (new-object psobject | add-member noteproperty "№" "$Count3" -passthru | add-member noteproperty "ADUsersAMAccountName" $_.ADUsersAMAccountName -passthru | add-member noteproperty "ADUserSID" $_.ADUserSID -passthru | add-member noteproperty "ADUserGroupSamAccountName" $_.ADUserGroupSamAccountName -passthru | add-member noteproperty "ADUserGroupSID" $_.ADUserGroupSID -passthru) $Count3++ } } # Готовим текст в лог if ($Log3.Length -gt 0) { $Text3 += "<br>---------------------------------------------------------------------</br>" $Text3 += "<b><font color=blue>Список пользователей лесадомена Active Directory " + (Get-ADDomain -Current LoggedOnUser).Forest + " и групп, в которые они были добавлены:</font></b>" $Text3 += ($Log3 | ConvertTo-Html) $Text3 >> $LogFile } "Выполнено: "+(Get-Date -Format "dd.MM.yyyy HH:mm:ss") >> $LogFile Write-Progress -Activity "Working..." -Completed -Status "All done." #Отображение индикатора выполнения завершим. Пропадет с экрана. } ELSE { Write-Host "Нет данных для обработки!" -ForegroundColor Yellow }; #================================================================================== Write-Host "" Write-Host "Журнал работы скрипта: $LogFile" -ForegroundColor DarkGreen
[/spoiler]
Отчеты о работе ADMT
Нужно сказать что у ADMT есть Мастер отчетов, который позволяет создать убогие и неинформативные, но тем не менее отчеты:
Я рекомендую в ходе подготовки, выполнения и завершения использовать формы, которые вы разработаете исходя из конкретно вашего сценария миграции.
Последние 20 логов хранятся в C:WindowsADMTLogs , посмотреть краткую информацию по ним удобно с помощью команды ADMT TASK :
Если есть необходимость вытащить из базы архив логов, можно создать новую папку и выполнить в ней ADMT TASK /GETLOG:YES
Надеюсь озвученная информация будет полезной, а если нужна будет помощь — используйте форму на главной странице моего сайта.