Windows Server для самых маленьких. Часть 24: Как работают групповые политики?

Все части серии «Windows Server для самых маленьких»

Групповая политика упоминалась почти в каждой части: в части 3 клиент скачивал её из SYSVOL при входе, в части 12 она должна была настраивать время на PDC, в части 20 её резервные копии входили в список обязательных, в части 23 ею предлагалось пополнять локальные группы. Пора разобрать её устройство: что такое объект политики на уровне каталога и файлов, как клиент решает, какие политики к нему относятся и в каком порядке, и как всем этим управлять из модуля GroupPolicy, — а в конце собрать ту самую политику для PDC, целиком командами.

Все команды ниже выполняются с рабочей станции администратора ws1 с RSAT (часть 15) в PowerShell 7. Домен — corp.example.com, контроллеры dc1 и dc2 в сайте Office, dc3 и dc4 в сайте Branch, dc1 — PDC. Модуль GroupPolicy не объявляет совместимость с PowerShell 7 и по умолчанию загружается через слой совместимости: объекты приходят десериализованными, и вложенные свойства вроде $_.Computer.DSVersion в вычисляемых колонках оказываются пустыми. Поэтому модуль загружаем напрямую:

PS C:\> Import-Module GroupPolicy -SkipEditionCheck

Из чего состоит политика

Объект групповой политики (GPO) живёт в двух местах одновременно, и это объясняет половину его странностей.

Контейнер в каталоге (GPC, Group Policy Container) — объект класса groupPolicyContainer с именем-GUID в CN=Policies,CN=System:

PS C:\> Get-ADObject 'CN={31B2F340-016D-11D2-945F-00C04FB984F9},CN=Policies,CN=System,DC=corp,DC=example,DC=com' -Properties displayName, versionNumber, gPCFileSysPath, gPCMachineExtensionNames, gPCUserExtensionNames, flags |
    Format-List displayName, Name, versionNumber, flags, gPCFileSysPath, gPCMachineExtensionNames, gPCUserExtensionNames

displayName              : Default Domain Policy
Name                     : {31B2F340-016D-11D2-945F-00C04FB984F9}
versionNumber            : 76
flags                    : 0
gPCFileSysPath           : \\corp.example.com\sysvol\corp.example.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}
gPCMachineExtensionNames : [{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{53D6AB1B-2488-11D1-A28C-00C04FB94F17}{D02B1F72-3407-48AE-BA88-E8213C6761F1}][{827D319E-6EAC-11D2-A4EA-00C04F79F83A}{803E14A0-B4FB-11D0-A0D0-00A0C90F574B}][{B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A}{53D6AB1B-2488-11D1-A28C-00C04FB94F17}]
gPCUserExtensionNames    :

Имя, версия, путь к файлам, флаги и список расширений клиента (CSE, client-side extension), которые должны обработать эту политику. Каждая пара квадратных скобок — одно расширение: первым идёт GUID самого расширения, за ним один или несколько GUID компонентов редактора, которыми настройки записаны. Здесь три расширения: реестр {35378EAC-…} (административные шаблоны и политики открытых ключей), параметры безопасности {827D319E-…} и агент восстановления EFS {B1BE8D72-…}. Если расширения в списке нет, клиент не станет искать его настройки в файлах, даже если они там лежат. Пользовательской части у этой политики нет — gPCUserExtensionNames пуст.

flags — отключённые половины политики: 0 — включено всё, 1 — отключена пользовательская часть, 2 — компьютерная, 3 — обе. Модуль показывает то же свойством GpoStatus (AllSettingsEnabled, UserSettingsDisabled, ComputerSettingsDisabled, AllSettingsDisabled).

Папка в SYSVOL (GPT, Group Policy Template) — по пути из gPCFileSysPath:

\\corp.example.com\SYSVOL\corp.example.com\Policies\{GUID}\
    GPT.INI                              [General] Version=…
    GPO.cmt                              комментарий к политике (Unicode)
    Machine\
        Registry.pol                     настройки реестра для компьютера (административные шаблоны)
        Microsoft\Windows NT\SecEdit\GptTmpl.inf    параметры безопасности: пароли, Kerberos, права, аудит
        Scripts\Startup\, Scripts\Shutdown\
        Preferences\                     предпочтения, по папке на тип: Drives, Registry, Groups, ScheduledTasks …
    User\
        Registry.pol
        Scripts\Logon\, Scripts\Logoff\
        Preferences\

Папки появляются, только когда в них что-то записано: у политики с одной настройкой реестра для компьютера есть лишь GPT.INI и Machine\Registry.pol. У двух политик, созданных вместе с доменом, папки называются MACHINE и USER заглавными — SYSVOL регистр не различает.

Версия — 32-битное число: старшие 16 бит — версия пользовательской части, младшие — компьютерной. Каждое сохранение части увеличивает её счётчик на единицу. В каталоге она лежит в versionNumber, в SYSVOL — в GPT.INI:

PS C:\> Get-ADObject -SearchBase 'CN=Policies,CN=System,DC=corp,DC=example,DC=com' -SearchScope OneLevel -Filter * -Properties displayName, versionNumber, flags |
    Format-Table displayName, versionNumber, @{n='User'; e={$_.versionNumber -shr 16}}, @{n='Computer'; e={$_.versionNumber -band 0xFFFF}}, flags -AutoSize

displayName                       versionNumber User Computer flags
-----------                       ------------- ---- -------- -----
Default Domain Policy                        76    0       76     0
Users - Intranet Zone                    196608    3        0     0
Network - Prefer IPv4                         8    0        8     0
Network - Prefer IPv6                     65539    1        3     0
Network Discovery                            74    0       74     0
Default Domain Controllers Policy            76    0       76     0
Remote Desktop                               20    0       20     0
Time - No Hyper-V Sync                        1    0        1     0
Workstations - Guest SMB                      5    0        5     0
DC - PDC Time Source                         10    0       10     0

196608 = 0x00030000: пользовательская часть сохранялась три раза, компьютерная — ни разу. 65539 = 0x00010003: один раз и три. Модуль показывает обе половины и версию в SYSVOL:

PS C:\> Get-GPO -All | Format-Table DisplayName, GpoStatus, @{n='User'; e={$_.User.DSVersion}}, @{n='Computer'; e={$_.Computer.DSVersion}}, @{n='Sysvol'; e={$_.Computer.SysvolVersion}}, @{n='WmiFilter'; e={$_.WmiFilter.Name}} -AutoSize

DisplayName                                GpoStatus User Computer Sysvol WmiFilter
-----------                                --------- ---- -------- ------ ---------
Default Domain Policy             AllSettingsEnabled    0       76     76
Users - Intranet Zone             AllSettingsEnabled    3        0      0
Network - Prefer IPv4             AllSettingsEnabled    0        8      8
Network - Prefer IPv6             AllSettingsEnabled    1        3      3
Network Discovery                 AllSettingsEnabled    0       74     74
Default Domain Controllers Policy AllSettingsEnabled    0       76     76
Remote Desktop                    AllSettingsEnabled    0       20     20
Time - No Hyper-V Sync            AllSettingsEnabled    0        1      1
Workstations - Guest SMB          AllSettingsEnabled    0        5      5
DC - PDC Time Source              AllSettingsEnabled    0       10     10 PDC emulator

Контейнер реплицируется вместе с каталогом, а папка — DFSR вместе с SYSVOL (часть 10), и это два независимых механизма. Решение, изменилась ли политика, клиент принимает по контейнеру, а настройки читает из файлов. Если на контроллере, к которому обратился клиент, каталог уже обновился, а SYSVOL ещё нет, клиент получит прежние файлы или не найдёт новых — отсюда события 1058 на только что созданных политиках. Отчёт gpresult /h помечает такую политику «AD / SYSVOL Version Mismatch», а консоль GPMC показывает расхождения на вкладке состояния инфраструктуры.

Два объекта с фиксированными GUID созданы при установке домена, они одинаковы во всех доменах. Default Domain Policy по рекомендации Microsoft хранит только политику учётных записей: пароли, блокировку и Kerberos. Эти настройки действуют для доменных учётных записей только из политик, привязанных к корню домена (заданные в политике OU касаются лишь локальных учётных записей машин этой OU), и контроллеры переносят их в атрибуты корня домена (minPwdLength, lockoutThreshold и другие из части 5); отдельные парольные требования для групп задаются не политикой, а объектами PSO (fine-grained password policy) в каталоге. Default Domain Controllers Policy — права пользователей и аудит на контроллерах; в ней же по умолчанию включены обязательная подпись SMB и защищённого канала (RequireSecuritySignature, RequireSignOrSeal в GptTmpl.inf). Остальное в них не кладут: свои настройки — в свои политики, а эти две при порче возвращает к исходному виду dcgpofix /target:domain или /target:dc.


Привязка

Сама по себе политика ни к кому не относится. Её привязывают к сайту, домену или OU, и привязка — атрибут gPLink на этом объекте:

PS C:\> Get-ADObject 'DC=corp,DC=example,DC=com' -Properties gPLink, gPOptions | Format-List gPLink, gPOptions

gPLink    : [LDAP://cn={8b9c0d1e-2f3a-4b4c-9d5e-6f7a8b9c0d1e},cn=policies,cn=system,DC=corp,DC=example,DC=com;0][LDAP://cn={3C5D6E7F-8091-4A2B-B3C4-D5E6F7081920},cn=policies,cn=system,DC=corp,DC=example,DC=com;0][LDAP://CN={31B2F340-016D-11D2-945F-00C04FB984F9},CN=Policies,CN=System,DC=corp,DC=example,DC=com;0]
gPOptions :

В схеме gPLink — одна строка, а групповая политика читает её как список: в квадратных скобках DN контейнера политики и через точку с запятой флаги привязки — 0 обычная, 1 отключена, 2 принудительная (enforced), 3 — отключённая принудительная. Новая привязка дописывается в начало строки, а применяются они справа налево: последняя в строке — первая по порядку (Order 1) и выигрывает у соседей по контейнеру. Тот же домен глазами модуля:

PS C:\> (Get-GPInheritance -Target 'DC=corp,DC=example,DC=com').GpoLinks | Format-Table Order, DisplayName, Enabled, Enforced -AutoSize

Order DisplayName            Enabled Enforced
----- -----------            ------- --------
    1 Default Domain Policy     True    False
    2 Users - Intranet Zone     True    False
    3 Time - No Hyper-V Sync    True    False

Default Domain Policy, привязанная первой, стоит в строке последней и имеет порядок 1; привязанные позже получили 2 и 3. gPOptions = 1 на домене или OU — блокировать наследование; пустой атрибут означает 0. Если снять последнюю привязку, в gPLink остаётся строка из одного пробела.

Клиент собирает политики по цепочке LSDOU: локальная политика машины, затем политики сайта, домена, OU и вложенных OU до той, в которой лежит объект. Применяются по порядку, и каждая следующая перекрывает предыдущую в тех настройках, которые задаёт. Отсюда правила:

  • настройка из OU побеждает настройку из домена, из вложенной OU — из родительской;
  • блокировка наследования на OU отрезает всё сверху — кроме принудительных привязок;
  • принудительная привязка побеждает всё ниже себя, в том числе блокировку, и из двух принудительных побеждает та, что выше;
  • внутри одного контейнера побеждает привязка с меньшим номером.
PS C:\> (Get-GPInheritance -Target 'OU=Domain Controllers,DC=corp,DC=example,DC=com').InheritedGpoLinks | Format-Table Order, DisplayName, Enabled, Enforced, Target -AutoSize

Order DisplayName                       Enabled Enforced Target
----- -----------                       ------- -------- ------
    1 Default Domain Controllers Policy    True    False OU=Domain Controllers,DC=corp,DC=example,DC=com
    2 Network Discovery                    True    False OU=Domain Controllers,DC=corp,DC=example,DC=com
    3 Remote Desktop                       True    False OU=Domain Controllers,DC=corp,DC=example,DC=com
    4 DC - PDC Time Source                 True    False OU=Domain Controllers,DC=corp,DC=example,DC=com
    1 Default Domain Policy                True    False DC=corp,DC=example,DC=com
    2 Users - Intranet Zone                True    False DC=corp,DC=example,DC=com
    3 Time - No Hyper-V Sync               True    False DC=corp,DC=example,DC=com

Список идёт по убыванию приоритета: верхняя строка выигрывает у всех нижних. Order — номер привязки внутри своего контейнера, поэтому у политик домена нумерация начинается заново. Принудительные привязки модуль ставит в начало списка, блокировка наследования на OU (GpoInheritanceBlocked : Yes в выводе Get-GPInheritance) убирает из него всё, что выше, кроме них.

Особый режим — loopback: обычно пользовательские настройки берутся из политик, привязанных к OU пользователя; на терминальных серверах и киосках нужно, чтобы они брались из OU компьютера. Настройка «Configure user Group Policy loopback processing mode» (Computer Configuration → Administrative Templates → System → Group Policy) в режиме Replace берёт для пользователя только политики компьютера, в режиме Merge — дописывает их после пользовательских, и при конфликте выигрывают они. Режим клиент записывает в журнал событием 5311.


Как клиент применяет

Служба Group Policy Client (gpsvc) на каждой машине домена:

  1. При загрузке — политики компьютера, при входе пользователя — его политики; затем в фоне каждые 90 минут со случайным сдвигом до 30 (на контроллерах — каждые 5). Фоновое обновление с Windows 8 и Server 2012 запускается задачей планировщика, и, судя по журналу, сдвиг выбирается один раз при старте службы: на рабочей станции события фонового обновления идут ровно через 105 минут, после перезагрузки — ровно через 106.
  2. Находит контроллер (событие 5308 в журнале) и запрашивает у него по LDAP gPLink и gPOptions своего сайта, домена и цепочки OU до своего объекта; для каждой найденной политики — её контейнер: версию, флаги, список расширений, права, WMI-фильтр.
  3. Отбрасывает отключённые, не прошедшие фильтры и пустые для этой половины (версия 0); расширение, у которого не поменялись ни политики, ни их список, свои настройки повторно не применяет.
  4. Для оставшихся читает файлы из SYSVOL по SMB — по пути из gPCFileSysPath, через пространство DFS \\corp.example.com\SYSVOL, которое отдаёт ссылки на контроллеры, начиная со своего сайта.
  5. Передаёт каждому расширению из списка его файлы, и расширения применяют настройки: реестр пишет в HKLM\SOFTWARE\Policies и HKCU\SOFTWARE\Policies, параметры безопасности применяет движок secedit, предпочтения создают диски и задачи.

В фоне применяется не всё: установка программ — только при загрузке или входе, перенаправление папок — при входе, сценарии запускаются при загрузке, выключении, входе и выходе. Если такому расширению есть что применить, оно пишет событие 1112 (нужна перезагрузка или новый вход), а gpupdate предлагает перезагрузиться или выйти. На медленном канале (по умолчанию меньше 500 кбит/с, событие 5314 с оценкой) реестр и безопасность применяются всегда, а установка программ, перенаправление папок и сценарии — нет. С Windows 8.1 и Server 2012 R2 клиент сохраняет полученные политики в локальное хранилище (событие 5216), и при синхронной обработке на следующей загрузке читает их оттуда, а не с контроллера; это управляется настройкой «Configure Group Policy Caching».

Два слова о tattooing. Настройки административных шаблонов пишутся в ветки Policies реестра, которые приложения читают первыми; когда политика перестаёт действовать, клиент эти ключи удаляет, и настройка возвращается к умолчанию. Предпочтения и часть параметров безопасности пишут в обычные ключи, и после снятия политики значение остаётся — «татуировка». Это не ошибка, а свойство, о котором нужно помнить, снимая политику.


Виды настроек

Раздел Где хранится Что
Административные шаблоны Registry.pol тысячи настроек Windows и приложений, описанных файлами ADMX; результат — ключи в Policies
Параметры безопасности GptTmpl.inf политика учётных записей (действует только из политик корня домена), права пользователей, аудит, ограниченные группы, службы, ACL на реестр и файлы, файрвол, AppLocker
Сценарии Scripts\ запуск при загрузке, выключении, входе, выходе; PowerShell или любые
Установка программ каталог и MSI на общей папке назначение и публикация MSI; устаревает, но работает
Перенаправление папок каталог Documents и Desktop на сервер
Предпочтения (Group Policy Preferences, GPP) Preferences\*.xml сетевые диски, принтеры, ярлыки, файлы, ключи реестра, локальные пользователи и группы, задачи планировщика, переменные среды — с нацеливанием на уровне элемента по десяткам условий (группа, ОС, IP, время, существование файла) и без татуировки, если отметить «Remove this item when it is no longer applied»

Предпочтения тоже записываются в gPCMachineExtensionNames: у политики с предпочтением реестра там [{B087BE9D-…}{BEE07A6A-…}] — расширение Group Policy Registry и его компонент редактора. Редактор вдобавок пишет запись с нулевым GUID расширения, [{00000000-0000-0000-0000-000000000000}{BEE07A6A-…}], в которой перечисляет свои компоненты, — у политик, собранных командами, её может не быть.

Административные шаблоны описываются файлами ADMX (структура) и ADML (строки на языке, по папке на язык: en-US, ru-RU), и у каждой версии Windows они свои. Редактор берёт их с локальной машины, если в домене нет Central Store — папки PolicyDefinitions в SYSVOL, откуда их читают все редакторы домена. В свежем домене её нет:

PS C:\> $store = '\\corp.example.com\SYSVOL\corp.example.com\Policies\PolicyDefinitions'
PS C:\> Test-Path $store
False
PS C:\> (Get-ChildItem C:\Windows\PolicyDefinitions -Filter *.admx).Count
225

На Windows 11 25H2 локально 225 шаблонов. Создают хранилище копированием всей папки C:\Windows\PolicyDefinitions с самой новой Windows в домене (или из пакета Administrative Templates, который Microsoft выпускает к каждой версии):

PS C:\> Copy-Item C:\Windows\PolicyDefinitions -Destination $store -Recurse

При обновлении Microsoft советует собирать новую папку с другим именем (PolicyDefinitions-25H2), добавлять туда шаблоны Office, Edge, Chrome от производителей и только потом переименовывать её в PolicyDefinitions, а старую оставить для отката: файлы разных версий, скопированные поверх друг друга, дают ошибки в редакторе.


Фильтры

Привязка к OU — первый фильтр. Второй — права: политику применяют только те, у кого на её контейнере есть Read и Apply Group Policy. По умолчанию это Authenticated Users:

PS C:\> Get-GPPermission -Name 'DC - PDC Time Source' -All | Format-Table @{n='Trustee'; e={$_.Trustee.Name}}, @{n='Type'; e={$_.Trustee.SidType}}, Permission, Denied, Inherited -AutoSize

Trustee                                 Type                  Permission Denied Inherited
-------                                 ----                  ---------- ------ ---------
Authenticated Users           WellKnownGroup                    GpoApply  False     False
Domain Admins                          Group GpoEditDeleteModifySecurity  False     False
Enterprise Admins                      Group GpoEditDeleteModifySecurity  False     False
ENTERPRISE DOMAIN CONTROLLERS WellKnownGroup                     GpoRead  False     False
SYSTEM                        WellKnownGroup GpoEditDeleteModifySecurity  False     False

Чтобы политика действовала на одну группу, Apply у Authenticated Users забирают и выдают этой группе. С обновлением безопасности 2016 года (MS16-072) компьютер читает политики пользователя своей учётной записью, и если у Authenticated Users забрать и Read, пользовательские политики перестанут применяться совсем. Поэтому Authenticated Users понижают до чтения, а не убирают (или выдают Read группе Domain Computers). Set-GPPermission без -Replace более высокий уровень не понижает — молча оставляет прежний:

PS C:\> Set-GPPermission -Name 'Sales - Drive Maps' -TargetName 'Authenticated Users' -TargetType Group -PermissionLevel GpoRead -Replace
PS C:\> Set-GPPermission -Name 'Sales - Drive Maps' -TargetName 'Sales' -TargetType Group -PermissionLevel GpoApply

Оба вызова возвращают объект политики. Без -Server командлеты модуля обращаются к PDC.

Третий — WMI-фильтр: запрос WQL к машине, и политика применяется, если он вернул хоть что-то. Классические: SELECT * FROM Win32_OperatingSystem WHERE ProductType = 2 — контроллеры домена; SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 5 — владелец PDC; SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "10.0.2%" — по версии. Запрос выполняется на клиенте при каждой обработке, и медленный запрос замедляет загрузку. Фильтр — объект класса msWMI-Som в CN=SOM,CN=WMIPolicy,CN=System, к политике его привязывает атрибут gPCWQLFilter её контейнера; у одной политики не больше одного фильтра, один фильтр — на сколько угодно политик. Командлета для создания фильтров в модуле нет, только оснастка или запись в каталог напрямую, как в примере ниже. У предпочтений свой, более удобный механизм — нацеливание на уровне элемента, и там, где можно, фильтруют им.


Модуль GroupPolicy

Команды для всего, что выше:

Get-GPO -All; Get-GPO -Name 'Servers - Baseline'                 # объекты
New-GPO -Name 'X' -Comment '…'; Remove-GPO; Rename-GPO; Copy-GPO   # создание
New-GPLink -Name 'X' -Target 'OU=Servers,DC=corp,DC=example,DC=com' -LinkEnabled Yes -Order 1 -Enforced No
Set-GPLink -Name 'X' -Target '…' -Enforced Yes; Remove-GPLink
Get-GPInheritance -Target '…'; Set-GPInheritance -Target '…' -IsBlocked Yes
Set-GPRegistryValue -Name 'X' -Key 'HKLM\SOFTWARE\Policies\…' -ValueName 'V' -Type DWord -Value 1   # административные шаблоны по ключу
Get-GPRegistryValue -Name 'X' -Key 'HKLM\SOFTWARE\Policies\…'
Set-GPPrefRegistryValue -Name 'X' -Context Computer -Action Update -Key '…' -ValueName 'V' -Type String -Value 'a'   # предпочтение реестра
Set-GPPermission -Name 'X' -TargetName 'Helpdesk' -TargetType Group -PermissionLevel GpoEdit
Get-GPOReport -Name 'X' -ReportType Html -Path .\X.html; Get-GPOReport -All -ReportType Xml
Backup-GPO -All -Path D:\GPOBackup; Restore-GPO -Name 'X' -Path D:\GPOBackup
Import-GPO -BackupGpoName 'X' -Path D:\GPOBackup -TargetName 'X' -CreateIfNeeded -MigrationTable .\map.migtable   # в другой домен
Get-GPResultantSetOfPolicy -Computer ws1 -User CORP\alice -ReportType Html -Path .\rsop.html
Invoke-GPUpdate -Computer ws1 -RandomDelayInMinutes 0 -Force       # удалённый gpupdate через задачу планировщика (с 2012)

Invoke-GPUpdate не подключается к машине по WinRM: он создаёт на ней задачу планировщика, которая запускает gpupdate. Для этого на машине должны быть открыты правила Remote Scheduled Tasks Management (RPC и RPC-EPMAP) и Windows Management Instrumentation (WMI-In); у серверов они по умолчанию выключены (часть 15), и тогда проще Invoke-Command dc1 { gpupdate /target:computer }. -Force у командлета по документации только отключает запрос подтверждения.

Get-GPOReport -ReportType Xml отдаёт политику целиком: имя, время создания и изменения, дескриптор безопасности, имя и описание WMI-фильтра, версии обеих частей (VersionDirectory и VersionSysvol), для каждой настройки административного шаблона — её имя в редакторе, состояние и текст справки, и в конце — куда политика привязана (LinksTo).

Чего в модуле нет: настройки параметров безопасности (GptTmpl.inf — текстовый INI, его правят редактором или копируют из эталонной политики через Import-GPO), сценарии, предпочтения кроме реестра (их XML можно положить в папку политики руками и добавить GUID расширения в gPCMachineExtensionNames, но это уже внутренности), WMI-фильтры.


Политика для PDC

Обещанное в части 12: источник времени, который следует за ролью PDC. Три части — WMI-фильтр на роль, политика с настройками W32Time, привязка к OU контроллеров. Сначала фильтр — объект в каталоге, без командлета:

PS C:\> $domain = Get-ADDomain
PS C:\> $id = '{' + (New-Guid).Guid.ToUpper() + '}'
PS C:\> $now = (Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmss.ffffff') + '-000'
PS C:\> $query = 'SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 5'
PS C:\> $attrs = @{
    'msWMI-Name'             = 'PDC emulator'
    'msWMI-Parm1'            = 'Domain controller holding the PDC emulator role (DomainRole = 5)'
    'msWMI-Parm2'            = "1;3;10;$($query.Length);WQL;root\CIMv2;$query;"
    'msWMI-ID'               = $id
    'msWMI-Author'           = 'alice.admin@corp.example.com'
    'msWMI-CreationDate'     = $now
    'msWMI-ChangeDate'       = $now
    'instanceType'           = 4
    'showInAdvancedViewOnly' = $true
}
PS C:\> New-ADObject -Name $id -Type 'msWMI-Som' -Path "CN=SOM,CN=WMIPolicy,CN=System,$($domain.DistinguishedName)" -OtherAttributes $attrs -PassThru

msWMI-Parm2 — строка с версией формата, числом запросов, длиной запроса, языком, пространством имён и самим запросом. Затем политика: десять значений в ветке Policies\Microsoft\W32Time — ровно то, что пишут административные шаблоны Configure Windows NTP Client, Enable Windows NTP Client, Enable Windows NTP Server и AnnounceFlags из Global Configuration Settings:

PS C:\> $gpo = New-GPO -Name 'DC - PDC Time Source' -Comment 'PDC emulator of the forest root syncs from pool.ntp.org and announces itself as a reliable time source; WMI filter "PDC emulator" makes the setting follow the role.'
PS C:\> $w = 'HKLM\SOFTWARE\Policies\Microsoft\W32Time'
PS C:\> $values = @(
    @{ Key = "$w\Parameters"; ValueName = 'NtpServer'; Type = 'String'; Value = '0.pool.ntp.org,0x8 1.pool.ntp.org,0x8 2.pool.ntp.org,0x8 3.pool.ntp.org,0x8' }
    @{ Key = "$w\Parameters"; ValueName = 'Type'; Type = 'String'; Value = 'NTP' }
    @{ Key = "$w\Config"; ValueName = 'AnnounceFlags'; Type = 'DWord'; Value = 5 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'Enabled'; Type = 'DWord'; Value = 1 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'CrossSiteSyncFlags'; Type = 'DWord'; Value = 2 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'ResolvePeerBackoffMinutes'; Type = 'DWord'; Value = 15 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'ResolvePeerBackoffMaxTimes'; Type = 'DWord'; Value = 7 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'SpecialPollInterval'; Type = 'DWord'; Value = 1024 }
    @{ Key = "$w\TimeProviders\NtpClient"; ValueName = 'EventLogFlags'; Type = 'DWord'; Value = 0 }
    @{ Key = "$w\TimeProviders\NtpServer"; ValueName = 'Enabled'; Type = 'DWord'; Value = 1 }
)
PS C:\> foreach ($v in $values) { Set-GPRegistryValue -Guid $gpo.Id @v | Select-Object DisplayName, @{n='Computer'; e={$_.Computer.DSVersion}} }

Каждый вызов — одно сохранение компьютерной части, так что после десяти версия станет 10. Ключи под Policies\Microsoft\W32Time — это те же параметры, что w32tm /config писал в Services\W32Time, но заданные политикой: они имеют приоритет и исчезают, когда политика перестаёт действовать. Get-GPOReport показывает такие значения как настройки соответствующих шаблонов, а не как «лишние» значения реестра.

Связь с фильтром и привязка:

PS C:\> Set-ADObject "CN=$($gpo.Id.ToString('B').ToUpper()),CN=Policies,CN=System,$($domain.DistinguishedName)" -Replace @{ gPCWQLFilter = "[$($domain.DNSRoot);$id;0]" }
PS C:\> New-GPLink -Guid $gpo.Id -Target "OU=Domain Controllers,$($domain.DistinguishedName)" -LinkEnabled Yes
PS C:\> Get-GPO -Guid $gpo.Id | Select-Object DisplayName, @{n='WmiFilter'; e={$_.WmiFilter.Name}}, GpoStatus

DisplayName          WmiFilter             GpoStatus
-----------          ---------             ---------
DC - PDC Time Source PDC emulator AllSettingsEnabled

Без вычисляемой колонки Select-Object WmiFilter покажет в таблице имя типа Microsoft.GroupPolicy.WmiFilter, а не фильтра: у объекта фильтра есть свойства Name, Description и Path. Сам фильтр в каталоге:

PS C:\> Get-ADObject -SearchBase 'CN=SOM,CN=WMIPolicy,CN=System,DC=corp,DC=example,DC=com' -Filter 'objectClass -eq "msWMI-Som"' -Properties msWMI-Name, msWMI-Parm2, msWMI-CreationDate |
    Format-List msWMI-Name, msWMI-Parm2, msWMI-CreationDate

msWMI-Name         : PDC emulator
msWMI-Parm2        : 1;3;10;55;WQL;root\CIMv2;SELECT * FROM Win32_ComputerSystem WHERE DomainRole = 5;
msWMI-CreationDate : 20261025082704.247764-000

На dc2, у которого DomainRole = 4 (контроллер без роли PDC), фильтр вернёт пустой результат, политика не применится, и он останется в иерархии домена. Когда роль переедет, на следующем обновлении политики (у контроллеров — через пять минут) новый PDC получит внешний источник, а со старого ключи Policies снимутся, и он вернётся к своим локальным настройкам. Поэтому на самих контроллерах локальную конфигурацию W32Time оставляют по умолчанию (NT5DS): тогда второй политики для остальных контроллеров не нужно. Проверить итог на PDC — Invoke-Command dc1 { w32tm /query /source } после обновления политики (Invoke-Command dc1 { gpupdate /target:computer }).

Резервная копия после изменения — привычка из части 20:

PS C:\> Backup-GPO -Guid $gpo.Id -Path D:\GPOBackup -Comment 'PDC time source' | Select-Object DisplayName, GpoId, Id, BackupDirectory

gpupdate и gpresult

На клиенте: gpupdate применяет изменившиеся политики, gpupdate /force — все, /target:computer или /target:user — только одну часть, /boot и /logoff — с перезагрузкой или выходом, если расширения этого требуют, /wait:<секунды> — сколько ждать (по умолчанию 600). Обновить только свою пользовательскую часть можно без повышения прав:

PS C:\> gpupdate /target:user
Updating policy...

User Policy update has completed successfully.

Что в итоге применилось — gpresult. Без повышения прав он видит только пользовательскую часть; gpresult /r без /scope молча её одну и покажет, а gpresult /r /scope:computer ответит «ERROR: Access Denied.» — компьютерную часть смотрят из консоли «от имени администратора». Пользовательская часть на ws1 (начало вывода gpresult /r /scope:user, без шапки и списка групп):

RSOP data for CORP\alice on WS1 : Logging Mode
---------------------------------------------------------------

OS Configuration:            Member Workstation
OS Version:                  10.0.26200
Site Name:                   N/A
Roaming Profile:             N/A
Local Profile:               C:\Users\alice
Connected over a slow link?: No


USER SETTINGS
--------------
    CN=Alice Smith,OU=Sales,OU=People,DC=corp,DC=example,DC=com
    Last time Group Policy was applied: 29.10.2026 at 15:41:11
    Group Policy was applied from:      dc1.corp.example.com
    Group Policy slow link threshold:   500 kbps
    Domain Name:                        CORP
    Domain Type:                        Windows 2008 or later

    Applied Group Policy Objects
    -----------------------------
        Users - Intranet Zone

    The following GPOs were not applied because they were filtered out
    -------------------------------------------------------------------
        Local Group Policy
            Filtering:  Not Applied (Empty)

Default Domain Policy и Time - No Hyper-V Sync тоже привязаны к домену, но пользовательская часть у них пустая, и в этом списке их нет вовсе. Дальше в выводе идёт список групп пользователя, по которому проверялись права на политики. В компьютерной части тот же отчёт перечисляет политики, отсеянные фильтрами, с причиной: Denied (Security) — нет прав, Denied (WMI Filter) — фильтр вернул пустой результат, Disabled (Link) — привязка отключена.

gpresult /h report.html даёт тот же отчёт с каждой настройкой и политикой, из которой она пришла, — главный инструмент для «почему у этой машины такая настройка»; gpresult /x — то же в XML. Путь к файлу у обоих ключей не длиннее 127 символов, иначе «ERROR: Value for '…' option cannot be more than 127 character(s).». Get-GPResultantSetOfPolicy делает отчёт для удалённой машины, и ему нужны открытые на ней правила Remote Event Log Management и WMI-In.

Итог каждой обработки клиент пишет в журнал System: 1500 и 1501 — политики компьютера и пользователя обработаны, изменений нет; 1502 и 1503 — обнаружены и применены новые настройки N политик. Подробный протокол каждого цикла — в журнале Microsoft-Windows-GroupPolicy/Operational, читать его можно только с повышенными правами:

События Что
4000, 4001 / 8000, 8001 начало и конец обработки при загрузке и при входе
4004, 4005 / 8004, 8005 ручная обработка (gpupdate) компьютера и пользователя
4006, 4007 / 8006, 8007 фоновая обработка
5308, 5326 какой контроллер найден и за сколько
5311, 5314 режим loopback, оценка канала и порог медленного
5312, 5313 список применяемых политик и отсеянных фильтрами
4016 / 5016, 7016 начало обработки расширения со списком политик, её успешный или неудачный конец со временем
5315 когда будет следующая обработка

Ошибки в журнале System: 1058 — не удалось прочитать gpt.ini политики из SYSVOL (разрешение имени и связь с контроллером, задержка репликации SYSVOL, отключённый клиент DFS — сам текст перечисляет эти причины); 1030 — не удалось получить новые настройки, код ошибки — на вкладке подробностей, обычно идёт в паре с 1058; 1129 — нет связи с контроллером; 1053 и 1055 — не удалось определить имя пользователя или компьютера (разрешение имён на контроллере, задержка репликации, защищённый канал); 1006 — не удалась привязка LDAP к контроллеру; 1096 — не удалось применить Registry.pol конкретной политики. Настоящее 1058 на рабочей станции:

PS C:\> (Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-GroupPolicy'; Id=1058} -MaxEvents 1).Message
The processing of Group Policy failed. Windows attempted to read the file \\corp.example.com\SysVol\corp.example.com\Policies\{3C5D6E7F-8091-4A2B-B3C4-D5E6F7081920}\gpt.ini from a domain controller and was not successful. Group Policy settings may not be applied until this event is resolved. This issue may be transient and could be caused by one or more of the following:
a) Name Resolution/Network Connectivity to the current domain controller.
b) File Replication Service Latency (a file created on another domain controller has not replicated to the current domain controller).
c) The Distributed File System (DFS) client has been disabled.

Одиночные 1058 на свежей политике — обычно SYSVOL контроллера, до которого изменение ещё не дошло, и gpresult строкой «applied from» скажет, какого. Бывает и так, что 1058 сыплются раз в пять минут на самом контроллере при здоровых SYSVOL и DNS: так ведёт себя виртуальный контроллер после переподключения его сетевого адаптера на гипервизоре, и лечится это перезагрузкой. Журнал Operational маленький и на контроллере перезаписывается быстрее чем за сутки — разбирать сбой нужно сразу.


Смотрим руками

Политика для PDC из примера, собранная в домене такими же командами. Её файлы в SYSVOL — только то, во что что-то записано:

PS C:\> $g = Get-GPO -Name 'DC - PDC Time Source'
PS C:\> $root = "\\corp.example.com\SYSVOL\corp.example.com\Policies\$($g.Id.ToString('B').ToUpper())"
PS C:\> Get-ChildItem $root -Recurse -File | Select-Object @{n='File'; e={$_.FullName.Substring($root.Length + 1)}}, Length

File                 Length
----                 ------
GPO.cmt                 328
GPT.INI                  23
Machine\Registry.pol   1816

PS C:\> Get-Content "$root\GPT.INI"
[General]
Version=10

PS C:\> $g.Computer | Select-Object DSVersion, SysvolVersion, Enabled

DSVersion SysvolVersion Enabled
--------- ------------- -------
       10            10    True

PS C:\> $g | Select-Object CreationTime, ModificationTime

CreationTime        ModificationTime
------------        ----------------
25.10.2026 11:27:21 25.10.2026 11:27:34

Десять сохранений за 13 секунд — версия 10. GPO.cmt — комментарий из New-GPO -Comment, модуль показывает его свойством Description. Версия в каталоге и в GPT.INI на каждом контроллере — если где-то меньше, изменение туда ещё не дошло, и клиенты, которые обращаются к этому контроллеру, применят политику позже:

PS C:\> $id = $g.Id.ToString('B').ToUpper()
PS C:\> 'dc1','dc2','dc3','dc4' | ForEach-Object {
    [pscustomobject]@{
        DC     = $_
        DS     = (Get-ADObject "CN=$id,CN=Policies,CN=System,DC=corp,DC=example,DC=com" -Server $_ -Properties versionNumber).versionNumber
        Sysvol = (Select-String -Path "\\$_\SYSVOL\corp.example.com\Policies\$id\GPT.INI" -Pattern '^Version=(\d+)').Matches.Groups[1].Value
    }
} | Format-Table -AutoSize

DC  DS Sysvol
--  -- ------
dc1 10 10
dc2 10 10
dc3 10 10
dc4 10 10

Что лежит в Registry.pol — через модуль, который его разбирает:

PS C:\> Get-GPRegistryValue -Name 'DC - PDC Time Source' -Key 'HKLM\SOFTWARE\Policies\Microsoft\W32Time\Parameters' | Format-Table ValueName, Type, Value -AutoSize

ValueName   Type Value
---------   ---- -----
NtpServer String 0.pool.ntp.org,0x8 1.pool.ntp.org,0x8 2.pool.ntp.org,0x8 3.pool.ntp.org,0x8
Type      String NTP

Какие контейнеры клиент перебрал для пользователя и в каком порядке — в XML-отчёте gpresult. Это и есть цепочка LSDOU, записанная самим клиентом:

PS C:\Temp> gpresult /scope:user /x rsop.xml /f
PS C:\Temp> $x = [xml](Get-Content .\rsop.xml)
PS C:\Temp> $x.Rsop.UserResults.SearchedSOM | Sort-Object { [int]$_.Order } | Format-Table Order, Type, Path, BlocksInheritance

Order Type   Path                                        BlocksInheritance
----- ----   ----                                        -----------------
1     Local  Local                                       false
2     Site   corp.example.com/Configuration/Sites/Office false
3     Domain corp.example.com                            false
4     OU     corp.example.com/People                     false
5     OU     corp.example.com/People/Sales               false

PS C:\Temp> $x.Rsop.UserResults.ExtensionStatus | Format-Table Name, BeginTime, EndTime, Error -AutoSize

Name                        BeginTime           EndTime             Error
----                        ---------           -------             -----
Group Policy Infrastructure 2026-10-29T12:41:11 2026-10-29T12:41:11 0
Registry                    2026-10-18T15:33:42 2026-10-18T15:33:42 0

Инфраструктура отработала только что (время в UTC), а расширение реестра последний раз применяло настройки 18 октября — когда изменилась единственная пользовательская политика. С тех пор ни политики, ни их список не менялись, и расширение каждый цикл пропускает. Фоновые циклы компьютера в журнале System:

PS C:\> Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-GroupPolicy'; Id=1500} -MaxEvents 6 | Format-Table TimeCreated, Id

TimeCreated           Id
-----------           --
29.10.2026 14:24:05 1500
29.10.2026 12:38:06 1500
29.10.2026 10:52:06 1500
29.10.2026 9:06:05  1500
29.10.2026 7:20:05  1500
29.10.2026 4:31:31  1500

Между 4:31 и 7:20 машина перезагрузилась; после этого циклы идут ровно через 1 час 46 минут — 90 минут и сдвиг в 16, который не меняется до следующей перезагрузки.


Источники

Устройство политики и порядок применения:

  • Group Policy overview — что такое GPO, GPC и GPT, расширения клиента;
  • Group Policy processing — LSDOU, принудительные привязки и блокировка наследования, loopback, интервалы обновления (90 минут со сдвигом до 30, пять минут на контроллерах), что не применяется в фоне;
  • Group Policy scope — формат gPLink, флаги привязки, порядок записей в строке, фильтрация правами и WMI;
  • [MS-GPOL]: Group Policy: Core Protocol и Sample gpt.ini File — спецификация: контейнер и шаблон политики, versionNumber из двух 16-битных половин, gPCMachineExtensionNames, flags, gPCWQLFilter, gpt.ini;
  • Loopback processing of Group Policy — режимы Merge и Replace;
  • What's New in Group Policy in Windows Server — кеширование политик, фоновое обновление задачей планировщика, Invoke-GPUpdate, состояние инфраструктуры;
  • Group Policy Preferences — предпочтения и нацеливание на уровне элемента;
  • dcgpofix — восстановление двух политик по умолчанию и что в них хранить.

Фильтры и права:

Административные шаблоны:

Модуль GroupPolicy и утилиты:

Время и диагностика:


Политики настроили машины. Следующий блок — о том, как машины и люди доказывают, кто они, не паролем, а сертификатом, и о том, кто эти сертификаты выдаёт.

Дальше — часть 25: удостоверяющий центр AD CS. Зачем своя инфраструктура открытых ключей и как устроена двухуровневая иерархия с отключённым корнем; установка корневого и выпускающего центров командами ADCSDeployment; шаблоны сертификатов и их права; автозачисление через групповую политику; списки отзыва, точки их распространения и OCSP; и что из части 23 сетевой серии про TLS здесь работает, а что устроено иначе.

Оставить комментарий могут только зарегистрированные пользователи.

Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.