
Неизвестную группу AD нельзя считать ненужной только потому, что в ней нет пользователей или её название непонятно. Она может входить в другую группу, использоваться в ACL, фильтре GPO, приложении или как primary group. Чтобы принять решение, нужно исследовать SID, членство и места использования, затем согласовать вывод с владельцем сервиса.
1. Зафиксируйте группу и идентификаторы
Выполняйте исследование с RSAT ActiveDirectory под учётной записью с необходимыми правами чтения. Начните с конкретной группы и определённого DC. В примере используются условные имена; укажите свою группу и контроллер.
Import-Module ActiveDirectory
Import-Module ActiveDirectory
$argsGroup = @{
Identity = 'Legacy_App_Access'
Server = 'dc01.example.internal'
Properties = @('Description','Info','ManagedBy','MemberOf','Members',
'WhenCreated','WhenChanged','SIDHistory','PrimaryGroupToken')
}
$group = Get-ADGroup @argsGroup
$group | Select-Object Name, SamAccountName, DistinguishedName, SID,
ObjectGUID, GroupCategory, GroupScope, Description, ManagedBy,
WhenCreated, WhenChanged
Запишите SID и ObjectGUID отдельно от имени. Переименование обычно сохраняет SID, а удаление и создание новой одноимённой группы — нет. WhenChanged не является надёжной датой последнего полезного использования и не заменяет журнал событий.
2. Проверьте прямых и рекурсивных участников
$dc = $argsGroup.Server
$direct = Get-ADGroupMember -Identity $group -Server $dc
$direct | Select-Object Name, ObjectClass, DistinguishedName, SID
Get-ADGroupMember -Identity $group -Server $dc -Recursive |
Select-Object Name, ObjectClass, DistinguishedName, SID
Прямой список показывает вложенные группы, а рекурсивный удобен для поиска конечных участников. Сохраняйте оба: развёрнутый перечень может скрыть, через какую промежуточную группу появился доступ. Get-ADGroupMember имеет ограничения работы с внешними членами и другими лесами; ошибка разрешения объекта не означает отсутствие участника.
Проверьте пользователей, компьютеры и сервисные аккаунты. Участник-компьютер тоже может быть нужен приложению или политике. Пустая группа может служить заранее созданной ролью либо предоставлять права через другие механизмы, которые не отображаются обычным списком member.
3. Найдите родительские группы
Свойство MemberOf показывает прямые родительские группы. Чтобы исследовать несколько уровней, используйте очередь и защиту от повторного посещения. Следующий фрагмент работает в выбранном домене и не обещает полный поиск всех межлесовых связей.
$pending = [Collections.Generic.Queue[string]]::new()
$seen = [Collections.Generic.HashSet[string]]::new()
foreach ($dn in $group.MemberOf) { $pending.Enqueue($dn) }
$parents = while ($pending.Count -gt 0) {
$dn = $pending.Dequeue()
if (!$seen.Add($dn)) { continue }
$p = Get-ADGroup -Identity $dn -Server $dc -Properties MemberOf
[pscustomobject]@{
Name = $p.Name; SID = $p.SID.Value
DistinguishedName = $p.DistinguishedName
}
foreach ($next in $p.MemberOf) { $pending.Enqueue($next) }
}
$parents | Format-Table -AutoSize
Проверяйте ACL не только исходного SID, но и найденных родительских групп. Иначе группа с незнакомым названием может выглядеть неиспользуемой, хотя через родителя даёт доступ к критичной папке или административной роли.
4. Исследуйте primary group и SIDHistory
Primary group пользователя представлена через primaryGroupID, а не обычную запись членства. Для группы вашего домена выделите RID и выполните поиск пользователей:
$rid = [int]($group.SID.Value.Split('-')[-1])
Get-ADUser -Server $dc -Filter { PrimaryGroupID -eq $rid } |
Select-Object SamAccountName, DistinguishedName, SID
$group.SIDHistory
При необходимости проверьте также компьютеры по primaryGroupID. Учитывайте границу домена: одинаковый RID в другом домене не означает ту же группу. SIDHistory может связывать мигрированный объект со старыми ACL. Права через SIDHistory и доверительные отношения должны рассматриваться отдельно; вывод пустого обычного членства не исключает такой доступ.
Проверьте primary group также у компьютеров выбранного домена:
Get-ADComputer -Server $dc -Filter { PrimaryGroupID -eq $rid } |
Select-Object Name, DistinguishedName, SID
RID берётся из SID именно исследуемой группы. Для SIDHistory выгрузите известные записи группы и отдельно исследуйте ACL старых SID. Отсутствие текущего SID в ACL не означает отсутствия доступа через SIDHistory или родительские группы.
5. Проверьте файловые ACL по SID
Создайте согласованный список исследуемых каталогов. Следующий фрагмент читает ACL только двух конкретных путей; он не выполняет рекурсивный поиск по всему файловому серверу и не изменяет права.
$sid = $group.SID.Value
$paths = @('D:\Shares\Finance', 'D:\Shares\Projects')
foreach ($path in $paths) {
$acl = Get-Acl -LiteralPath $path
$rules = $acl.GetAccessRules($true, $true,
[Security.Principal.SecurityIdentifier])
foreach ($rule in $rules) {
if ($rule.IdentityReference.Value -eq $sid) {
[pscustomobject]@{
Path = $path; SID = $sid
Rights = $rule.FileSystemRights
Type = $rule.AccessControlType
Inherited = $rule.IsInherited
Inheritance = $rule.InheritanceFlags
Propagation = $rule.PropagationFlags
}
}
}
}
Повторите проверку для SID родительских групп и согласованных дочерних папок с отдельными ACL. Наследуемую запись нужно исследовать у источника наследования. Проверяйте Allow и Deny, права общего ресурса SMB и локальные группы сервера. Владелец объекта тоже может иметь значение. Совпадение SID в одной ACL — факт использования; отсутствие в двух выбранных каталогах не доказывает отсутствие во всей инфраструктуре.

Ограниченный обход каталогов без перехода по junction. После согласования корня можно искать SID во всех вложенных каталогах, исключая reparse points. Ниже ограничение 5000 директорий; достижение лимита означает неполный поиск. Файлы с отдельными ACL этим вариантом не покрываются — добавляйте их отдельным согласованным проходом.
$targets = @($group.SID.Value) + @($parents | ForEach-Object SID)
$queue = [Collections.Generic.Queue[string]]::new()
$queue.Enqueue('D:\Shares\Finance')
$count = 0
$scanErrors = [Collections.Generic.List[object]]::new()
$found = while ($queue.Count -gt 0) {
if (++$count -gt 5000) { throw 'Directory limit: scan incomplete' }
$path = $queue.Dequeue()
try {
$item = Get-Item -LiteralPath $path -ErrorAction Stop
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { continue }
$acl = Get-Acl -LiteralPath $path -ErrorAction Stop
foreach ($rule in $acl.GetAccessRules($true, $true,
[Security.Principal.SecurityIdentifier])) {
if ($rule.IdentityReference.Value -in $targets) {
[pscustomobject]@{
Path = $path; SID = $rule.IdentityReference.Value
Rights = $rule.FileSystemRights
Type = $rule.AccessControlType
Inherited = $rule.IsInherited
}
}
}
Get-ChildItem -LiteralPath $path -Directory -Force -ErrorAction Stop |
Where-Object { -not ($_.Attributes -band [IO.FileAttributes]::ReparsePoint) } |
ForEach-Object { $queue.Enqueue($_.FullName) }
} catch {
$scanErrors.Add([pscustomobject]@{ Path = $path; Error = $_.Exception.Message })
}
}
$found | Export-Csv C:\AdminReports\group-acl.csv -NoTypeInformation -Encoding UTF8
$scanErrors | Export-Csv C:\AdminReports\group-acl-errors.csv -NoTypeInformation -Encoding UTF8
Каталог отчётов подготовьте заранее. Errors не должен скрываться за итогом «совпадений нет»: закрытая папка остаётся непроверенной. Большой поиск выполняйте в согласованное окно; разрешение чтения ACL не означает право читать содержимое документов. Повторяющиеся наследуемые совпадения сведите к их источнику, сохранив первоначальную выгрузку.
Отдельно проверьте SMB-права и соответствие SID, учитывая ошибки разрешения имён:
Get-SmbShare | Where-Object { -not $_.Special } | ForEach-Object {
$share = $_
Get-SmbShareAccess -Name $share.Name | ForEach-Object {
$entry = $_
try {
$account = [Security.Principal.NTAccount]::new($entry.AccountName)
$entrySid = $account.Translate([Security.Principal.SecurityIdentifier]).Value
} catch { $entrySid = 'UNRESOLVED' }
if ($entrySid -in $targets -or $entrySid -eq 'UNRESOLVED') {
[pscustomobject]@{
Share = $share.Name; Account = $entry.AccountName
SID = $entrySid; Right = $entry.AccessRight; Type = $entry.AccessControlType
}
}
}
}
UNRESOLVED требует разбора trust/DC/удалённого субъекта. Он не является подтверждённым совпадением с группой. SMB-permission и NTFS-permission исследуйте совместно, а не заменяйте одно другим.
6. Найдите использование в GPO
Установите модуль GroupPolicy и проверьте разрешения объектов GPO. Поиск ниже выводит совпадения SID в перечне прав; он не является поиском по всем значениям настроек внутри политики.
Import-Module GroupPolicy
foreach ($gpo in Get-GPO -All) {
foreach ($permission in Get-GPPermission -Guid $gpo.Id -All) {
if ([string]$permission.Trustee.Sid -eq $sid) {
[pscustomobject]@{
GPO = $gpo.DisplayName; Guid = $gpo.Id
Permission = $permission.Permission
}
}
}
}
Проверьте security filtering, делегирование, user rights assignment, Restricted Groups, Group Policy Preferences и item-level targeting. Для анализа содержимого экспортируйте согласованные GPO через Get-GPOReport и исследуйте отчёты; совпадение текста требует проверки контекста. Возможности чтения прав описаны в Get-GPPermission.
Чтобы найти упоминания внутри настроек GPO, экспортируйте XML в отдельный каталог и ищите подтверждённые SID, DN и имя. Поиск имени даёт также ложные совпадения и не является доказательством права:
$dir = 'C:\AdminReports\GroupGPO'
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Get-GPO -All | ForEach-Object {
Get-GPOReport -Guid $_.Id -ReportType Xml -Path (Join-Path $dir ($_.Id.ToString()+'.xml'))
}
Выполните поиск подтверждённых идентификаторов по полученным отчётам:
$match = @{
SimpleMatch = $true
Pattern = @($group.SID.Value, $group.DistinguishedName, $group.SamAccountName)
}
Get-ChildItem $dir -Filter '*.xml' | Select-String @match
Результат связывайте с GUID GPO и конкретной настройкой. Проверьте, что политика применяется к нужным компьютерам/пользователям: link, inheritance, filtering и WMI могут исключать найденную настройку. Search по строке DN может не найти XML-экранированное представление; сохраняйте проверку разрешений и ручной разбор соответствующих параметров.
7. Исследуйте приложения и локальные роли
Проверьте системы, которые используют AD/LDAP: VPN, файлы, базы, порталы, мониторинг, backup и управление гипервизорами. Приложение может ссылаться на SID, DN или строковое имя группы. Переименование безопасно для одних интеграций и ломает другие, поэтому не используйте rename как безусловно безвредный эксперимент.
На согласованных Windows-серверах проверьте локальные группы и права служб. Используйте инвентаризацию и обращения к владельцам, а не массовый обход всех машин без ограничения. Для каждого места использования записывайте источник, конкретное право, прямой или косвенный путь, владельца и степень подтверждения.
8. Сопоставьте историю изменений
Если аудит был настроен, исследуйте создание группы, изменения участников и административные действия на DC. Отсутствие старых событий может объясняться сроком хранения. Текущее состояние AD не восстановит задним числом все изменения, а WhenChanged не показывает, кто и зачем их сделал.
Свяжите события с заявками, документами миграции и историей проекта. ManagedBy и описание помогают найти ответственного, но могут быть устаревшими. Подтвердите назначение у владельца сервиса. Сохраните исходный экспорт участников и групповых связей в защищённом каталоге; в нём могут находиться персональные данные.
9. Подготовьте решение без немедленного удаления
Возможные результаты: группа используется и требует документации; используется избыточно и требует отдельного пересмотра прав; назначение не подтверждено; кандидат на вывод из эксплуатации. Для последнего нужен согласованный период наблюдения и план восстановления.
У группы AD нет универсального переключателя «отключить», аналогичного пользователю. Удаление членов, исключение из родителя, изменение ACL или переименование — реальные изменения доступа и требуют проверки последствий. Не делайте их одновременно. Если Active Directory Recycle Bin включён и условия восстановления соблюдены, он может помочь вернуть удалённый объект; создание новой группы с прежним именем не возвращает её SID и старые права автоматически.
Перед изменениями экспортируйте состав группы, вложенность и SID. Восстановление членства не должно становиться созданием новой группы:
$group | Export-Clixml C:\AdminReports\group-object-before.xml
Get-ADGroupMember -Identity $group -Server $dc |
Export-Clixml C:\AdminReports\group-members-before.xml
$group.MemberOf | Set-Content C:\AdminReports\group-parents-before.txt -Encoding UTF8
После согласованного удаления конкретного членства учтите, что уже выданные security tokens и прикладные сессии могут продолжать действовать. Новый вход тестового пользователя и проверка whoami /groups в его контексте помогают оценить новый token; не запускайте whoami только от администратора и не переносите его результат на сотрудника. Не завершайте все сессии компании ради одного теста. Для каждого проверенного сервиса запишите положительный и отрицательный сценарии доступа.
10. Оформите карточку назначения и приёмку
Запишите SID, GUID, имя, scope, category, владельца, участников, родителей, primary group, SIDHistory, проверенные ACL, GPO и приложения. Отдельно перечислите непроверенные домены, серверы и системы. Такой перечень делает вывод воспроизводимым и не позволяет подменить «не нашли в двух папках» утверждением «нигде не используется».
Перед согласованным выводом из эксплуатации проверьте резервный экспорт и процедуру восстановления, проведите тест владельца приложения и установите критерии остановки. После каждого изменения проверяйте типовые сценарии пользователей и служебных процессов. Завершённое расследование должно объяснить конкретный путь доступа группы и основания решения, а не только предложить удаление ради порядка в каталоге.
Связанные инструкции: аудит изменений AD, аудит доступа к файлам.
