· 18 мин чтения

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.

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.