суббота, 19 июня 2010 г.

Миграция портала SharePoint Services в домен Active Directory

Предупреждение.

Всё нижеследующее вы можете использовать на свой страх и риск! Автор не несёт никакой ответственности за возможные риски: утерю информации, некорректное функционирование программного обеспечения и т.п.

Постановка задачи.

Имеется веб-портал SharePoint Services 3.0, аутентификация пользователей в котором происходит по локальным учётным записям на самом сервере. Требуется ввести сервер в домен Active Directory и использовать для аутентификации на портале учётные записи из домена.

1. Получение информации об имеющихся на портале учётных записях

Запускаем MS SQL Enterprise Manager, открываем базу данных WSS_Content. Информация о пользователях находится в таблице UserInfo. Запускаем из Enterprise Manager'а инструмент SQL Query Analyzer, в которм выполняем следующий запрос:
select tp_SystemID, tp_Login, tp_Title
from UserInfo;
Полученную информацию копируем в таблицу Excel. В выбранных колонках находятся: SID пользователя, логин пользователя вместе с доменом в виде "ДОМЕН\Логин", строка описания пользователя. SID пользователя в базе данных хранится в двоичном виде, а Query Analyzer выводит его в шестнадцатеричном виде.

После этого в таблице должны иметься следующие столбцы:
A - шестнадцатеричный SID локальной учётной записи,
B - логин локальной учётной записи,
C - описание локальной учётной записи.

2. Добавляем в полученный список столбец с учётными записями пользователей из домена

Каждой учётной записи, используемой для входа на портал, нужно сопоставить учётную запись из домена. Возможно, на этом этапе понадобится завести в домене учётные записи недостающих пользователей.

После этого в таблице должны иметься следующие столбцы:
A - шестнадцатеричный SID локальной учётной записи,
B - логин локальной учётной записи,
C - описание локальной учётной записи,
D - логин доменной учётной записи.

3. Получение SID пользователей из домена

Берём программу user2sid Евгения Рудного по ссылке http://evgenii.rudnyi.ru/programming.html#sid2user. Копируем столбец D в текстовый файл, а в начале каждой строки добавляем команду user2sid. Копируем получившийся bat-файл в каталог с программой. Запускаем bat-файл, перенаправив вывод из него в другой текстовый файл. В полученном текстовом файле убираем всё, кроме SID'ов, так чтобы в каждой строчке файла было по одному SID'у. Информацию из текстового файла добавляем в столбец E файла Excel.

После этого в таблице должны иметься следующие столбцы:
A - шестнадцатеричный SID локальной учётной записи,
B - логин локальной учётной записи,
C - описание локальной учётной записи,
D - логин доменной учётной записи,
E - SID доменной учётной записи.

Будьте внимательны! В списке должны присутствовать ровно столько SID'ов пользователей, сколько их имелось в первоначальном столбце. Также они должны следовать точно в таком же порядке, как в Excel-файле. Если что-то идёт не так, нужно поправить Excel-таблицу и bat-файл и повторить этот этап снова.

4. Конвертирование доменных SID в шестнадцатеричный вид

Для понимания того, каким образом SID хранится в двоичном виде в системе, можно воспользоваться следующей статьёй: Microsoft Security Descriptor(SID)Attributes.

Пишем на PHP сценарий для конвертирования SID:
<?

  function swap($hexdword)
  {
    $res = "";
    for($i = 6; $i >= 0 ; $i -= 2)
      $res .= $hexdword[$i] . $hexdword[$i+1];
    return $res;
  }

  function sid2hex($sid)
  {
    $sidl = explode("-", $sid);

    $hex_sid = "0x" .
      sprintf("%02X", $sidl[1]+0) .
      sprintf("%02X", $sidl[2]+0) .
      "000000000005";

    for($i = 0; $i < $sidl[2]; $i++)
      $hex_sid .= swap(sprintf("%08X", $sidl[3+$i]+0));

    return $hex_sid;
  }

  function hex2sid($hex_sid)
  {
    $hex_sid = trim($hex_sid);
    $sidl[0] = "S";
    $sidl[1] = hexdec(substr($hex_sid, 2, 2));

    $sidl[2] = hexdec(substr($hex_sid, 4, 2));

    for($i = 0; $i < $sidl[2]; $i++)
      $sidl[3+$i] = hexdec(swap(substr($hex_sid, 18+$i*8, 8)));

    $sid = implode("-", $sidl);

    return $sid;
  }

  $sids = "S-1-5-21-583367659-4273102991-479599032-4176
S-1-5-21-583367659-4273102991-479599032-4450";

  $sidsl = explode("\n", $sids);

  foreach ($sidsl as $sid)
    echo sid2hex($sid) . "<br>";
?>
Для того, чтобы быть уверенным в правильности написанных функций, я написал их пару. Проверив, что преобразование SID в шестнадцатеричный вид и обратно, не искажает его, я удостоверился, что функции написаны правильно.

В вышеприведённом сценарии я оставил только пару SID'ов. Вам следует вставить на место этих двух SID'ов содержимое столбца E. Полученный после выполнения сценария результат нужно скопировать в столбец F.

После этого в таблице должны иметься следующие столбцы:
A - шестнадцатеричный SID локальной учётной записи,
B - логин локальной учётной записи,
C - описание локальной учётной записи,
D - логин доменной учётной записи,
E - SID доменной учётной записи,
F - шестнадцатеричный SID доменной учётной записи.

5. Получение SQL-запросов для миграции портала на доменные учётные записи

Забегая вперёд, я хочу сказать, что обнаружил в базе данных ещё одну таблицу, в которой упоминаются логины пользователей. Эти логины не используются для проверки входа пользователей, они используются лишь для вывода информации о пользователе. Можно не менять их - в конце концов мы занимаемся не вполне чистым делом, а грязным хаком, а можно поменять, чтобы несоответствие реального логина и информации о логине не бросалось в глаза. итак, эта информация находилась в таблице AllUserData в поле nvarchar3.

Теперь мы можем написать в Excel'е формулы, которые сгенерируют нам SQL-запросы, необходимые для внесения необходимых изменений в БД SharePoint Services.
G ="update UserInfo set tp_Login = '" & D2 &"', tp_SystemID = " & F2 & " where tp_Login = '" & B2 &"' "

H ="update AllUserData set nvarchar3 = '" & D2 &"' where nvarchar3 = '" & B2 &"' "
Чтобы не бросаться голой задницей на амбразуру, подготовим SQL-запросы для отката навороченного:
I ="update UserInfo set tp_Login = '" & B2 &"', tp_SystemID = " & A2 & " where tp_Login = '" & D2 &"' "

J ="update AllUserData set nvarchar3 = '" & B2 &"' where nvarchar3 = '" & D2 &"' "
После этого в таблице должны иметься следующие столбцы:
A - шестнадцатеричный SID локальной учётной записи,
B - логин локальной учётной записи,
C - описание локальной учётной записи,
D - логин доменной учётной записи,
E - SID доменной учётной записи,
F - шестнадцатеричный SID доменной учётной записи,
G - SQL-запрос для обновления информации в таблице UserInfo,
H - SQL-запрос для обновления информации в таблице AllUserData,
I - SQL-запрос для отката обновления таблицы UserInfo,
J - SQL-запрос для отката обновления таблицы AllUserData.

Создаём два текстовых файла - update.txt и rollback.txt. В первый копируем столбцы G и H, а во второй - I и J.

6. Миграция

Для миграции нужно выделить перерыв обслуживания примерно в 15 минут. За это время можно будет выполнить SQL-запросы из файла update.txt и ввести компьютер в домен. На случай если что-то пойдёт не так, лучше увеличить паузу до 30 минут, чтобы вывести компьютер из домена и выполнить SQL-запросы из файла rollback.txt.

На случай же если же что-то пойдёт совсем не так, следует заготовить резервную копию базы данных. Перед снятием резервной копии нужно перевести базу данных портала в режим "Только чтение" через "Центр администрирования" SharePoint Services, вкладку "Управление приложениями", пункт "Квоты и блокировки семейства узлов", а затем снять резервную копию средствами MS SQL Enterprise Manager. После восстановления следует не забыть восстановить режим "Нет блокировки".

Я выполнял обновление из дома, глубоким вечером, разумеется проработав в рабочее время сценарий миграции и текстовые файлы с SQL-запросами.

7. Кое-что ещё

Дополнительно, я поменял учётную запись администратора SharePoint Services в "Центре администрирования" на доменную. Поменял я её в базе данных SharePoint_Admincontent_многобукв в таблицах AllUserData и UserInfo. Это уже не столь критично и не столь сложно. Администраторов мало и они, в отличие от пользователей, могут выдержать и больший перерыв в обслуживании.

9 комментариев:

shikovan комментирует...

А на практике, с готовым и настроенным решением, с не которым объемом данных, это получалось?

morbo комментирует...

А вы думаете я чисто умозрительный эксперимент сделал?

Конечно получалось, и не раз, а два раза. Первый раз локальные учётные записи заменил на доменные. Второй раз переехал с одного домена на другой.

shikovan комментирует...

Ну да, я про эксперемент и спрашивал.
М.б. в стерильных условиях прокатило а в боевых условиях нет. Спасибо.

Анонимный комментирует...

А если перед тем как вогнать в домен Shrepoint перевел все учетные записи с помощь ADMT? нужно ли всё это проделывать? Так же я этот сервер перенес бы с помощью ADMT

morbo комментирует...

ADMT может не знать о том, что идентификаторы учётных записей, используемые Sharepoint хранятся в базе данных MS SQL. Поэтому, наверняка придётся это проделывать.

Анонимный комментирует...

А если в роли сервисной учетной записи используется Local system, это никак не облегчает жизнь?)

morbo комментирует...

Дело не в том, под какой учётной записью работают сервисы Sharepoint, а в том что Sharepoint проверяет права доступа к разделам портала по логинам и SID'ам пользователей.

Советую собрать стенд, на котором попробовать поставить Sharepoint, восстановить ваш портал, провести миграцию, а потом проверить, всё ли работает как должно. В случае обнаружения проблем искать метод их устранения и опробовать его на тестовом стенде. Окончательный переход делайте тогда, когда уже будет понятно, что нужно сделать, чтобы после перехода всё нормально работало.

Unknown комментирует...

Сделал все по инструкции, в результате выполнения запроса вылетает ошибка
Cannot insert duplicate key row in object 'dbo.UserInfo' with unique index 'UserInfo_SID'.

Как быть?

morbo комментирует...

Игорь, видимо в таблице уже есть учётные записи пользователей с таким идентификатором SID.

Как быть? Можно убрать запрос update, приводящий к этой ошибке. А вообще - я писал в начале статьи уведомление. Если не понимаете, что делаете - не стоит даже начинать. Всё описанное - это очень грубый хак, я не могу дать гарантий, что всё будет работать правильно, даже если вы соблюдёте инструкцию буква в букву.