Миграция контроллера домена с SAMBA на ActiveDirectory

от автора

image
Вот и пришло время рассказать о способе, который методом научного тыка, нескольких умных людей и несколько часов свободного времени помогли мне мигрировать домен, построенный предыдущим админом с SAMBA на ActiveDirectory.

Многие знают и используют возможность использовать SAMBA в качестве домена, но, по моему мнению, это можно использовать только как лабораторный стенд, а в реальный жизни лучше не использовать. Это решает каждый сам, меня не устроило отсутствие групповых политик, постоянная невозможность авторизации, частые зависания и неимоверные утечки памяти (через 30 дней непрерывной работы, иногда, пожираются все 16 ГиБ оперативной памяти и утилизируется весь swap-раздел). Поэтому моему терпению пришёл конец, я попытался собраться с мыслями и несколько раз пытался эмулировать переход в виртуальной среде, но всё не было времени, пока не пришёл сибирский пушистый зверёк и не положил по непонятным причинам всю структуру и зависящий от него bind9, который полностью парализовал работу офиса в 70 человек. Вот тут-то и настало время срочного перехода, 2 дня чтения разных мануалов, 2 дня попыток взлететь на ActiveDirectory ни к чему не привели, пока не натолкнулся на эту статью и она же на TechNet .
Так же били попытки смены ролей домена, т.е. ввести в линуксовый домен Windows Server и сделать его резервным контроллером, а затем повысить его роль до pdc, но, как показал 1 тест в виртуальной среде – это была неудачная идея, т.к. переносились все глюки GPO (их просто не было), а возможно ещё какие-либо недоразумения.

У меня всё же не получилось всё свести к одному нажатию кнопки, поэтому будет использоваться Active Perl (х86), Microsoft Office Excel (делал на 2003 и 2007), Batch, VBS, а так же утилита newsid (которую на сайте успешно удалили) и дополнительный модуль acctinfo.
Все действия будут происходить в Windows-окружении на рабочей станции, а потом Windows Server (здесь хочется уточнить, что Windows Server был 2003 R2 SP2 по некоторым соображениям, поэтому работа на более современных системах не тестировалась), так же есть один нюанс, Windows Server лучше использовать английской версии, без MUI, пока происходит миграция, потом ничто не мешает его поставить.

Итак, хватит воды, приступим к самому процессу.

Перед дальнейшими работами можно начать ставить систему, но ничего в ней не настраивая и не создавая (я даже драйвера ставить не стал, пока не создам пользователей).

Дня начала нам надо вытащить список всех пользователей и групп, в которых они находятся (у меня была проблема, не все пользователи экспортировались с группами, в которые они входят, надо проверять), для этого используем vbs скрипт, которыйнашёл здесь, но его пришлось модифицировать, что бы получить более наглядно и нужные параметры (так же стоит упомянуть что это надо делать на ПК, входящим в домен и под доменной учёткой, я делал под учёткой глобального администратора домена).

export AD User Accounts.vbs

On Error Resume Next    strFileName = "Users-Groups-SIDs.xlsx"    Set objShell = CreateObject("Wscript.Shell")    strPath = Wscript.ScriptFullName  Set objFSO = CreateObject("Scripting.FileSystemObject")    Set objFile = objFSO.GetFile(strPath)  strFolder = objFSO.GetParentFolderName(objFile)      SET objExcelApp = CREATEOBJECT("Excel.Application")  SET objWB = objExcelApp.Workbooks.Add  SET objExcel = objWB.Worksheets(1)    objWB.SaveAs(strFolder & "\" & strFileName)    Const ADS_SCOPE_SUBTREE = 2    Set objConnection = CreateObject("ADODB.Connection")  Set objCommand =   CreateObject("ADODB.Command")  objConnection.Provider = "ADsDSOObject"  objConnection.Open "Active Directory Provider"  Set objCommand.ActiveConnection = objConnection    objCommand.Properties("Page Size") = 1000  objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE     'Set the path of the file to the same folder of the script  'Open the file and make the workbook visible  Set objExcel = CreateObject("Excel.Application")  Set objWorkbook = objExcel.Workbooks.Open(strFolder & "\" & strFileName)  objExcel.Visible = True    'objExcel.Cells(1, 1).Value = "Name"  'objExcel.Cells(1, 1).Font.Bold = TRUE  'objExcel.Columns(1).ColumnWidth = 40  'objExcel.Cells(1, 2).Value = "Security ID"  'objExcel.Cells(1, 2).Font.Bold = TRUE  'objExcel.Columns(2).ColumnWidth = 60    'Starting row of the Excel is 2, since first row are column headings  y = 2    objCommand.CommandText = _      "SELECT * FROM 'LDAP://DC=movavi,DC=srv' WHERE objectCategory='user'"    Set objRecordSet = objCommand.Execute     objRecordSet.MoveFirst     Do Until objRecordSet.EOF     strADsPathUser = objRecordSet.Fields("ADsPath").Value     'wScript.echo strADsPathUser      Set objUser = GetObject(strADsPathUser)          z = 1       objExcel.Cells(y,z) = objUser.sn       objExcel.Cells(1, z).Value = "sn"        'Wscript.Echo objUser.sn       objExcel.Cells(1, z).Font.Bold = TRUE  z = z + 1       objExcel.Cells(y,z) = objUser.givenName       objExcel.Cells(1, z).Value = "givenName"        'Wscript.Echo objUser.givenName       objExcel.Cells(1, z).Font.Bold = TRUE  z = z + 1       objExcel.Cells(y,z) = objUser.initials       objExcel.Cells(1, z).Value = "initials"        'Wscript.Echo objUser.initials       objExcel.Cells(1, z).Font.Bold = TRUE        z = z + 1       objExcel.Cells(y,z) = objUser.description        objExcel.Cells(1, z).Value = "description"        objExcel.Cells(1, z).Font.Bold = TRUE        'Wscript.Echo objUser.description        z = z + 1       objExcel.Cells(y,z) = objUser.codePage        objExcel.Cells(1, z).Value = "codePage"        objExcel.Cells(1, z).Font.Bold = TRUE        'Wscript.Echo objUser.codePage             z = z + 1       objExcel.Cells(y,z) = objUser.sAMAccountName        objExcel.Cells(1, z).Value = "sAMAccountName"       'Wscript.Echo objUser.sAMAccountName         objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1       objExcel.Cells(y,z) = objUser.codePage        objExcel.Cells(1, z).Value = "codePage"        objExcel.Cells(1, z).Font.Bold = TRUE        'Wscript.Echo objUser.codePage         z = z + 1             objExcel.Cells(y,z) = objUser.mail       objExcel.Cells(1, z).Value = "mail"        'Wscript.Echo objUser.mail       objExcel.Cells(1, z).Font.Bold = TRUE          z = z + 1        intUserSID = fnGet_HexString(objUser.ObjectSID)                   objExcel.Cells(y,z) = intUserSID          objExcel.Cells(1, z).Value = "ObjectSID"       'Wscript.Echo objUser.ObjectSID         objExcel.Cells(1, z).Font.Bold = TRUE        z = z + 1       objExcel.Cells(y,z) = objUser.userPrincipalName       objExcel.Cells(1, z).Value = "userPrincipalName"        'Wscript.Echo objUser.userPrincipalName       objExcel.Cells(1, z).Font.Bold = TRUE    z = z + 1             objExcel.Cells(y,z) = objUser.displayName       objExcel.Cells(1, z).Value = "displayName"        'Wscript.Echo objUser.displayName       objExcel.Cells(1, z).Font.Bold = TRUE         z = z + 1             objExcel.Cells(y,z) = objUser.distinguishedName       objExcel.Cells(1, z).Value = "distinguishedName"        'Wscript.Echo objUser.distinguishedName       objExcel.Cells(1, z).Font.Bold = TRUE   z = z + 1        intUserSID = stringlist(objUser.memberOf)                   objExcel.Cells(y,z) = intUserSID          objExcel.Cells(1, z).Value = "memberOf"       'Wscript.Echo objUser.memberOf         objExcel.Cells(1, z).Font.Bold = TRUE                                          ' нет необходимости       z = z + 1             objExcel.Cells(y,z) = objUser.physicalDeliveryOfficeName       objExcel.Cells(1, z).Value = "physicalDeliveryOfficeName"        'Wscript.Echo objUser.physicalDeliveryOfficeName       objExcel.Cells(1, z).Font.Bold = TRUE  z = z + 1             objExcel.Cells(y,z) = objUser.telephoneNumber       objExcel.Cells(1, z).Value = "telephoneNumber"        'Wscript.Echo objUser.telephoneNumber       objExcel.Cells(1, z).Font.Bold = TRUE  z = z + 1             objExcel.Cells(y,z) = objUser.profilePath       objExcel.Cells(1, z).Value = "profilePath"        'Wscript.Echo objUser.profilePath       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.scriptPath       objExcel.Cells(1, z).Value = "scriptPath"        'Wscript.Echo objUser.scriptPath       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.homeDirectory       objExcel.Cells(1, z).Value = "homeDirectory"        'Wscript.Echo objUser.homeDirectory       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.homeDrive       objExcel.Cells(1, z).Value = "homeDrive"        'Wscript.Echo objUser.homeDrive       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.title       objExcel.Cells(1, z).Value = "title"        'Wscript.Echo objUser.title       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.department       objExcel.Cells(1, z).Value = "department"        'Wscript.Echo objUser.department       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.company       objExcel.Cells(1, z).Value = "company"        'Wscript.Echo objUser.company       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.manager       objExcel.Cells(1, z).Value = "manager"        'Wscript.Echo objUser.manager       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.homePhone       objExcel.Cells(1, z).Value = "homePhone"        'Wscript.Echo objUser.homePhone       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.pager       objExcel.Cells(1, z).Value = "pager"        'Wscript.Echo objUser.pager       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.mobile       objExcel.Cells(1, z).Value = "mobile"        'Wscript.Echo objUser.mobile       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.facsimileTelephoneNumber       objExcel.Cells(1, z).Value = "facsimileTelephoneNumber"        'Wscript.Echo objUser.facsimileTelephoneNumber       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.ipphone       objExcel.Cells(1, z).Value = "ipphone"        'Wscript.Echo objUser.ipphone       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.info       objExcel.Cells(1, z).Value = "info"        'Wscript.Echo objUser.info       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.streetAddress       objExcel.Cells(1, z).Value = "streetAddress"        'Wscript.Echo objUser.streetAddress       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.postOfficeBox       objExcel.Cells(1, z).Value = "postOfficeBox"        'Wscript.Echo objUser.postOfficeBox       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.l       objExcel.Cells(1, z).Value = "l"        'Wscript.Echo objUser.l       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.st       objExcel.Cells(1, z).Value = "st"        'Wscript.Echo objUser.st       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.c        objExcel.Cells(1, z).Value = "c"        'Wscript.Echo objUser.c       objExcel.Cells(1, z).Font.Bold = TRUE z = z + 1             objExcel.Cells(y,z) = objUser.wWWHomePage       objExcel.Cells(1, z).Value = "wWWHomePage"        'Wscript.Echo objUser.wWWHomePage       objExcel.Cells(1, z).Font.Bold = TRUE        '''''''''''''''''''''''''''       y = y + 1       objRecordSet.MoveNext     Loop    objCommand.CommandText = _      "SELECT * FROM 'LDAP://DC=movavi,DC=srv' WHERE objectCategory='group'"    Set objRecordSet = objCommand.Execute      objRecordSet.MoveFirst     Do Until objRecordSet.EOF          strADsPathGroup = objRecordSet.Fields("ADsPath").Value        'wScript.echo strADsPathGroup         Set objGroup = GetObject(strADsPathGroup)               'if objGroup.groupType = "-2147483646" then        objExcel.Cells(y,1) = objGroup.sAMAccountName        'Wscript.Echo objUser.sAMAccountName          intGroupSID = fnGet_HexString(objGroup.ObjectSID)                    objExcel.Cells(y,2) = intGroupSID          'Wscript.Echo intUserSID        'End if        y = y + 1        objRecordSet.MoveNext     Loop    objRecordSet.Close  objConnection.Close    SET objSheet = NOTHING  SET objWB =  NOTHING  objExcelApp.Quit()  SET objExcelApp = NOTHING    Wscript.echo "Script Finished..."      ''' Function stringlist(memberOf)        Dim objmemberOf      ' Heart of the script, extract a list of Groups from memberOf objmemberOf  = objUser.GetEx("memberOf") For Each objGroup in objmemberOf    strList = strList & """" & objGroup & """" & " " Next stringlist = strUser & strList 'WScript.Echo "Groups for " & strUser & strList End Function  '''    Function fnGet_HexString(intSID)        Dim strRet, i, b        strRet = ""           For i = 0 to Ubound(intSID)             b = hex(ascb(midb(intSID,i+1,1)))             If( len(b) = 1 ) then b = "0" & b                 strRet = strRet & b        Next              fnGet_HexString = fnHexStrToDecStr(strRet)      End Function    Function fnHexStrToDecStr(strSid)      Dim arrbytSid, lngTemp, j     ReDim arrbytSid(Len(strSid)/2 - 1)       For j = 0 To UBound(arrbytSid)          arrbytSid(j) = CInt("&H" & Mid(strSid, 2*j + 1, 2))     Next       fnHexStrToDecStr = "S-" & arrbytSid(0) & "-" & arrbytSid(1) & "-" & arrbytSid(8)     lngTemp = arrbytSid(15)     lngTemp = lngTemp * 256 + arrbytSid(14)     lngTemp = lngTemp * 256 + arrbytSid(13)     lngTemp = lngTemp * 256 + arrbytSid(12)       fnHexStrToDecStr = fnHexStrToDecStr & "-" & CStr(lngTemp)       lngTemp = arrbytSid(19)     lngTemp = lngTemp * 256 + arrbytSid(18)     lngTemp = lngTemp * 256 + arrbytSid(17)     lngTemp = lngTemp * 256 + arrbytSid(16)       fnHexStrToDecStr = fnHexStrToDecStr & "-" & CStr(lngTemp)       lngTemp = arrbytSid(23)     lngTemp = lngTemp * 256 + arrbytSid(22)     lngTemp = lngTemp * 256 + arrbytSid(21)     lngTemp = lngTemp * 256 + arrbytSid(20)       fnHexStrToDecStr = fnHexStrToDecStr & "-" & CStr(lngTemp)       lngTemp = arrbytSid(25)     lngTemp = lngTemp * 256 + arrbytSid(24)       fnHexStrToDecStr = fnHexStrToDecStr & "-" & CStr(lngTemp)   End Function   

При запуске откроется окно Office Excel, с открытым файлом Users-Groups-SIDs.xlsx и в него будут построчно записываться данные (желательно мышку не трогать, никуда не нажимать пока работа не закончится, иначе могут быть ошибки в получении данных). После окончания работы скрипта получим уведомление, Script Finished… которое означает его завершение, теперь подождём несколько секунд что бы скрипт освободил таблицу, на это мы получим уведомление от офиса, что таблица доступна для записи, соглашаемся и жмём кнопку «сохранить». На выходе у нас получилась таблица скрин таблицы где есть столбцы: sn; givenName; initials; description; codePage; sAMAccountName; codePage; mail; ObjectSID; userPrincipalName; displayName; distinguishedName; memberOf; physicalDeliveryOfficeName; telephoneNumber; profilePath; scriptPath; homeDirectory; homeDrive; title; department; company; manager; homePhone; pager; mobile; facsimileTelephoneNumber; ipphone; info; streetAddress; postOfficeBox; l; st; c; wWWHomePage

Большинство из них нам не нужны (можно поправить в самом скрипте, удалив ненужные параметры, но на первое время пусть будут все).
Из полученных значений сейчас нам понадобятся столбцы sAMAccountName и ObjectSID, сортируем ObjectSID в порядке возрастания (от А до Я) копируем их сохраняя в текстовый файл users.txt, немного изменив их вид, должно поучится так: sAMAccountName,ObjectSID
Т.е. вот так:

dns-gw-sult,S-1-5-21-833212901-2941102506-3986841923-1101 DnsAdmins,S-1-5-21-833212901-2941102506-3986841923-1102 IIS_IUSRS,S-1-5-21-833212901-2941102506-3986841923-1102 DnsUpdateProxy,S-1-5-21-833212901-2941102506-3986841923-1103 ivanov,S-1-5-21-833212901-2941102506-3986841923-1105 ozonov,S-1-5-21-833212901-2941102506-3986841923-1108 elina,S-1-5-21-833212901-2941102506-3986841923-1111 anna,S-1-5-21-833212901-2941102506-3986841923-1113 dash,S-1-5-21-833212901-2941102506-3986841923-1115 denis,S-1-5-21-833212901-2941102506-3986841923-1116 danuev,S-1-5-21-833212901-2941102506-3986841923-1119 

Как видим, каждая запись в отдельной строчке, можно увидеть что последние номера идут по порядку, но есть и отсутствующие интервалы (поскольку самба писала пользователей в LDAP с разными RID (т.е. номерами по порядку), а AD будет создавать пользователей подряд, начиная с какого-то определенного RID, нужно создать всех недостающих пользователей и как-то более или менее понятно их обозвать), вот мы их и заполним, используя perl скрипт script-add user.pl:

script-add user.pl

use strict; use warnings; use Data::Dumper;  my (%input, %output,$max);  my $input_file = "users.txt"; my $output_file = "output.txt"; my $sambaSID = "S-1-5-21-833212901-2941102506-3986841923-";  open FIN, "<$input_file"; while (<FIN>) { 	 chomp(); 	 if (/(.*),$sambaSID(.*)/) { 		 $input{$2}=$1; 		 $max=$2 			if $2 > $max; 	 }	  } close FIN;  print Dumper(\%input); print Dumper($max);  open FOUT, ">$output_file"; for (my $i=1001;$i<=$max;$i++) { 	 if (exists $input{$i}) { 		  print "input: $input{$i} i: $i\n"; 		  print FOUT "$input{$i}\n";      } else {           print FOUT "user$i\n";      } } close FOUT;  

В изначальной статье есть не очень рабочие моменты, поэтому попросил знакомого исправить ошибки, по крайней мере скрипт заработал как планировалось.
И на выходе получили файл output.txt (в котором пользователи будут начинаться с RID 1001 и далее и иметь имена user1001 и т.д.) с содержанием:

output.txt

user1001 user1002 user1003 user1004 user1005 user1006 user1007 user1008 user1009 user1010 user1011 user1012 user1013 user1014 user1015 user1016 user1017 user1018 user1019 user1020 user1021 user1022 user1023 user1024 user1025 user1026 user1027 user1028 user1029 user1030 user1031 user1032 user1033 user1034 user1035 user1036 user1037 user1038 user1039 user1040 user1041 user1042 user1043 user1044 user1045 user1046 user1047 user1048 user1049 user1050 user1051 user1052 user1053 user1054 user1055 user1056 user1057 user1058 user1059 user1060 user1061 user1062 user1063 user1064 user1065 user1066 user1067 user1068 user1069 user1070 user1071 user1072 user1073 user1074 user1075 user1076 user1077 user1078 user1079 user1080 user1081 user1082 user1083 user1084 user1085 user1086 user1087 user1088 user1089 user1090 user1091 user1092 user1093 user1094 user1095 user1096 user1097 user1098 user1099 user1100 dns-gw-sult IIS_IUSRS DnsUpdateProxy user1104 ivanov user1106 user1107 ozonov user1109 user1110 elina user1112 anna user1114 dash denis user1117 user1118 danuev 

Полученный список помещаем в таблицу dsadd-new.xls пришлось немного изменить под свои нужды. В изменённую таблицу помещаем в столбец Login. Начальные SID пользователей в столбец SID и проверяем, у пользователя user-1101 должен быть S-1-5-21-833212901-2941102506-3986841923-1101 у пользователя user-1102 такой S-1-5-21-833212901-2941102506-3986841923-1102 (думаю что логика работы понятна, а SID известных пользователей должен оставаться таким же как и был, у нас есть в файле Users-Groups-SIDs.xlsx). Поместить пользователя в группу, в какой он раньше был, то для этого надо из файла Users-Groups-SIDs.xlsx взять столбец memberOf и поместить каждому пользователю в файл dsadd-new.xls в столбец GROUP. Так же необходимо заполнить все остальные поля, если надо, но обязательно нажо заполнить столбцы Фамилия и Имя, если этого не сделать, то формирование команд на создание будет некорректным. Столбец Login автоматически сформирует имя пользователя для входа в домен, если Вас не устроит смена логина, измените шаблон или же напишите сами необходимый логин (у меня некоторые пользователи имели не такой как принято логин, поэтому у них заменял на необходимый).
Так же прошу обратить внимание на тот факт, что если:
— столбец Отчество не будет заполнено, то при создании пользователей к ним добавится лишний пробел вконце, что может повлечь к проблемам у некоторых программ;
— автоматическое создание группы у меня так и не получилось допилить, поэтому создадим группы при помощи крипта, который создаст их в OU-Users, но это может повлечь к неработоспособности некоторых авторизаций в различных сервисах (Apache, OpenVPN, etc.), т.к. у меня были ещё и OU-Builtin и OU-Groups, но решил пока сложить всё в кучу.

add_group.cmd

rem Создание групп (CN) в CN Users rem dsadd group "cn=,cn=users,dc=mvi,dc=srv"  for %%A in ( jira-users, jira-administrators, Developers, jira-developers, mvi-users, berry-dev, online-developers, marketing-users, Marketing, ne-users, ne-developers, ne-admin, marketing-administrators, online-users, bills,QA, ) do dsadd group "cn=%%A,cn=users,dc=mvi,dc=srv"  rem Создание подразделения (OU) в корне rem dsadd group "cn=,cn=groups,dc=mvi,dc=srv"  dsadd ou "ou=Groups,dc=movavi,dc=srv" for %%B in ( vpn-users, svn-users, jenkins-adm, jenkins, PHP_Developers, amazon-users, ) do dsadd group "cn=%%B,ou=groups,dc=mvi,dc=srv" 

После выполнения рутинных операций по копипасту и проверке, можно подготовить bat файлы которые помогут создать пользователей и команды по добавлению их в группы. В столбцах: ИТОГО и ИТОГО в группу находятся команды для создания пользователей и команды по добавлению их в группы соответственно, их содержимое и сохраним в bat файлы add_user.cmd, add_group.cmd.
Формирование пользователей и групп готово.

Теперь займёмся Windows Server. На текущий момент система должна быть установлена. Нам понадобится утилита newsid. Возьмём SID из списка пользователей, это S-1-5-21-833212901-2941102506-3986841923 (из полученного SID пользователя удалим последние знаки, вплоть до "-" и получим SID домена), и заменим текущий SID системы на наш. Система начнёт изменения и перезагрузится (после перезагрузки можно ещё раз запустить утилиту и посмотреть что SID изменился). Всё, можно устанавливать службу ActiveDirectory (не знаю кто как, но я сначала ставлю DNS, пропуская его настройки, потом при помощи dcpromo настраиваю саму службу домена), но перед этим надо либо выключить сервер SAMBA и bind, либо выключить эти службы (мне пришлось выключить службы, т.к. это ещё и шлюз в интернет руки бы оторвал такому админу). Не знаю, можно ли указать другое название домена (ничто не мешает), но мне надо было оставить текущее. Производим необходимые настройки и перезагружаем систему (всё как обычно). Теперь необходимо установить модуль для расширения отображаемых свойств объекта acctinfo (по ссылке выше, откуда скачивать, написано как устанавливать), запускаем оснастку «Active Directory — пользователи и компьютеры». Пытаемся создать одного пользователя из первой строки скрипта add_user.cmd

dsadd User "cn=user-1101 user-1101 ,cn=users,dc=movavi,dc=srv" -UPN dns-gw-tauconsult@movavi.srv -samid dns-gw-tauconsult -display "user-1101 user-1101 " -fn user-1101  -ln user-1101 -pwd "Passw0rD" -mustchpwd yes -disabled no -pwdneverexpires yes 

Смотрим в «Active Directory — пользователи и компьютеры» какой у него SID в свойствах, во вкладке Additional Account Info. Если имя пользователя соответствует с его окончанием SID, то всё правильно и не требует коррекции (если SID не соответствует, то начинаем с пользователя со следующим номером SID, т.е. к текущему прибавляем 1 ). На этом месте у меня произошёл epic fail с одним пользователем, у него SID содержал 1105, а пользователи у меня начали создаваться с 1106, поэтому пришлось долго мучатся и переносить все данные пользователя, т.к. его идентификатор был неверным.
После выяснения порядка, с которого создаются пользователи необходимо скорректировать создание пользователей, удалив/закомментировав команды, которые уже не соответствуют, т.е. убираем всё, что находится до user-1107 и можно смело запускать скрипт add_user.cmd. После выполнения надо создать группы, иначе ничего не выйдет. Запускаем add_group.cmd проверяем, все ли группы создались (стоит обратить внимание на то, что создаются глобальные группы безопасности, если необходимы другие типы, то надо добавить в скрипт -scope {l | g | u}, читаем мануал по dsadd group).

dsadd group /?

Описание.  Добавление группы в каталог. Синтаксис: dsadd group <GroupDN> [-secgrp {yes | no}] [-scope {l | g | u}]            [-samid <SAMName>] [-desc <Description>] [-memberof <Group ...>]            [-members <Member ...>] [{-s <Server> | -d <Domain>}] [-u <UserName>]            [-p {<Password> | *}] [-q] [{-uc | -uco | -uci}] Параметры  Значение                Описание <DN_группы>             Обязательное или stdin. Различающееся имя (DN)                          добавляемой группы. Если конечный объект не указан,                          он берется из стандартного ввода (stdin). -secgrp {yes | no}      Указывает, что группа является (yes) или не является                         (no) группой безопасности. По умолчанию: yes. -scope {l | g | u}      Указывает, что область действия группы является                         локальной (l), глобальной (g) или универсальной (u).                         Если домен находится в смешанном режиме, универсальная                         область не поддерживается. По умолчанию: глобальная. -samid <имя_SAM>        Задает имя учетной записи SAM группы <имя_SAM>                         (например, "operators"). -desc <описание>        Задает описание группы <описание>. -memberof <группа...>   Делает группу членом одной или нескольких групп,                         определяемых разделяемым пробелами списком имен DN                          <группа...>. -members <член...>      Добавляет одного или несколько членов в группу. Члены                         задаются разделяемым пробелами списком имен <член...>. {-s <сервер> | -d <домен>}                         -s <сервер> задает подключение к контроллеру домена                         (КД) с именем <сервер>.                         -d <домен> задает подключение к КД в домене.                         По умолчанию: КД в домене входа. -u <пользователь>       Подключение с именем <пользователь>. По умолчанию:                         вошедший пользователь. Формат имени: имя,                         домен\имя или имя участника-пользователя (UPN). -p {<пароль> | *}       Пароль пользователя <пользователь>. Если задана *,                         выводится приглашение ввести пароль. -q                      Тихий режим: подавление всего вывода для                          стандартного вывода. {-uc | -uco | -uci}     -uc Задает вход с канала или вывод на канал                          в формате Юникод.                          -uco Задает вывод на канал или в файл                          в формате Юникод.                          -uci Задает вход с канала или из файла                         в формате Юникод.  Примечание. Если целевой объект не указан в командной строке, этот целевой объект будет  получен через стандартный ввод (STDIN). Данные STDIN могут быть приняты с  клавиатуры, перенаправлены из файла или из другой команды. Чтобы обозначить  конец ввода данных STDIN с клавиатуры или перенаправленного файла, используйте  CTRL+Z или символ конца файла (EOF).  Если указываемое значение содержит пробелы, заключите его в кавычки (например,  "CN=Ivan Ivanov,CN=Users,DC=microsoft,DC=com"). Если вы указываете несколько значений, разделите их пробелами (например, список  различающихся имен группы).  См. также: dsadd computer /? - сведения по добавлению компьютера в каталог. dsadd contact /? - сведения по добавлению контакта в каталог. dsadd group /? - сведения по добавлению группы в каталог. dsadd ou /? - сведения по добавлению подразделения в каталог. dsadd user /? - сведения по добавлению пользователя в каталог. dsadd quota /? - сведения по добавлению квоты в каталог.  Дополнительные сведения по программам командной строки службы каталогов: dsadd /? - сведения по добавлению объектов. dsget /? - сведения по отображению объектов. dsmod /? - сведения по изменению объектов. dsmove /? - сведения по перемещению объектов. dsquery /? - сведения по поиску объектов, отвечающих заданным условиям. dsrm /? - сведения по удалению объектов. 

После проверки, запускаем скрипт, который добавит пользователей в группы add_user_group.cmd (обращаю внимание что могут быть различия, проверяйте где были созданы группы и какие указаны у пользователя).

Вот и закончили миграцию, теперь у нас ActiveDirectory вместо SAMBA, пользователи имеют те же идентификаторы, осталось ввести в домен сами компьютеры и задача будет завершена, придётся только допиливать некоторые моменты, которые индивидуальны у каждого.

Текст, написанный мной, как обычно сумбурен, хаотичен, содержит ошибки, возможно неточности и др.

Все скрипты в одном месте (с примерами).

P.S.
Надеюсь все помнять что cmd, в данном случае, надо сохранять используя OEM кодировку?

ссылка на оригинал статьи http://habrahabr.ru/post/173985/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *