PowerShell для Active Directory: 12 рабочих скриптов на 2026 год
Меня зовут Семёнов Евгений Сергеевич, я директор АйТи Фреш. За 15 лет администрирования Active Directory я собрал библиотеку из примерно 80 PowerShell-скриптов, которые автоматизируют 90% рутины. В этой статье покажу 12 самых полезных — от bulk-создания пользователей до бэкапа GPO. Все скрипты протестированы на Windows Server 2019, 2022 и 2025, работают в продакшене у моих клиентов.
Подготовка: модуль и права
Перед тем как запускать любой скрипт, проверяю наличие модуля ActiveDirectory:
# На Windows Server
Install-WindowsFeature RSAT-AD-PowerShell
# На Windows 11
Get-WindowsCapability -Online | Where-Object {$_.Name -like "Rsat.ActiveDirectory*"} |
Add-WindowsCapability -Online
# Проверка
Import-Module ActiveDirectory
Get-Command -Module ActiveDirectory | Measure-Object
Под автоматические задачи в Task Scheduler создаю gMSA — managed service account, у которого пароль ротируется автоматически:
# Создание KDS Root Key (один раз на лес)
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))
# Создание gMSA
New-ADServiceAccount -Name "gmsa-adscripts" `
-DNSHostName "gmsa-adscripts.corp.itfresh.ru" `
-PrincipalsAllowedToRetrieveManagedPassword "DC01$","DC02$"
# Установка на сервере, где будут крутиться скрипты
Install-ADServiceAccount -Identity "gmsa-adscripts"
Скрипт 1. Массовое создание пользователей из CSV
HR присылает CSV с новыми сотрудниками — за минуту все создаются в AD с правильной OU, группами и почтой:
$users = Import-Csv -Path "C:\hr\new_users.csv" -Encoding UTF8 -Delimiter ";"
foreach ($u in $users) {
$login = ConvertTo-Translit -Text "$($u.Surname).$($u.Name.Substring(0,1))"
$password = ConvertTo-SecureString "Welcome$(Get-Random -Min 1000 -Max 9999)!" -AsPlainText -Force
New-ADUser `
-Name "$($u.Surname) $($u.Name) $($u.Patronymic)" `
-GivenName $u.Name `
-Surname $u.Surname `
-DisplayName "$($u.Surname) $($u.Name)" `
-SamAccountName $login `
-UserPrincipalName "$login@corp.itfresh.ru" `
-EmailAddress "$login@itfresh.ru" `
-Department $u.Department `
-Title $u.Position `
-Office $u.Office `
-Path "OU=Standard,OU=Users,DC=corp,DC=itfresh,DC=ru" `
-AccountPassword $password `
-ChangePasswordAtLogon $true `
-Enabled $true
Add-ADGroupMember -Identity "GG-$($u.Department)" -Members $login
Write-Host "Создан: $login / пароль отправлен в почту руководителю"
}
Скрипт 2. Onboarding нового сотрудника
Заявка из Битрикс24 — скрипт создаёт учётку, заводит почту, выдаёт лицензию Microsoft 365, добавляет в группы доступа к 1С:
function New-CorpUser {
param(
[Parameter(Mandatory)] [string]$Surname,
[Parameter(Mandatory)] [string]$Name,
[Parameter(Mandatory)] [string]$Department,
[string]$Manager,
[string[]]$Groups
)
$login = (ConvertTo-Translit "$Surname.$($Name.Substring(0,1))").ToLower()
$tempPass = -join ((48..57)+(65..90)+(97..122) | Get-Random -Count 14 | ForEach-Object {[char]$_})
New-ADUser -Name "$Surname $Name" -SamAccountName $login `
-UserPrincipalName "$login@corp.itfresh.ru" `
-GivenName $Name -Surname $Surname `
-Department $Department -Manager $Manager `
-Path "OU=$Department,OU=Users,DC=corp,DC=itfresh,DC=ru" `
-AccountPassword (ConvertTo-SecureString $tempPass -AsPlainText -Force) `
-ChangePasswordAtLogon $true -Enabled $true
foreach ($g in $Groups) { Add-ADGroupMember -Identity $g -Members $login }
# Создание домашней папки
$home = "\\fileserver\users\$login"
New-Item -Path $home -ItemType Directory
$acl = Get-Acl $home
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("CORP\$login","FullControl","ContainerInherit,ObjectInherit","None","Allow")
$acl.AddAccessRule($rule)
Set-Acl -Path $home -AclObject $acl
Write-Output @{ Login=$login; Password=$tempPass; Home=$home }
}
Скрипт 3. Offboarding при увольнении
Сотрудник уволен в 18:00 — за 30 секунд блокируем учётку, выкидываем из всех групп, убираем в OU «Disabled», конвертируем почту в shared mailbox:
function Disable-CorpUser {
param([Parameter(Mandatory)] [string]$Login)
$u = Get-ADUser $Login -Properties MemberOf,Manager
Write-Host "Отключаем: $($u.Name)"
Disable-ADAccount -Identity $Login
# Сброс пароля на случайный
Set-ADAccountPassword -Identity $Login -Reset -NewPassword `
(ConvertTo-SecureString (-join((33..126)|Get-Random -Count 32|%{[char]$_})) -AsPlainText -Force)
# Удаление из всех групп кроме Domain Users
$u.MemberOf | ForEach-Object {
Remove-ADGroupMember -Identity $_ -Members $Login -Confirm:$false
}
# Перенос в Disabled OU
Move-ADObject -Identity $u.DistinguishedName `
-TargetPath "OU=_Disabled,DC=corp,DC=itfresh,DC=ru"
# Метка с датой увольнения в Description
Set-ADUser -Identity $Login -Description "Disabled $(Get-Date -Format yyyy-MM-dd)"
Write-Host "Готово. Перенесён в _Disabled."
}
Скрипт 4. Аудит неактивных учёток
Раз в месяц нахожу всех, кто не логинился больше 90 дней — кандидаты на отключение:
$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter {Enabled -eq $true -and LastLogonTimestamp -lt $cutoff} `
-Properties LastLogonTimestamp, Department, Manager |
Select-Object Name, SamAccountName, Department,
@{N="LastLogon";E={[DateTime]::FromFileTime($_.LastLogonTimestamp)}} |
Sort-Object LastLogon |
Export-Csv "C:\reports\inactive_users_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation -Encoding UTF8
Скрипт 5. Отчёт по истечению паролей
Утром приходит отчёт: чей пароль истечёт в ближайшие 7 дней — рассылаем напоминания:
$soon = (Get-Date).AddDays(7)
Get-ADUser -Filter {Enabled -eq $true} `
-Properties msDS-UserPasswordExpiryTimeComputed, EmailAddress |
Where-Object {
$exp = [DateTime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")
$exp -lt $soon -and $exp -gt (Get-Date)
} |
Select-Object Name, SamAccountName, EmailAddress,
@{N="Expires";E={[DateTime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed")}}
Скрипт 6. Аудит членства в Domain Admins
Критичная проверка: кто состоит в привилегированных группах. Запускается раз в неделю, отчёт уходит руководителю:
$privileged = @("Domain Admins","Enterprise Admins","Schema Admins","Account Operators","Backup Operators")
foreach ($g in $privileged) {
Get-ADGroupMember -Identity $g -Recursive |
Select-Object @{N="Group";E={$g}}, Name, SamAccountName, ObjectClass
} | Export-Csv "C:\reports\privileged_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation -Encoding UTF8
Скрипт 7. Бэкап всех GPO ежедневно
$path = "\\nas01\backup\GPO\$(Get-Date -Format yyyy-MM-dd)"
New-Item -Path $path -ItemType Directory -Force | Out-Null
Backup-GPO -All -Path $path -Comment "Daily auto-backup $(Get-Date)"
# Удаление бэкапов старше 30 дней
Get-ChildItem "\\nas01\backup\GPO" -Directory |
Where-Object {$_.CreationTime -lt (Get-Date).AddDays(-30)} |
Remove-Item -Recurse -Force
Скрипт 8. Поиск компьютеров без бэкапа BitLocker-ключа
Get-ADComputer -Filter {Enabled -eq $true} -Properties OperatingSystem |
Where-Object {$_.OperatingSystem -like "Windows 1*"} |
ForEach-Object {
$hasKey = Get-ADObject -Filter {objectClass -eq "msFVE-RecoveryInformation"} `
-SearchBase $_.DistinguishedName -SearchScope OneLevel
[PSCustomObject]@{
Computer = $_.Name
HasBitLockerKey = [bool]$hasKey
}
} | Where-Object {-not $_.HasBitLockerKey}
Скрипт 9. Проверка репликации между DC
$failures = Get-ADReplicationFailure -Target (Get-ADDomainController -Filter *).Name
if ($failures) {
$body = $failures | ConvertTo-Html | Out-String
Send-MailMessage -From "ad-monitor@itfresh.ru" -To "admin@itfresh.ru" `
-Subject "AD Replication Failure!" -Body $body -BodyAsHtml `
-SmtpServer "mail.itfresh.ru"
}
Скрипт 10. Очистка отключённых учёток старше 180 дней
$cutoff = (Get-Date).AddDays(-180)
Get-ADUser -Filter {Enabled -eq $false} -Properties whenChanged |
Where-Object {$_.whenChanged -lt $cutoff -and $_.DistinguishedName -like "*OU=_Disabled*"} |
ForEach-Object {
Write-Host "Удаляю: $($_.SamAccountName)"
Remove-ADUser -Identity $_ -Confirm:$false
}
Скрипт 11. Поиск пустых групп
Get-ADGroup -Filter * -Properties Members |
Where-Object {$_.Members.Count -eq 0} |
Select-Object Name, GroupCategory, GroupScope, DistinguishedName
Скрипт 12. Health-check домена
# Сборка единого отчёта
$report = @{
Domain = (Get-ADDomain).DNSRoot
Forest = (Get-ADForest).Name
DCs = (Get-ADDomainController -Filter *).Count
Users = (Get-ADUser -Filter {Enabled -eq $true}).Count
Computers = (Get-ADComputer -Filter {Enabled -eq $true}).Count
Groups = (Get-ADGroup -Filter *).Count
ReplicationFailures = (Get-ADReplicationFailure -Target (Get-ADDomainController -Filter *).Name).Count
FSMORoles = (Get-ADDomainController -Filter * | Where-Object {$_.OperationMasterRoles}).Name
}
$report | Format-List
dcdiag /v /e | Out-File "C:\reports\dcdiag_$(Get-Date -Format yyyyMMdd).txt"
Логирование, обработка ошибок и нотификации
Любой production-скрипт обязан писать журнал и присылать уведомление, когда что-то пошло не так. Иначе скрипт молча падает три недели подряд, и админ узнаёт об этом, когда уже накопилась куча проблем. Мой минимальный шаблон-обёртка для каждого скрипта:
$LogPath = "C:\Logs\AD\$(Get-Date -Format yyyy-MM-dd)_$($MyInvocation.MyCommand.Name).log"
Start-Transcript -Path $LogPath -Append
try {
# === основной код скрипта ===
Write-Host "[$(Get-Date)] Старт"
# пример действия
$created = New-CorpUser -Surname "Иванов" -Name "Сергей" -Department "Sales" -Groups @("GG-Sales","GG-1C-Users")
Write-Host "[$(Get-Date)] Создан пользователь $($created.Login)"
Write-Host "[$(Get-Date)] Готово"
} catch {
$err = "$($_.Exception.Message)`n$($_.ScriptStackTrace)"
Write-Host "[$(Get-Date)] ОШИБКА: $err" -ForegroundColor Red
# Отправка в Telegram-бот мониторинга
$tgToken = "BOT_TOKEN"
$tgChat = "-100xxxxxxxxxx"
$message = "AD-script $($MyInvocation.MyCommand.Name) FAILED on $env:COMPUTERNAME`n$err"
Invoke-RestMethod -Uri "https://api.telegram.org/bot$tgToken/sendMessage" -Method Post `
-Body @{chat_id=$tgChat; text=$message; parse_mode="Markdown"}
} finally {
Stop-Transcript
}
Логи храню 90 дней, по утрам через скрипт-агрегатор сканирую ключевое слово ERROR — если за ночь что-то упало, в Telegram прилетает сводка.
Запуск по расписанию через Task Scheduler
$action = New-ScheduledTaskAction -Execute "pwsh.exe" `
-Argument "-NoProfile -File C:\Scripts\Backup-GPO.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 02:00
$principal = New-ScheduledTaskPrincipal -UserId "CORP\gmsa-adscripts$" -LogonType Password
Register-ScheduledTask -TaskName "AD-Backup-GPO-Daily" `
-Action $action -Trigger $trigger -Principal $principal
Внедрение автоматизации Active Directory
Готов адаптировать эти скрипты под вашу инфраструктуру и поднять автоматизацию onboarding, аудита, бэкапов и мониторинга AD за 5–10 рабочих дней. Бесплатный аудит существующего домена для новых клиентов.
Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш
FAQ — частые вопросы по PowerShell в AD
- Какой модуль PowerShell нужен для работы с Active Directory?
- Модуль ActiveDirectory из RSAT. На Windows Server: Install-WindowsFeature RSAT-AD-PowerShell. На Windows 11: Get-WindowsCapability -Online | Where-Object {$_.Name -like 'Rsat.ActiveDirectory*'} | Add-WindowsCapability -Online. После этого доступно 147 командлетов.
- Как безопасно тестировать скрипты, которые меняют AD?
- Используйте параметр -WhatIf — он показывает, что произойдёт, без реального выполнения. Большинство командлетов AD его поддерживают. Боевые скрипты сначала тестируйте в изолированной лаборатории на Hyper-V с копией продуктивного домена.
- Под какой учёткой запускать скрипты в Task Scheduler?
- Group Managed Service Account (gMSA) — пароль ротируется автоматически каждые 30 дней. Создаётся через New-ADServiceAccount, права делегируются точечно. Никогда не используйте Domain Admin для автоматических задач.
- Как обработать кириллицу в SamAccountName?
- Транслитерация по ГОСТ 7.79-2000. Пишется простой функцией с хеш-таблицей: А=A, Б=B, Ж=Zh, Х=Kh, Ы=Y, Я=Ya и т.д. Обязательно проверить уникальность через Get-ADUser -Filter {SamAccountName -eq $login} перед New-ADUser.
- Чем PowerShell 7 лучше Windows PowerShell 5.1 для AD?
- PS7 быстрее, поддерживает параллельные циклы (ForEach-Object -Parallel), лучше работает с REST. Но модуль ActiveDirectory официально поддерживается и в 5.1, и в 7. Для скриптов автоматизации AD в 2026 я уже массово перевёл клиентов на PS7.