суббота, 6 апреля 2013 г.

Автоматизация публикации виртуальных машин в UAG 2010.

Microsoft Forefront Unified Access Gateway (UAG) 2010 - очень интересное средство для публикации внутренних ресурсов для внешних (да и внутренних тоже) пользователей. Но вот есть в нем очень серьезный недостаток - нет никаких средств автоматизации процесса управления.

Точнее так, средства автоматизации есть, в виде COM-интерфесов, но они недокументированы и понять, как они работают и какие параметры принимают, мне так и не удалось. Информации на просторах Интернет об этом я также не нашел.
Дополнительные неудобства вызывает и консоль управления UAG, явно выполненная не в майкрософтовском стиле (по известным причинам).

Ну и что с этого, скажете вы? Тогда немного об истоках проблемы.
Есть вычислительная среда, в виде энного количества серверов, включенных в группу управления VMM. В VMM нарезаны облака, на облака делегировано управление для конечных пользователей (разработчиков). В этих облаках разработчики создают виртуальные машины для своих нужд. Виртуальные машины необходимо как-то публиковать для доступа как снаружи так изнутри. На помощь нам приходит UAG - мы публикуем RDP виртуальных машин. Казалось бы, что еще нужно?
Но тут начинаются проблемы. Поскольку среда динамичная и относительно большая, то поддерживать такое решение (с ручной публикацией) становится практически невозможным:
- виртуальные машины постоянно создаются и удаляются (а это нужно актуализировать информацию).
- необходимо постоянно добавлять/удалять пользователей для опубликованных ярлыков UAG.

Вот здесь то мы и встречаемся с неудобной консолью управления UAG и отсутствием автоматизации.

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

Дать доступ к консоли UAG конечным пользователям - не вариант.

После недолгих изучений, было обнаружено, что UAG хранит свою конфигурацию в файле UAG.egf, что находится в common\conf\ в папке установки. Файл текстовый, имеет некоторую иерархическую структуру. В качестве узлов иерархии выступают объекты, такие как, trunk, приложения, политики и т.д.

Теперь нужно было как-то разобрать эту информацию.
Полностью описывать структуры файла не буду, да и таких знаний у меня нет. Меня больше интересовали именно приложения RDP.
Структура файла:
{
     настройки самого UAG (версия схемы, алгоритмы шифрования и т.д.)
     {
      Репозитории аутентификации
       {
       }
      ...
     }
     {
        trunk http (80) и его настройки
      }
      {
         trunk https и его настройки
         {
                опубликованные приложения  <<< то что нам нужно
         }
      }
...........
}

Приложение формируется из набора атрибутов со значениями.
Формат атрибута следующий:

m_<тип><наименование атрибута>_<ID-приложения>[_<податрибуты и коды>]=[<значение>]

В "[]" заключены необязательные данные.
К типам относятся b (boolean), n (numeric), cs (скорее всего case-sensitive string) и т.д. Хотя конечно это не нужная для нас информация, мы ее никак использовать не будем.
Наименование атрибута - думаю понятно что это. Примеры: AppName, AppID, AppAuthList.
У приложения есть целочисленный уникальный идентификатор (ID-приложения). Уникальный скорее всего в пределах транка (Trunk).
Могут быть некоторые податрибуты. Например, для AuthList - это Repository, UserName и т.д.
Если например мы публикуем ярлык для нескольких пользователей, то податрибуты для разных пользователей еще будут различаться по целочисленному уникальному идентификатору.
После знака "равно" идет значение, хотя оно может отсутствовать.

Список атрибутов для ярлыка RDP можно посмотреть здесь - http://pastebin.com/5sz0EV9z
Список атрибутов для конкретного приложения можно получить с помощью вот этого скрипта (Powershell) - http://pastebin.com/WqHP0fx9. На входе путь к файлу UAG и имя приложения, которое отображается в консоли. На выходе список атрибутов со значениями.

В принципе достаточно повторить все эти атрибуты, добавить в секцию приложений конкретного транка, сохранить и активировать конфигурацию. В итоге нужный ярлык с приложением должен появиться на UAG-портале.
Но не все так просто. У приложения есть идентификатор вот в таком виде:
m_csAppId_24=7F81AB0527A443BEBA2018216CBCD110.
Когда изначально разбирался в структуре файла, я думал что это некий хэш MD5. Тем более очень похож, 128 бит. Пробовал его прикладывать к имени приложения, но результат отрицательный. К чему бы еще приложить - не нашел. Сейчас пишу эту статью и в голову пришла мысль, что это обычный GUID, без знаков "-" и фигурных скобок. Возможно в ближайшее время эта информация подтвердится.

Дополнительно нужно было убедиться, что в файле не меняются другие атрибуты или если меняются то какие и где.
Для этого написал небольшой скрипт для получения различий между двумя файлами (сначала поискал готовые утилиты, но они меня не удовлетворили, а тратить время на дальнейший поиск и изучение еще и этого вопроса вовсе не хотелось). Скрипт можно взять здесь - http://pastebin.com/3w5hp4Wz. На входе пути файлов Source и Dest.
Как выяснилось изменяются еще ряд атрибутов, такие как количество приложений в транке.

Все это я учел и начал вручную публиковать RDP-ярлык.
То есть, я беру и повторяю список атрибутов для приложения "Personal RDP", изменяю некоторые атрибуты (имена, ID и т.д.), изменяю дополнительные атрибуты и активирую конфигурацию. Мое приложение на портале не появляется и конфигурация откатывается на изначальную. Ошибок почему-то в мониторе активации нет.

Тогда я иду другим способом. Создаю некоторое приложение через консоль UAG. Далее изменяю нужные мне атрибуты уже через текстовый файл: имя, отображаемое имя, внутренний IP-адрес, список авторизации и т.д.. Применяю конфигурацию. Работает!
Ну собственно таким способом и решил действовать.

В результате родился следующий скрипт (Powershell): http://pastebin.com/jW9S0qaC
Список функций:

  • Set-UAGConfig - устанавливает глобальные переменные с расположением файла-egf, расположением каталога для бэкапов файлов-egf и путь к активатору (об этом немного позже). По умолчанию, в качестве $SourceFile берется UAG.egf из каталога установки UAG. $BackupDir - c:\EGFBackups (если не существует, то происходит попытка создания каталога). $UagActivator - по умолчанию C:\Scripts\UAGActivate.exe.
  • Backup-UAGConfig - копирует egf-файл из $SourceFile в $BackupDir с добавлением временной метки (для уникальности)
  • Disable-UAGApp - отключает опубликованное приложением, переименовывает в Unused<ID> и очищает некоторые атрибуты. Например, если вы хотите удалить приложение, то вызываете эту функцию.
  • Enable-UAGApp - включает приложение. Эта функция находит доступное отключенное приложением (с именем Unused<ID>), включает его и присваивает ему атрибуты. Какие? Имя приложения, имя приложения на портале, в ShortDescription попадает ID облака (это конкретно моя реализация), в LongDescription попадает GUID виртуальной машины, IP-адрес внутреннего сервера (ВМ). Большинство этих атрибутов устанавливается конкретно под мои задачи. Вам они скорее всего не подойдут. В этом случае необходимо изменить скрипт.
  • Set-UAGApp - обновляет атрибуты для уже опубликованного приложения. В моем случае это имя приложения, имя приложения на портале, ProjectID -> ShortDescription, VMID -> LongDescription и IP-адрес.
  • Get-UAGAuth - возвращает список авторизации для опубликованного приложения (выходной формат описан далее).
  • Set-UAGAuth - устанавливает список авторизации для опубликованного приложения.
  • Get-UAGAppList - возвращает список приложений, опубликованный для конкретного облака по ProjectID (это конкретно под мою задачу).
  • Get-UAGApp - возвращает приложение по VMID или по имени на портале (VMID берется из LongDescription).
  • Activate-UAGConfig - активирует конфигурацию с помощью файла из переменной $UAGActivator.

Как этим пользоваться?

Предварительный этап:
1) Необходимо через UAG консоль создать пул приложений RDP, с произвольными именами (отображение на портале нужно включить, список авторизации пустой), сохраняете конфигурацию (опционально можно ее применить).
2) Далее вы импортируете функции в консоль Powershell:
Import-Module "c:\Scripts\UAGFunctions.ps1"
3) делаете резервную копию файла конфигурации
Backup-UAGConfig
4) Отключаете созданные приложения
Disable-UAGApp -AppName "Имя приложения на портале"
Внимание!!! Обратите внимание, что в моей реализации я работаю с приложениями по имени, отображаемому на портале или по VMID, который я сохраняю в атрибуте LongDescription.
5) Активируете конфигурацию командой
Activate-UAGConfig

Далее 1 шаг - включаем свободное приложение (Unused):

  1. $ErrorActionPreference = "Stop"
  2. Get-PSSession | Remove-PSSession
  3. #Пароль для учетной записи, которая подключается удаленно к UAG
  4. $SecureString = ConvertTo-SecureString -String "\`d.T.~Vb/{41BAA34C-B0E6-41D0-8F2B-CB3901AAF6A8}\`d.T.~Vb/" -AsPlainText-Force
  5. $Creds = New-Object system.Management.Automation.PSCredential("domain\os2uag_connector", $SecureString)
  6. $SessionOpt = New-PSSessionOption -OperationTimeout (30*60*1000)
  7. #Подключаемся к UAG через PSRemoting
  8. $Session = New-PSSession -ComputerName "server-uag01" -Credential $Creds -SessionOption $SessionOpt
  9. #Импортируем модуль с функциями
  10. Invoke-Command -Session $Session -ScriptBlock {Import-Module "c:\Scripts\UAGFunctions.ps1"}
  11. #Устанавливаем глобальные переменные $BackupDir и UAGActivator
  12. Invoke-Command -Session $Session -ScriptBlock {Set-UAGConfig -BackupDir "c:\EGFBackups" -UAGActivatorPath"c:\Scripts\UAGActivate.exe" }
  13. #Бэкапим конфиг
  14. Invoke-Command -Session $Session -ScriptBlock {Backup-UAGConfig}
  15. $ScriptBlock = {
  16.         #Заполняем входную информацию для публикации
  17.         $VMID = "\`d.T.~Ed/{6F6D86D8-3492-4328-B606-7B983F64826F}.VM ID\`d.T.~Ed/"
  18.         $AppName = "\`d.T.~Ed/{6F6D86D8-3492-4328-B606-7B983F64826F}.VM Name\`d.T.~Ed/"
  19.         $ProjectID = [int]("\`d.T.~Ed/{6F6D86D8-3492-4328-B606-7B983F64826F}.Cloud\`d.T.~Ed/".Split("_")[0].Substring(1))
  20.         $IPAddress = "\`d.T.~Ed/{90F45712-817A-4429-A5F0-668F26467CFD}.{08BCA15A-22A5-4770-81E7-1D41E21D1CDA}\`d.T.~Ed/"
  21.        
  22.         #Смотрим опубликовано ли уже приложение по имени
  23.         $App = Get-UAGApp -AppName $AppName
  24.         if ($App){
  25.                 if ($App.VMID -ne ($VMID.ToString().ToUpper().Replace("-",""))){
  26.                         #Если приложение (ВМ) с таким именем опубликовано и VMID различаются, то скорее всего старую ВМ удалили и создали с тем же именем
  27.                         #в этом случае отключаем исходное
  28.                         Disable-UAGApp -AppName $AppName
  29.                         #включаем ярлык, заполняем атрибутами
  30.                         Enable-UAGApp -AppName $AppName -AppGUID $VMID -ProjectID $ProjectID -IpAddr $IPAddress
  31.                 }
  32.                 else{
  33.                         #обновляем информацию о приложении
  34.                         Set-UAGApp -AppName $AppName -IpAddr $IPAddress -ProjectID $ProjectID
  35.                 }
  36.         }
  37.         else{
  38.                 $App = Get-UAGApp -VMID $VMID
  39.                 if (-not $App){
  40.                         #Включаем Unused приложение
  41.                         Enable-UAGApp -AppName $AppName -AppGUID $VMID -ProjectID $ProjectID -IpAddr $IPAddress
  42.                 }
  43.                 else {
  44.                         #обновляем информацию о приложении
  45.                         Set-UAGApp -AppName ($App.AppPortalName) -AppGUID $VMID -IpAddr $IPAddress -NewAppName $AppName-ProjectID $ProjectID
  46.                 }
  47.         }
  48. }
  49. #выполняем скрипт-блок, сформированный ранее
  50. Invoke-Command -Session $Session -ScriptBlock $ScriptBlock
  51. $Session | Remove-PSSession


Шаг 2 - настраиваем авторизацию:

  1. $ErrorActionPreference = "Stop"
  2. $SecureString = ConvertTo-SecureString -String "\`d.T.~Vb/{41BAA34C-B0E6-41D0-8F2B-CB3901AAF6A8}\`d.T.~Vb/" -AsPlainText-Force
  3. $Creds = New-Object system.Management.Automation.PSCredential("domain\os2uag_connector", $SecureString)
  4. $SessionOpt = New-PSSessionOption -OperationTimeout (30*60*1000)
  5. $Session = New-PSSession -ComputerName "server-uag01" -Credential $Creds -SessionOption $SessionOpt
  6. Invoke-Command -Session $Session -ScriptBlock {Import-Module "c:\Scripts\UAGFunctions.ps1"}
  7. Invoke-Command -Session $Session -ScriptBlock {Set-UAGConfig -BackupDir "c:\EGFBackups" -UAGActivatorPath"c:\Scripts\UAGActivate.exe" }
  8. $ScriptBlock = {
  9.         #Имя приложения, отображаемое на портале
  10.         $AppName = "\`d.T.~Ed/{6F6D86D8-3492-4328-B606-7B983F64826F}.VM Name\`d.T.~Ed/"
  11.         #DN пользователя в AD (я использую Active Directory Repository, для других репозиториев эти атрибуты могут отличаться)
  12.         $UserDN = "\`d.T.~Ed/{D6BCE08A-3CA4-4480-9482-4635B5083573}.Distinguished Name\`d.T.~Ed/"
  13.         $UserCanArr = @($UserDN.split(",") | where {$_ -notlike "dc=*"} | foreach {$_.Substring(3)})
  14.         $UserCanArr = @(for($i=($UserCanArr.Count-1);$i -ge 0$i--) { $UserCanArr[$i] })
  15.         #User Canonical String без домена (это тоже особенность Active Directory Repository в UAG)
  16.         $UserCanStr = [string]::Join('\', $UserCanArr)
  17.         #Получаю список авторизации для приложения
  18.         $authlist = Get-UAGAuth -AppName $AppName
  19.         #Создаю новый объект
  20.         $userObj = New-Object System.Object
  21.         #DN
  22.         $userObj | Add-Member -Name ID -MemberType NoteProperty -Value $UserDN
  23.         #Тип - 1 (пользователь)
  24.         $userObj | Add-Member -Name IsUser -MemberType NoteProperty -Value "1"
  25.         #Это имя репозитория (не имя домена, а именно репозитория аутентификации)
  26.         $userObj | Add-Member -Name Repository -MemberType NoteProperty -Value "domain.com"
  27.         #User Canonical name
  28.         $userObj | Add-Member -Name UserName -MemberType NoteProperty -Value $UserCanStr
  29.         #Разрешить - 1 (для блокирования видимо 0)
  30.         $userObj | Add-Member -Name Allow -MemberType NoteProperty -Value "1"
  31.         #добавляю к исходному списку (hash-таблица, в которой в качестве ключа используется DN) нового пользователя
  32.         $authlist[$userObj.ID] = $userObj
  33.         #Устанавливаю список авторизации для приложения
  34.         Set-UAGAuth -AppName $AppName -UserArray $authlist
  35. }
  36. #Выполняю скрипт-блок, подготовленный ранее
  37. Invoke-Command -Session $Session -ScriptBlock $ScriptBlock
  38. #Здесь можно активировать, но в моем случае активация конфигурации происходит чуть позже
  39. #Invoke-Command -Session $Session -ScriptBlock { Activate-UAGConfig }
  40. $Session | Remove-PSSession


Теперь завершающий этап - активация:

  1. $ErrorActionPreference = "Stop"
  2. $SecureString = ConvertTo-SecureString -String "\`d.T.~Vb/{41BAA34C-B0E6-41D0-8F2B-CB3901AAF6A8}\`d.T.~Vb/" -AsPlainText-Force
  3. $Creds = New-Object system.Management.Automation.PSCredential("domain\os2uag_connector", $SecureString)
  4. $SessionOpt = New-PSSessionOption -OperationTimeout (30*60*1000)
  5. $Session = New-PSSession -ComputerName "server-uag01" -Credential $Creds -SessionOption $SessionOpt
  6. Invoke-Command -Session $Session -ScriptBlock {Import-Module "c:\Scripts\UAGFunctions.ps1"}
  7. Invoke-Command -Session $Session -ScriptBlock {Set-UAGConfig -BackupDir "c:\EGFBackups" -UAGActivatorPath"c:\Scripts\UAGActivate.exe" }
  8. Invoke-Command -Session $Session -ScriptBlock { Activate-UAGConfig }
  9. $Session | Remove-PSSession


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

Что за UAGActivate.exe?
Это скрипт Autoit, скомпилированный в .exe файл. Он запускает ConfigMgrUtil.exe для активации конфигурации UAG и посылает ему нажатия клавиш. Почему-то ConfigMgrUtil.exe отказался, в моем случае, принимать команды через поток StdIn, пришлось извращаться и делать через autoit.

Скрипт autoit:

Run("C:\Program Files\Microsoft Forefront Unified Access Gateway\utils\ConfigMgr\ConfigMgrUtil.exe -a")
WinWait("C:\Program Files\Microsoft Forefront Unified Access Gateway\utils\ConfigMgr\ConfigMgrUtil.exe")
ControlSend("C:\Program Files\Microsoft Forefront Unified Access Gateway\utils\ConfigMgr\ConfigMgrUtil.exe", "", "", "{ENTER}")
ControlSend("C:\Program Files\Microsoft Forefront Unified Access Gateway\utils\ConfigMgr\ConfigMgrUtil.exe", "", "", "{ENTER}")
ControlSend("C:\Program Files\Microsoft Forefront Unified Access Gateway\utils\ConfigMgr\ConfigMgrUtil.exe", "", "", "{ENTER}")

Сам autoit здесь - http://www.autoitscript.com/site/autoit/
Скомпилированный выше скрипт здесь - http://www.filehosting.org/file/details/428697/UAGActivate.exe
SHA-1: b97f3151c3016f5eba54b460c43545d1810e5d21
Этот файл нужно сохранить под оригинальным именем (UAGActivate.exe) в папке c:\Scripts. Либо при выполнении скрипта/модуля с UAG-функциями установить путь к активатору.

У меня реализована схема публикации через Service Manager Portal (здесь пользователи создают заявки), далее отрабатывает System Center 2012 Orchestrator (бывший Opalis).
Runbook в Orchestrator выглядит вот так:
Скрипт с функциями был написан для схемы UAG версии 2010 SP1, но без изменений работает на SP3. Тестировался на протяжении месяца, без каких-либо проблем.

пятница, 5 апреля 2013 г.

Скрипт для визуализации классов в SCOM (SCOM Class Visualizer script) 0.3

[Обновление] Скрипт для визуализации иерархии классов в SCOM.

Добавил отображение связей между классами.
Теперь это выглядит так (немного пришлось переместить фигуры):







Возможности:
1) Разные типы связей отображаются в цвете:
- Hosting - синие пунктирные линии
- Containment - синие сплошные
- Reference - красные пунктирные
- Membership - черные пунктирные
- Связи иерархии классов отображаются как черные сплошные линии.
2) Доступны свойства и атрибуты связей.
3) Возможность отключить формирование связей (если точнее, то они по умолчанию и так отключены).

Осталась проблема отображения и размещения фигур, но есть мысли как это исправить.

Для отображения связей между классами необходимо добавить переключатель -IncludeRelationship.

Теперь список параметров выглядит так:
-Help : вывод справочной информации
-ClassNames <[string[]]> : необходимо указать массив классов
-IncludeRelationship : включить отображение связей между классами

Текст скрипта: http://pastebin.com/nqe8TMaw
Описание: http://gexeg.blogspot.ru/2013/04/scom-scom-class-visualizer-script.html

четверг, 4 апреля 2013 г.

Скрипт для визуализации классов в SCOM (SCOM Class Visualizer script) 0.2

 [Обновление] Скрипт для визуализации иерархии классов в SCOM.
Добавил отображение атрибутов базовых классов.
Теперь это выглядит так:



Текст скрипта: http://pastebin.com/DNFaFHEn

Версия 0.1 и описание: http://gexeg.blogspot.ru/2013/04/scom-scom-class-visualizer-script.html
Версия 0.3: http://gexeg.blogspot.ru/2013/04/scom-scom-class-visualizer-script-03.html

среда, 3 апреля 2013 г.

Скрипт для визуализации классов в SCOM (SCOM Class Visualizer script)

Накидал небольшой скрипт для визуализации иерархии классов в SCOM.
Пока в версии 0.1 есть следующие возможности:
- отрисовка иерархии классов в Visio 2010;
- отрисовка происходит от указанного дочернего класса и вплоть до System.Entity (Object)
- на входе можно указать массив классов для отрисовки
- определенным цветом отражаются различные типы классов. Серый - singleton, белые - абстрактные, синие - назовем их обычными классами (то есть не абстрактные).
- в Shape Data Panel отображаются свойства класса (такие как Abstract, Singleton, Public и т.д.), атрибуты класса.
- в версии 0.2 появилось отображение атрибутов базовых классов
- в версии 0.3 появилось отображение связей между классами

Как появится время, планирую:
 - добавить в Shape Data отображение атрибутов родительского класса. В настоящий момент отображаются только определенные в конкретном классе атрибуты. Доступно в версии 0.2
 - добавить связи между классами (Hosted, Containment, References). Доступно в версии 0.3
 - подумать как отобразить мониторы и правила, привязанные к классам

Требования:
- Скрипт Powershell 2.0 (скорее всего будет работать и на 1.0 и на 3.0)
- Скрипт тестировался с Visio 2010. С Visio 2007 вряд ли будет работать , а вот с Visio 2013 может быть есть совместимость.
- Запускать нужно на сервере SCOM, хотя если немного изменить текст, то можно присоединиться и к удаленному серверу. Главное чтобы на запускающем хосте была установлена консоль Operations Manager.

Название скрипта SCOMDrawClassTree.ps1.
Параметры:

-Help                    : вывод справочной информации
-ClassNames <[string[]]> : необходимо указать массив классов

Примеры использования:
1) Отрисует дерево объектов для класса "Microsoft.Windows.Server.2008.AD.DomainControllerRole"
.\SCOMDrawClassTree.ps1 -ClassNames "Microsoft.Windows.Server.2008.AD.DomainControllerRole"

2) Отрисует дерево объектов для двух классов. Массив классов можно задать в виде строк, разделенных запятой:
.\SCOMDrawClassTree.ps1 -ClassNames "Microsoft.Windows.Server.2008.AD.DomainControllerRole", "Microsoft.Windows.Server.2008.AD.DC.KDC"

3) А вот так можно отрисовать все классы в MP Microsoft.Windows.Server.AD.Library:
#сначала формируем массив имен классов из MP Microsoft.Windows.Server.AD.Library
$classNames = Get-SCOMClass -ManagementPack (Get-ScomManagementPack -Name "Microsoft.Windows.Server.AD.Library") | foreach {$_.Name}
#далее рисуем
.\SCOMDrawClassTree.ps1 -ClassNames $classNames

Текст скрипта:
http://pastebin.com/nqe8TMaw

Предыдущие версии:
Версия 0.1: http://pastebin.com/BZ3YP3JR
Версия 0.2: http://pastebin.com/DNFaFHEn


Скриншоты:



четверг, 22 ноября 2012 г.

Репликация баз данных в SCCM 2012

Введение.
В этой статье я хотел бы рассказать о новом механизме репликации данных, появившемся в SCCM 2012. Статья не претендует на сверхточность или супердетальность описания этого процесса. Основное внимание будет уделено именно концепции механизма репликации, хотя некоторые детали, который на мой взгляд могут быть полезны, все-таки будут приведены.
В System Center 2012 Configuration Manager появился дополнительный способ репликации данных иерархии SCCM – репликация данных с помощью механизмов SQL Server. Достаточно оправданный ход со стороны Microsoft в сторону упрощения реализации этой задачи. В процессе репликации задействуется два важных механизма: Database Change Tracking и SQL Server Service Broker. Первый появился в SQL Server  2008, второй в SQL Server 2005. Видимо из-за отсутствия Database Change Tracking в SCCM 2007 (или 2007 R2) не было репликации с помощью SQL Server (хотя может и по другим причинам). 

Основные понятия.
Для понимания самого процесса репликации очень важно разобраться в используемых механизмах.
  • Database Change Tracking – механизм, который появился в SQL Server 2008. Предназначен для отслеживания изменений в таблицах SQL Server, предоставляет функции для работы с этими изменениями. Подробнее об этой функции можно почитать здесь - http://msdn.microsoft.com/en-us/library/bb933875.aspx.
  • SQL Server Service Broker – механизм SQL Server, входящий в компонент Database Engine. Предназначен для обмена сообщениями в распределенных приложениях. Механизм пришел на смену MSMQ (Microsoft Message Queuing) и призван упростить задачу межпроцессного обмена информацией. Подробнее о Service Broker можно прочитать здесь - http://msdn.microsoft.com/en-us/library/ms166104(v=sql.100).aspx. Позже мы еще вернемся к Service Broker.
  • Тип данных для репликации: global, site, local, global_proxyЛокальные данные не реплицируются, глобальные данные реплицируются на всю иерархию, данные сайта реплицируются с основного (primary) сайта на центральный (central) сайт. Global_proxy - это подмножество глобальных данных. Тип этой репликации используется только между Primary и Secondary сайтами, поэтому, например, на CAS вы не найдете соответствующий код для репликации.



Service Broker.
Этот сервис пришел на смену MSMQ, упростив задачу межпроцессного обмена данными. Service Broker освобождает вас от рутиной работы и многое далает за вас. Он берет на себя такую работу как авторизация, аутентификация (за счет посредников конечно), доставка данных, хранение, уникальность, очередность и т.д. И причем все эти функции доступные через весьма простые программные интерфейсы и T-SQL.
Для работы с Service Broker (в том числе и в SCCM) используются следующие объекты:
  • Диалог (dialog) – можно сказать что это сессия TCP, не в прямом смысле конечно, но похоже. То есть также есть некоторые две стороны, которые обмениваются сообщениями. При этом обеспечивается гарантированная доставка, подтверждение, очередность. Сторона установившая диалог называется инициатор (initiator), отвечающая сторона – target.
  • Сообщения (Message) – этими объектами просходит обмен. Сообщения могут быть любого типа, от бинарных данных, до «правильного» XML или даже Service Broker может проверять сообщения с помощью XML-схемы.

Для SCCM определены ряд сообщений, такие как, DRS_SyncStart, DRS_SyncEnd, DRS_SyncData и т.д. Всего определено порядка 30 сообщений. Например, определение основного сообщения репликации данных DRS_SyncStart выглядит так:
CREATE MESSAGE TYPE [DRS_SyncData] AUTHORIZATION [dbo] VALIDATION = NONE
  • Контракты (Contracts) – эти объекты определяют типы сообщений, которые можно послать в диалоге, а также разрешенные типы сообщений для обоих сторон диалога. Например, можно ограничить отправку определенных сообщений, которые может послать только инициатор (initiator), тип сообщений которыми может ответить target или указать что сообщение может использоваться обеими сторонами.

Для SCCM определены контракты по приоритетам репликации: CriticalPriority, HighPriority и т.д.
К примеру, для репликации данных с приоритетом ниже среднего определен контракт LowNormalPriority:
CREATE CONTRACT [LowNormalPriority]
AUTHORIZATION [dbo] ([DRS_SchemaChange] SENT BY ANY,[DRS_StartMsgBuilder] SENT BY ANY,[DRS_SyncBinaryData] SENT BY ANY,[DRS_SyncBinaryDataCompressed] SENT BY ANY,[DRS_SyncComplete] SENT BY ANY,[DRS_SyncData] SENT BY ANY,[DRS_SyncDataCompressed] SENT BY ANY,[DRS_SyncEnd] SENT BY ANY,[DRS_SyncStart] SENT BY ANY)
  •  Сервисы (Services) – Диалог между участниками происходит как раз с использованием сервисов. У сервисов есть имена и очереди сообщений. Можно представить что сервисы это почтовые адреса.
Для SCCM используются такие сервисы как, ConfigMgrDRS_Site<код_сайта> (этот для репликации глобальных данных между сайтами), ConfigMGRDRSSite_Site<код_сайта (для репликации данные с областью site) или, например, ConfigMgrDrsMsgBuilder (используется для запуска репликации).
Например, определение сервиса для репликации глобальных данных (в том числе и global_proxy):
CREATE SERVICE [ConfigMgrDRS_SiteABC]
AUTHORIZATION [dbo]
ON QUEUE [dbo].[ConfigMgrDRSQueue]
([CriticalPriority],[HighPriority],[LowNormalPriority],[LowPriority],[NormalPriority])
  •  Очереди (Queues) – объекты, которые ассоциированы с некоторым сервисом. Сообщение, отправленное на целевой сервис, попадает в его очередь и ждет обработки. Процесс извлечения сообщений может происходить  вручную или с помощью хранимой процедуры, которая вешается на очередь.
Для каждого сервиса созданы очереди. Так выглядит очередь для репликации глобальных данных сайта BCD:
CREATE QUEUE [dbo].[ConfigMgrDRSQueue]WITH STATUS = ON , RETENTION = OFF ,
POISON_MESSAGE_HANDLING (STATUS = ON)  ON [PRIMARY]

А так - очередь для инициации репликации:
CREATE QUEUE [dbo].[ConfigMgrDRSMsgBuilderQueue]
WITH STATUS = ON , RETENTION = OFF ,
ACTIVATION (  STATUS = ON , PROCEDURE_NAME = [dbo].[spDRSMsgBuilderActivation] , MAX_QUEUE_READERS = 5 , EXECUTE AS N'dbo'  ),
POISON_MESSAGE_HANDLING (STATUS = ON)  ON [PRIMARY]
На этой очереди висит процедура spDRSMsgBuilderActivation, которая запускается если в очереди есть сообщения.
  • Маршруты (Routes). С помощью маршрутов определяется расположение целевого сервиса. Можно сравнить данный объект с типом записи SRV в DNS. Если целевой сервис на который отправляется сообщение находится на удаленной системе, то просматривается маршрут, который был создан для этого сервиса и в котором указан сетевой адрес и порт (адрес и порт Service Broker). Точнее сначала просматривается маршрут до целевого сервиса, а потом уже определяется что он на удаленной системе. Если маршрут не найден, то используется маршрут по умолчанию, который указывает на локальный экземпляр SQL Server.

Для каждого сервиса определены маршруты. Например, для удаленного сайта создан маршрут ConfigMgrDRSRoute_SiteBCD, который указывает на удаленную систему:
CREATE ROUTE [ConfigMgrDRSRoute_SiteBCD]
AUTHORIZATION [dbo]
WITH  SERVICE_NAME  = N'ConfigMgrDRS_SiteBCD' ,
ADDRESS  = N'TCP://EF-CM12-02.cm12.local:4022'
Теперь концептуально рассмотрим сам процесс обмена сообщениями в Service Broker.

Диалог между сервисами происходит следующим образом:

1) Приложение-инициатор начинает диалог:
BEGIN DIALOG CONVERSATION @dialog_handle
FROM SERVICE [ConfigMgrDRS_SiteABC]
TO SERVICE 'ConfigMgrDRS_SiteBCD'
ON CONTRACT [NormalPriority]
2) В @dialog_handle возвращается описатель этого диалога. Этот описатель используется для отправки и приема сообщений между участниками.
3) Формируется сообщение. Способ формирования не имеет значение. Как формируются сообщения в SCCM будет описано чуть ниже.
4) Далее, сформированное сообщение отправляется:
SEND
ON CONVERSATION @dialog_handle
MESSAGE TYPE DRS_SyncData (@Message)
5) Диалог и объекты служб (а также контракт) определяется по описателю. По маршруту определяется расположение сервисов.
6) Сообщение отправляется целевому сервису.
7) После приема на целевой стороне, сообщение попадает в очередь, которая привязана к сервису.
8) Далее сообщение, вручную или с помощью ассоциированной с очередью хранимой процедурой, извлекается из очереди. Например, вот такой конструкцией:
WAITFOR (
      RECEIVE TOP(1) @ReceivedMessageType=message_type_name,
                     @ReplicationGroup=message_body,
                     @ConversationHandle=conversation_handle
            FROM dbo.ConfigMgrDRSMsgBuilderQueue
            ), TIMEOUT 5000
9)      Полученное сообщение обрабатывается.
10)   Может быть отправлено ответное сообщение.
11)   Диалог завершается конструкцией END CONVERSATION @DialogHandle

Репликация SCCM
Обсудив принципы работы с Service Broker, можно переходить к принципам функционирования репликации в SCCM.

Инициатор.

1)      Например, мы изменили границы (Boundary) в нашей иерархии. Процесс проходит через SMS-провайдер, попадает в базу, запускается ряд дополнительных потоков, таких как Database Notification Monitor, Hierarchy Manager и т.д. На этом этапе данные зафиксированы в БД (в нашем случае в таблице BoundaryEX). Далее.
2) [SMS_REPLICATION_CONFIGURATION_MONITOR]: Компонент MS_REPLICATION_CONFIGURATION_MONITOR запускает каждую минуту процедуру spDRSInitiateSynchronization (можно увидеть в логе rcmctrl.log).
3)      [spDRSInitiateSynchronization]: Процедура просматривает таблицу ReplicationData и получает все группы объектов репликации, которые не реплицировались определенный интервал времени (SyncInverval*60) и который в данный момент не реплицируются. Для глобальных данных определен интервал в 1 минуту, для данных сайта – 5 минут.

Объекты Boundary относятся к группе 3 (столбец ID) с типом репликации global.
4)      [spDRSInitiateSynchronization]: Для каждой группы репликации формируется сообщение типа DRS_StartMsgBuilder с телом равным наименованию группы репликации (ReplicationGroup). Сообщение отправляется сервису  ConfigMgrDRSMsgBuilder (и от сервиса ConfigMgrDRSMsgBuilder). Это локальный сервис. Отправка сообщения происходит, если репликация инициируется с сервера в Primary сайте или область репликации не равна site. То есть это проверка, что CAS не должен отсылать данные с областью site другим сайтами (Primary).
5)      [Service Broker]: Сообщение попадает в очередь ConfigMgrDRSMsgBuilderQueue (ассоциирована с сервисом ConfigMgrDRSMsgBuilder). На очереде висит процедура spDRSMsgBuilderActivation
6) [spDRSMsgBuilderActivation]: Процедура spDRSMsgBuilderActivation сканирует очередь ConfigMgrDRSMsgBuilderQueue, вытаскивает сообщения с типом DRS_StartMsgBuilder и запускает другую процедуру – spDRSSendChangesForGroup.

7)      [spDRSSendChangesForGroup]: Это основная процедура для отправки изменений. В этой процедуре происходят различные проверки, получение описателей диалогов Service Broker и т.д.
8)      [spDRSSendChangesForGroup]: Определяется максимальная версия данных в базе данных. Версию можно восприниматься как некий USN (Unique Sequence Number), который ассоциируется с изменениями. MaxVersion = CHANGE_TRACKING_CURRENT_VERSION().
9)      [spDRSSendChangesForGroup]: Определяются последние версии (USN) отправленные в целевой сайти (а также полученные). Эти данные извлекаются из таблицы DRS_MessageActivity и DRS_MessageActivity_Sent.
10)      [spDRSSendChangesForGroup]: Дальше происходит определение статей (таблица ArticleData), которые необходимо реплицировать. Статьи в данном случае это таблицы (нас интересует BoundaryEx). У каждой статьи есть группа репликации (в нашем случае 3). С помощью функций Database Change Tracking определяется USN статьи и, если USN статьи больше чем USN последней отправленной репликации, то статья попадает под процесс репликации.
11)      [spDRSSendChangesForGroup]: Сами данные необходимые для репликации  (измененные с момента последней удачной репликации) определяется с помощью хранимой процедуры SCCM_RDS_spGet<Имя статьи>Changes в случае если область репликации равна  site или с помощью функции SCCM_DRS.fnGet<Имя статьи>Changes в ином случае. Процедуры spGet<Имя статьи> существуют только на первичном сайте (не standalone), поскольку CAS не реплицирует данные с областью site. Последнее предложение справедливо и для вторичного сайта.
12)      [fn|spGet<имя статьи>Changes]: В этой функции (или процедуре) используется обычная конструкция T-SQL  from CHANGETABLE(CHANGES <имя таблицы>). Измененные данные возвращаются в формате XML или бинарном виде (после сериализации процедурой spDRSSerializeQuery). Бинарный вид данных используется только для области репликации site. Почему именно так – сказать не могу. Для Secondary-сайта также используются функции формата fnGet<имя статьи>ChangesSec. В этих функциях происходит фильтрация данных, реплицируемых на вторичный сайт или со вторичного сайта. Также на вторичном сайте перед формированием данных из репликации исключаются некоторые статьи (типа global_proxy) (ArticleData.OptionalFlag & 0x2 = 0x2).
13)      [spDRSSendChangesForGroup]: Далее отправляется сообщение типа DRS_StartSync сервису ConfigMgrDRS_Site<код целевого сайта> или ConfigMgrDRSSite_Site<Код целевого сайта> (из процедуры spDRSSendStartMsg). ConfigMgrDRSSite_Site<Код целевого сайта> используется для данных типа site.
14)      [spDRSSendChangesForGroup]: Если есть данные (отличные от данных типа site) для репликации, то далее изменения формируются в сообщения и отправляются с помощью процедуры spDRSSendDataMsg. Если сжатие для группы репликации (ReplicationData.Flags & 1 = 1, в моем случае везде NULL, то есть сжатие не используется) включено и размер сообщения > 2КБ, то происходит сжатие даннных с помощью функции fnCompressData.  Если сжатые данные больше чем изначальные, то сжатие не используется. Если используется сжатие, то тип сообщения DRS_SyncDataCompressed, иначе тип сообщения DRS_SyncData. Служба для отправки ConfigMgrDRS_Site<код-целевого-сайта>.
Вот так выглядит сообщение DRS_SyncData:
<DRS_SyncData BuildNumber="7711" LastSyncVersionToSource="1221127" ThisVersion="77819" SyncID="1B102B82-71B2-4051-8B2A-15C41E430354" ReplicationGroupID="3" MessageID="4D06DD9F-CBE0-4907-8139-C06955926D4B">
<Operation Type="I" TableName="BoundaryEx" Context="">
    <row BoundaryID="8" Name="" CreatedBy="CM12\Administrator" CreatedOn="2012-11-18T20:46:20" ModifiedBy="CM12\Administrator" ModifiedOn="2012-11-18T20:46:20" BoundaryType="0" Flags="0" Value="192.168.59.0" />
  </Operation>
</DRS_SyncData>
15)      [spDRSSentDataMsg или spDRSSentBinaryDataMsg]: Для групп 2 и 3 сообщения логируются процедурой spDRSInsertSentMessage в таблицу DRSSentMessage. Можно конечно попробовать изменить текст функции fnISLogDrsMessageDataOn (в тестовой среде конечно) для изучения репликации, но Microsoft позаботился об этом и каждую минуту запускается код, который изменяет текст некоторых объектов на исходный. Если хотите посмотреть на содержимое сообщений, то лучше убрать проверку непосредственно в хранимых процедурах spDRSSentDataMsg и spDRSSentBinaryDataMsg. Также можно вставить некий код логирования в процедуры spDRSSentStartMsg и SpDRSSentEndMsg.
16)      [spDRSSendChangesForGroup]: Если есть данные сайта (site scope) для репликации, то используется отправка бинарных данных. Этот тип данных и тип сообщения используется только для репликации данных сайта и измененные данные извлекаются с помощью процедуры SCCM_DRS.spGet<имя статьи>Changes. Отправка данных происходит в отдельной процедуре spDRSSentBinaryDataMsg.
17)      [spDRSSentBinaryDataMsg]: Отправка бинарных данных происходит по тому же принципу. Проверяется необходимость сжатия для группы данных и отправка сообщения DRS_SyncBinaryDataCompressed или DRS_SyncBinaryData. Теми же самыми механизмами определяется необходимость логирования данных. Сервис используется другой – ConfigMgrDRSSite_Site<код сайта>. Secondary-сайт не реплицирует данные с областью site, поэтому такой сервис отсутствует на сервере баз данных вторичного сайта.
В бинарном виде сообщение не очень презентабельно, а вот после десериализации выглядит как обычная таблица:

Это таблица Summarizer_Components.
18)       [spDRSSendChangesForGroup]: Диалог репликации завершается вызовом процедуры spDRSSentEndMsg, в которой отправляется сообщение типа DRS_SyncEnd.
19)      [spDRSSendChangesForGroup]: Далее происходит очистка различных временных таблиц и обновление статуса репликации в таблице DRS_MessageActivity_Sent. Эта таблица постоянно используется для определения текущего статуса и для обновления статуса по ходу выполнения процесса.

На другой стороне.
Что же происходит на принимающей стороне?
С принимающей стороной немного сложнее, поскольку используется в основном код CRL.
1)      [SMS_REPLICATION_CONFIGURATION_MONITOR]: На стороне приема для обработки используется CRL хранимая процедура – spDRSActivation, которая определена в сборке messagehandlerservice.dll (можно найти в bin каталоге SCCM). Метод для вызова процедуры – ServiceProcedure из класса Microsoft.ConfigurationManager.DataReplicationService.MessageHandlerService.
2)      [messagehandlerservice.dll]: В этой же сборке определены дополнительные классы и их методы для работы с сообщениями репликации.
3)      [messagehandlerservice.dll]: Извлекается сообщение, парсится, определяется его тип, метод кодирования. Из сообщения извлекаются данные для изменения, определяется зависимость данных и выполняется соответствующая хранимая процедура для внесения изменений в целевые таблицы. Для операции Delete вызывается процедура SCCM_DRS.spDelete<имя таблицы>. Для операций Insert или Update вызывается хранимая процедура SCCM_DRS.spUpsert<имя таблицы>.
4)      [spDRSDelete|spDRSUpsert]: В этих процедурах определены дополнительные проверки и методы обнаружения конфликтов. Обнаруженные конфликты логируются в таблицу DRSConflictInfo. Данные для изменения вносятся в целевые таблицы.
5)      [messagehandlerservice.dll]: Из дополнительных полезностей можно отметить логирование сообщений процедурой  spDRSInsertReceivedMessage. Необходимость логирования определяется все той же функцией fnIsLogDrsMessageDataOn (логируются группы 2 и 3). Сообщения на этот раз сохраняются в таблице DRSReceivedMessages.

Вместо заключения.
Вместо заключения хотелось бы перечислить ряд дополнительных методов,  с помощью которых можно получить сведения о репликации баз данных в SCCM 2012:
1)      Область Monitoring консоли управления ConfigManager. С помощью узла Database Replication (и его контекстных команд) можно получить сведения о статуе репликации, запустить диагностику.
2)      Таблицы DRS_MessageActivity, DRS_MessageActivity_Send, DRS_MessageActivity_Receive. Эти таблицы используются в процессе репликации для отслеживания статуса, а также для внесения сведений о ресультатах репликации. В таблицах есть поле для ошибок, возникающих в ходе репликации. Расшифровку этих ошибок можно поискать в соответствующих процедурах или функциях, запускаемых во время репликации. Большую часть сведений из этих таблиц можно получить с помощью консоли управления.
3)      Процедура spDiagDRS. Эта процедура запрашивает данные с разных таблиц (в том числе и с DRS_MessageActivity*) и отображает информацию о статусе (DRS_MessageActivity*), статьях (ArticleData), группах репликации (ReplicationData) и т.д.. Также отображаются сведения о Service Broker.
4)      Стандартный отчет Service Broker. В SQL Management Studio доступен отчет, отображающий статистику Service Broker. Для запуска необходимо открыть контекстное меню папочки Service Broker (в Management Studio) и выбрать Reports > Standard Reports > Service Broker Statistics. Данный отчет отображает различные диаграммы, состояние и статистику сервисов и очередей. Также отображаются базовые сведения о производительности Service Broker.