· 15 мин чтения

Task Scheduler через PowerShell: создаём, управляем и развёртываем задачи на парке серверов

Я Семёнов Евгений Сергеевич, директор АйТи Фреш. За 15+ лет на серверах у нас на клиентских парках работают сотни scheduled tasks: ежедневные бэкапы, отчёты по AD, чистки логов, синхронизации с 1С, выгрузки в ФНС. И я всегда настаиваю, чтобы задачи создавались из PowerShell — через модуль ScheduledTasks, а не кликами в GUI. Потому что скрипт-создание: воспроизводимо, читается как документация, деплоится на 50 серверов за минуту, сохраняется в git. В этой статье — как я сам настраиваю планировщик на клиентских серверах, включая сервисные учётки gMSA и массовое развёртывание.

Модуль ScheduledTasks

В Windows Server 2012+ и Windows 8+ есть встроенный модуль ScheduledTasks. Набор команд:

КомандлетЧто делает
New-ScheduledTaskActionОписание, что выполнять (powershell.exe + аргументы)
New-ScheduledTaskTriggerКогда запускать (Daily, Weekly, AtLogon, AtStartup)
New-ScheduledTaskPrincipalОт какой учётки, с какими правами
New-ScheduledTaskSettingsSetНастройки: ExecutionTimeLimit, RestartOnFailure и т.д.
Register-ScheduledTaskСоздать и зарегистрировать задачу
Get-ScheduledTaskПросмотр задач
Start-ScheduledTaskЗапустить сейчас
Export-ScheduledTask / Register-ScheduledTask -XmlЭкспорт/импорт в XML

Базовый пример

$action = New-ScheduledTaskAction -Execute 'powershell.exe' `
    -Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Scripts\Reports\Daily-AD.ps1"' `
    -WorkingDirectory 'C:\Scripts\Reports'

$trigger = New-ScheduledTaskTrigger -Daily -At 7:30am

$principal = New-ScheduledTaskPrincipal `
    -UserId 'CORP\svc_scripts' `
    -LogonType Password `
    -RunLevel Highest

$settings = New-ScheduledTaskSettingsSet `
    -AllowStartIfOnBatteries `
    -DontStopIfGoingOnBatteries `
    -StartWhenAvailable `
    -ExecutionTimeLimit (New-TimeSpan -Hours 1) `
    -RestartCount 2 `
    -RestartInterval (New-TimeSpan -Minutes 5)

Register-ScheduledTask -TaskName 'Daily-AD-Report' `
    -TaskPath '\ITFresh\' `
    -Action $action -Trigger $trigger -Principal $principal -Settings $settings `
    -Description 'Ежедневный отчёт по неактивным AD-учёткам в 7:30' `
    -Force

При регистрации задач с LogonType Password нужно передать пароль:

$pwd = Read-Host -AsSecureString 'Password for svc_scripts'
Register-ScheduledTask ... -User 'CORP\svc_scripts' -Password (
    [Runtime.InteropServices.Marshal]::PtrToStringAuto(
        [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd)))

Группа Managed Service Account (gMSA)

Пароли в Task Scheduler — боль: их нужно где-то хранить, менять каждые 3 месяца, обновлять на всех серверах. gMSA решает это: AD сам ротирует пароль каждые 30 дней, в задаче хранится только имя учётки.

# На DC — создание KDS root key (один раз на лес)
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))

# Создаём gMSA с доступом для конкретных серверов
New-ADServiceAccount -Name gmsa-scripts -DNSHostName gmsa-scripts.corp.itfresh.local `
    -PrincipalsAllowedToRetrieveManagedPassword (Get-ADComputer 'srv-task01')

# На целевом сервере
Install-WindowsFeature RSAT-AD-PowerShell
Install-ADServiceAccount gmsa-scripts
Test-ADServiceAccount gmsa-scripts   # должно вернуть True

# В задаче используем учётку без пароля
$principal = New-ScheduledTaskPrincipal -UserId 'CORP\gmsa-scripts$' `
    -LogonType Password -RunLevel Highest
Register-ScheduledTask ... -Principal $principal

Я всегда использую gMSA для задач, которым нужен доступ к другим серверам: чтение AD, бэкап SQL, передача файлов по SMB. Пароль без нас никто не узнает, потому что его не существует в явном виде.

Триггеры: все варианты

# Ежедневно в 07:30
New-ScheduledTaskTrigger -Daily -At '07:30'

# Еженедельно по вторникам и четвергам в 22:00
New-ScheduledTaskTrigger -Weekly -DaysOfWeek Tuesday,Thursday -At '22:00'

# Ежемесячно 1-го числа в 04:00 (через XML или advanced)
$t = New-ScheduledTaskTrigger -Once -At '04:00'
# + advanced XML редактирование для монтли

# Каждые 15 минут круглосуточно
$t = New-ScheduledTaskTrigger -Once -At (Get-Date) `
    -RepetitionInterval (New-TimeSpan -Minutes 15) `
    -RepetitionDuration (New-TimeSpan -Days 3650)

# При запуске системы
New-ScheduledTaskTrigger -AtStartup

# При логоне конкретного пользователя
New-ScheduledTaskTrigger -AtLogOn -User 'CORP\operator1'

# По событию в Event Log
$trigger = New-CimInstance -ClassName MSFT_TaskEventTrigger `
    -Namespace Root/Microsoft/Windows/TaskScheduler -ClientOnly `
    -Property @{ Subscription = '' }

Мини-кейс: массовая миграция legacy-задач

Май 2025, клиент — холдинг из 6 юрлиц, 94 Windows-сервера в разных филиалах. На каждом — от 3 до 15 scheduled tasks, созданных руками за последние 10 лет разными админами. Пароли зашиты, некоторые под domain admin, половина с expired credentials. Задача — привести в порядок, централизовать, перевести на gMSA.

Что сделали за месяц:

Результат: 612 задач, 0 с зашитыми паролями, 0 под личными учётками, все с логированием в централизованный EventLog. За следующие 6 месяцев — ни одного инцидента «задача упала из-за смены пароля». Стоимость работ 210 тыс. руб.

Экспорт/импорт XML

Для массового развёртывания удобно экспортировать задачу в XML и разложить её по 50 серверам:

# На эталонном сервере
$xml = Export-ScheduledTask -TaskName 'Daily-AD-Report' -TaskPath '\ITFresh\'
Set-Content -Path '\\file\share\tasks\Daily-AD-Report.xml' -Value $xml -Encoding UTF8

# Массовый импорт
$servers = Get-Content .\task-servers.txt
$xml = Get-Content '\\file\share\tasks\Daily-AD-Report.xml' -Raw
foreach ($s in $servers) {
    Invoke-Command -ComputerName $s -ScriptBlock {
        Register-ScheduledTask -Xml $using:xml -TaskName 'Daily-AD-Report' `
            -TaskPath '\ITFresh\' -Force
    }
}

Мониторинг выполнения задач

# Все задачи с их последним результатом
Get-ScheduledTask -TaskPath '\ITFresh\*' |
    Get-ScheduledTaskInfo |
    Select-Object TaskName, LastRunTime,
        @{N='LastResult';E={ '0x{0:X8}' -f $_.LastTaskResult }},
        NextRunTime

# Задачи, упавшие в последний раз
Get-ScheduledTask | Get-ScheduledTaskInfo |
    Where-Object { $_.LastTaskResult -ne 0 -and $_.LastTaskResult -ne 267009 } |
    Format-Table TaskName, LastRunTime, LastTaskResult

# Проход по всем серверам через WinRM
$report = Invoke-Command -ComputerName (Get-ADComputer -Filter { OperatingSystem -like '*Server*' }).DNSHostName -ScriptBlock {
    Get-ScheduledTask -TaskPath '\ITFresh\*' | Get-ScheduledTaskInfo |
        Select-Object @{N='Host';E={ $env:COMPUTERNAME }}, TaskName, LastRunTime, LastTaskResult
}
$report | Where-Object LastTaskResult -ne 0 |
    Export-Excel -Path .\failed-tasks.xlsx -AutoSize

Логирование и алерты

Я всегда добавляю в задачи:

Типичные ошибки

Наведём порядок в ваших задачах Windows

Проведу аудит всех scheduled tasks на ваших серверах, переведу на gMSA, добавлю логирование и алерты, напишу централизованный отчёт. От 10 серверов до сотен узлов, 15+ лет опыта с корпоративными Windows-инфраструктурами. Выделенный сервер автоматизации — Dell с Xeon Platinum 8280 и 40G Mellanox в дата-центре МТС, если нужно.

Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш

FAQ — частые вопросы по Scheduled Tasks

От какой учётки запускать задачи?
Сервисная учётка с минимальными правами. Для регулярных задач в домене — gMSA (Group Managed Service Account), пароль ротирует сам AD. Под SYSTEM — только для системных задач, не для скриптов с доступом к сети.
Как правильно указать аргумент PowerShell?
powershell.exe -NoProfile -ExecutionPolicy Bypass -File 'C:\Scripts\daily.ps1'. Если параметры — через -Command. Не забудьте полный путь к скрипту и -NoProfile для скорости.
Как задать запуск каждые 15 минут круглосуточно?
New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 15) -RepetitionDuration (New-TimeSpan -Days 3650). Или через -Daily + повтор в advanced-настройках.
Почему задача помечена Running, но ничего не делает?
Типичная причина — скрипт ждёт ввода или упёрся в неинтерактивную сессию. Добавьте -NonInteractive и -WindowStyle Hidden. Проверьте права запуска и, что скрипт не требует user interaction.
Как массово развернуть задачу на 50 серверов?
Экспортировать в XML (Export-ScheduledTask), через Invoke-Command импортировать на каждом. Или через DSC ресурс ScheduledTask из ComputerManagementDsc. Или через GPO Preferences — Scheduled Tasks.

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

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

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

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