
PowerShell позволяет управлять AD воспроизводимо: сначала выбрать объект и зафиксировать состояние, затем выполнить ограниченное изменение и проверить результат на том же контроллере. Ниже — десять рабочих задач для домена Windows Server, а не команды для массового удаления. Примеры используют учебный домен corp.example, контроллер dc01.corp.example и OU Lab. Замените их своими значениями. Рабочая среда — Windows PowerShell 5.1 с RSAT, отдельная учётная запись с делегированными правами. Локальный запуск от администратора нужен для установки компонентов, но сам по себе не даёт полномочий в AD.
Перед началом установите RSAT на поддерживаемой редакции Windows и откройте отдельную административную сессию. Команду установки выполняйте только там, где компонент действительно отсутствует:
Get-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools*'
Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
Import-Module ActiveDirectory
$dc = 'dc01.corp.example'
$ou = 'OU=Lab,DC=corp,DC=example'
Get-ADRootDSE -Server $dc | Select-Object defaultNamingContext
New-Item -ItemType Directory -Path C:\Admin\Evidence -Force
Проверьте, что defaultNamingContext соответствует нужному домену. Если модуль не найден — исправьте установку RSAT; если сервер недоступен — проверьте корпоративный DNS и доступ к AD Web Services. Не подменяйте выбранный контроллер случайным сервером: чтение и последующая запись должны иметь понятный источник. Документация модуля ActiveDirectory.

1. Сбросить пароль пользователя и назначить смену при входе
Сброс пароля — административная операция, которая отличается от смены пароля самим пользователем. До выполнения подтвердите личность заявителя через утверждённый канал, проверьте samAccountName, SID и OU. Совпадение отображаемого имени недостаточно: в разных подразделениях могут работать однофамильцы. Пароль вводится интерактивно, не попадает в текст скрипта или журнал команд.
$u = Get-ADUser -Identity 'lab.ivanov' -Server $dc -Properties Enabled,PasswordLastSet
$u | Select-Object SamAccountName,SID,DistinguishedName,Enabled,PasswordLastSet
$newPassword = Read-Host 'Временный пароль' -AsSecureString
Set-ADAccountPassword -Identity $u.ObjectGUID -Server $dc -Reset -NewPassword $newPassword -WhatIf
Set-ADAccountPassword -Identity $u.ObjectGUID -Server $dc -Reset -NewPassword $newPassword
Set-ADUser -Identity $u.ObjectGUID -Server $dc -ChangePasswordAtLogon $true
Get-ADUser -Identity $u.ObjectGUID -Server $dc -Properties pwdLastSet,PasswordLastSet |
Select-Object SamAccountName,pwdLastSet,PasswordLastSet
Remove-Variable newPassword
WhatIf проверяет план вызова, но не доказывает, что пароль удовлетворяет политике. Ошибка сложности требует нового пароля; обход политики не нужен. pwdLastSet=0 означает требование смены при следующем входе. Для сервисных учётных записей этот сценарий не подходит: смена может остановить службу. Возвратить старый неизвестный пароль невозможно — при ошибке назначают новый согласованный пароль. Проверяйте также результирующую fine-grained policy через Get-ADUserResultantPasswordPolicy. Set-ADAccountPassword.
2. Отключить и включить учётную запись
Блокировка входа при увольнении должна быть привязана к заявке и времени. Сохраните GUID и исходное Enabled: возврат должен восстанавливать именно предыдущее состояние, а не всегда включать аккаунт. Отключение не завершает уже выданные сессии SaaS, VPN и приложения автоматически — эти системы проверяют отдельно.
$u = Get-ADUser 'lab.ivanov' -Server $dc -Properties Enabled
$before = $u | Select-Object ObjectGUID,SamAccountName,Enabled,DistinguishedName
$before | Export-Clixml C:\Admin\Evidence\account-before.xml
Disable-ADAccount -Identity $u.ObjectGUID -Server $dc -WhatIf
Disable-ADAccount -Identity $u.ObjectGUID -Server $dc
Get-ADUser $u.ObjectGUID -Server $dc | Select-Object SamAccountName,Enabled
# Возврат только после согласования
$old = Import-Clixml C:\Admin\Evidence\account-before.xml
if ($old.Enabled) { Enable-ADAccount -Identity $old.ObjectGUID -Server $dc }
Ожидаемый результат первого изменения — Enabled=False на выбранном DC. После репликации проверьте второй контроллер, если приложение обращается к нему. Access denied исправляют делегированием нужной операции в конкретной OU, а не выдачей Domain Admins оператору.
3. Разблокировать пользователя и найти причину повторной блокировки
Разблокировка без поиска источника помогает на несколько секунд, если старый пароль продолжает использовать телефон, задача или служба. Сначала найдите LockedOut в ограниченной OU; затем прочитайте событие 4740 на контроллерах, где фиксировалась блокировка. Поле CallerComputerName — подсказка, а не доказательство, что проблему создал человек за этим компьютером.
Search-ADAccount -LockedOut -UsersOnly -SearchBase $ou -Server $dc |
Select-Object SamAccountName,DistinguishedName
Unlock-ADAccount -Identity 'lab.ivanov' -Server $dc -WhatIf
Unlock-ADAccount -Identity 'lab.ivanov' -Server $dc
Get-ADUser 'lab.ivanov' -Server $dc -Properties LockedOut | Select-Object LockedOut
Get-WinEvent -ComputerName $dc -FilterHashtable @{
LogName='Security'; Id=4740; StartTime=(Get-Date).AddHours(-2)
} -ErrorAction Stop | ForEach-Object {
$xml = [xml]$_.ToXml(); $fields = @{}
foreach ($d in $xml.Event.EventData.Data) { $fields[$d.Name] = $d.'#text' }
[pscustomobject]@{Time=$_.TimeCreated; User=$fields.TargetUserName; Caller=$fields.CallerComputerName}
}
Отсутствие события может означать, что вы смотрите другой DC, аудит не включён или журнал перезаписан. Не делайте вывод «атаки нет» из пустой выборки. Описание события 4740.
4. Найти пустые группы и проверить их назначение
Пустая группа может использоваться в файловой ACL, GPO, VPN или приложении. Удаление уничтожит её SID; новая группа с тем же названием не вернёт старый доступ. Следующий отчёт ищет отсутствие прямых member, но отдельно отмечает пользователей с primaryGroupID. Пустые результаты не разрешают удаление.
$groups = Get-ADGroup -Filter * -SearchBase $ou -Server $dc -Properties member,Description
$report = foreach ($g in $groups) {
if (@($g.member | Where-Object { $_ }).Count -eq 0) {
$rid = $g.SID.Value.Split('-')[-1]
$primary = @(Get-ADUser -LDAPFilter "(primaryGroupID=$rid)" -Server $dc)
[pscustomobject]@{Name=$g.Name;SID=$g.SID.Value;GUID=$g.ObjectGUID;PrimaryUsers=$primary.Count;Description=$g.Description}
}
}
$report | Export-Csv C:\Admin\Evidence\empty-groups.csv -NoTypeInformation -Encoding UTF8
Область Get-ADUser здесь — домен, потому что primary group может быть вне OU группы. Перед выводом из эксплуатации ищите SID в ресурсах и назначайте владельца. Это самостоятельная проверка, описанная в статье о назначении группы AD.
5. Добавить пользователя в группу и отменить только собственное изменение
Определите группу по GUID, проверьте её категорию и scope. Для выдачи доступа требуется согласование владельца ресурса. Сохранённый список прямых участников позволяет отличить существовавшее членство от добавленного этой операцией.
$g = Get-ADGroup 'Lab-File-Readers' -Server $dc
$u = Get-ADUser 'lab.ivanov' -Server $dc
$members = @(Get-ADGroupMember $g.ObjectGUID -Server $dc)
$already = $members.ObjectGUID -contains $u.ObjectGUID
$members | Export-Clixml C:\Admin\Evidence\group-members-before.xml
if (-not $already) {
Add-ADGroupMember -Identity $g.ObjectGUID -Members $u.ObjectGUID -Server $dc -WhatIf
Add-ADGroupMember -Identity $g.ObjectGUID -Members $u.ObjectGUID -Server $dc
}
Get-ADGroupMember $g.ObjectGUID -Server $dc | Select-Object Name,ObjectGUID
# Согласованный возврат только нового членства
if (-not $already) {
Remove-ADGroupMember -Identity $g.ObjectGUID -Members $u.ObjectGUID -Server $dc -Confirm
}
Доступ в существующем сеансе может не появиться сразу: групповой токен сформирован при входе, приложение может иметь собственный кэш. Проверьте новый сеанс пользователя и фактическое открытие нужного ресурса. Add-ADGroupMember.
6. Получить прямой и вложенный состав группы
Прямой состав нужен для управления членством, рекурсивный — для понимания эффективного включения через вложенные группы. Get-ADGroupMember -Recursive разворачивает дерево, но не сохраняет полный путь вложенности и не заменяет проверку primary group, SIDHistory и внешних доверий.
$g = Get-ADGroup 'Lab-File-Readers' -Server $dc
Get-ADGroupMember $g.ObjectGUID -Server $dc |
Select-Object Name,SamAccountName,ObjectClass,SID |
Export-Csv C:\Admin\Evidence\members-direct.csv -NoTypeInformation -Encoding UTF8
Get-ADGroupMember $g.ObjectGUID -Recursive -Server $dc |
Select-Object Name,SamAccountName,ObjectClass,SID |
Export-Csv C:\Admin\Evidence\members-recursive.csv -NoTypeInformation -Encoding UTF8
Не смешивайте количество прямых и конечных участников в отчёте. Для межлесовых объектов могут понадобиться доступность другого леса и разрешения на чтение; ошибка не равна отсутствию членства. CSV содержит персональные сведения, поэтому храните его в административной папке с ограниченной ACL.
7. Найти неактивные компьютеры без автоматического удаления
LastLogonDate основан на реплицируемом lastLogonTimestamp и не является точным последним входом. Ноутбук в длительной командировке, резервный сервер и выключенная VM могут быть законными объектами. Выборка — кандидаты для сверки с инвентаризацией, владельцами и журналами.
$candidates = Search-ADAccount -AccountInactive -ComputersOnly -TimeSpan 90.00:00:00 -SearchBase $ou -Server $dc
$candidates | ForEach-Object {
Get-ADComputer $_.DistinguishedName -Server $dc -Properties LastLogonDate,OperatingSystem,Enabled
} | Select-Object Name,ObjectGUID,Enabled,LastLogonDate,OperatingSystem,DistinguishedName |
Export-Csv C:\Admin\Evidence\inactive-computers.csv -NoTypeInformation -Encoding UTF8
Если нужна точная оценка последней доменной аутентификации, собирают нереплицируемый lastLogon со всех DC и выбирают максимальное значение, учитывая недоступные источники. Даже это не показывает все локальные входы. Search-ADAccount.
8. Отключить подтверждённо выведенный компьютер
Выберите один объект из согласованного списка. Не передавайте всю предыдущую выборку напрямую в Disable-ADAccount: ошибки инвентаризации превратятся в массовую остановку. Отключение аккаунта компьютера может нарушить его доверие и доступ к доменным ресурсам.
$c = Get-ADComputer 'LAB-PC01' -Server $dc -Properties Enabled
$c | Select-Object ObjectGUID,Enabled,DistinguishedName |
Export-Clixml C:\Admin\Evidence\computer-before.xml
Disable-ADAccount $c.ObjectGUID -Server $dc -WhatIf
Disable-ADAccount $c.ObjectGUID -Server $dc
Get-ADComputer $c.ObjectGUID -Server $dc | Select-Object Name,Enabled
# Возврат исходного Enabled после согласования
$oldComputer = Import-Clixml C:\Admin\Evidence\computer-before.xml
if ($oldComputer.Enabled) { Enable-ADAccount $oldComputer.ObjectGUID -Server $dc }
Отдельно оформляют карантинную OU и GPO, если это предусмотрено процедурой. Перемещение объекта меняет применяемые политики; его нельзя считать безвредной заменой отключения. После возврата проверьте secure channel на самом клиенте, DNS и время, не начинайте с повторного ввода в домен.
9. Найти компьютеры по ОС и сформировать инвентаризационный отчёт
OperatingSystem в AD — полезный ориентир, но не подтверждение установленного build, патчей и состояния поддержки. Атрибут может быть устаревшим. Отчёт следует сверять с системой управления устройствами.
Get-ADComputer -Filter 'OperatingSystem -like "Windows*"' -SearchBase $ou -Server $dc -Properties OperatingSystem,OperatingSystemVersion,LastLogonDate |
Select-Object Name,OperatingSystem,OperatingSystemVersion,LastLogonDate,Enabled |
Sort-Object OperatingSystem,Name |
Export-Csv C:\Admin\Evidence\windows-inventory.csv -NoTypeInformation -Encoding UTF8
На разрешённом доступном хосте можно отдельно получить Win32_OperatingSystem через CIM. Не трактуйте отсутствие ответа как факт отсутствия компьютера: firewall, WinRM, отключённое питание и права дают разные причины. Для планирования миграции фиксируйте также приложение, владельца, совместимость и срок замены.
10. Удалить аккаунт только после карантина и подготовленного восстановления
Удаление — финальная стадия вывода из эксплуатации, а не способ временно запретить вход. Export-Clixml сохраняет сведения для расследования, но не является резервной копией AD и не восстанавливает пароль или исходный SID. До удаления нужна проверенная процедура восстановления, например включённая AD Recycle Bin с подходящим сроком хранения; её включение — отдельное изменение леса.
Get-ADOptionalFeature -Filter 'Name -like "Recycle Bin Feature"' -Server $dc |
Select-Object Name,EnabledScopes
$u = Get-ADUser 'lab.retired' -Server $dc -Properties memberOf,Enabled
$u | Export-Clixml C:\Admin\Evidence\retired-user.xml
if ($u.Enabled) { throw 'Сначала согласованный карантин и отключение' }
Remove-ADUser -Identity $u.ObjectGUID -Server $dc -WhatIf
# Выполнять только при подтверждённом выводе и готовом восстановлении
Remove-ADUser -Identity $u.ObjectGUID -Server $dc -Confirm
После удаления проверяют отсутствие объекта, необходимые записи аудита и доступность зависимых процессов. Восстановление через Restore-ADObject проверяется в лаборатории заранее: оно не гарантирует возврата всех внешних разрешений и приложений. Restore-ADObject. Критерий хорошей операции — понятный объект, ограниченное изменение, доказанный результат и воспроизводимый возврат.
Нужно автоматизировать обслуживание Active Directory?
Скрипты эффективны вместе с контролем изменений, резервным копированием, журналами, моделью прав и проверяемым откатом.
