![image](https://habrastorage.org/getpro/habr/post_images/120/628/711/120628711fe5e6bdb97c6a6d046df408.png)
Вот и пришло время рассказать о способе, который методом научного тыка, нескольких умных людей и несколько часов свободного времени помогли мне мигрировать домен, построенный предыдущим админом с 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=mvi,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=mvi,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=mvi,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, либо выключить эти службы (мне пришлось выключить службы, т.к. это ещё и шлюз в интернет
dsadd User "cn=user-1101 user-1101 ,cn=users,dc=mvi,dc=srv" -UPN dns-gw-sult@mvi.srv -samid dns-gw-sult -display "user-1101 user-1101 " -fn user-1101 -ln user-1101 -pwd "p6Jx3Xre" -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 кодировку?
Об ошибках прошу сообщать для исправления.
Используемый материал: