Планировщик заданий не запускается или завершается с 0x1 — диагностика и надёжная эксплуатация
· Обновлено: · Го Комура · Планировщик заданий, Windows, PowerShell, Автоматизация бизнес-процессов, Пакетная обработка, Эксплуатация, Устранение неполадок, Техническая консультация
История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619925)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Планировщик заданий не запускается или завершается с 0x1 — диагностика и надёжная эксплуатация. KomuraSoft LLC. https://comcomponent.com/ru/blog/windows-task-scheduler-reliable-scheduled-tasks/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21619925
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.21619926
«Хочу, чтобы PowerShell-скрипт для сводки запускался каждое утро в 6». «Вручную скрипт работает, а стоит поставить его в Планировщик заданий — перестаёт». Когда речь заходит об автоматизации бизнес-процессов, разговор почти всегда рано или поздно упирается именно в это.
В этом блоге уже шла серия про автоматизацию: обслуживание логов, тесты скриптов на Pester, запуск PowerShell из C# и автоматизация бизнес-процессов в Power Automate. Все эти статьи в итоге исходят из того, что задачу будут запускать по расписанию через Планировщик заданий. Но сам Планировщик заданий оказывается на удивление капризным механизмом: «вручную работает, по расписанию — нет», «никто не заметил, что задача давно не выполняется» — такие истории не кончаются.
В этой статье разберём те части Планировщика заданий, которые напрямую приводят к эксплуатационным инцидентам: учётная запись выполнения и тип входа, диагностика ситуации «не запускается», типичные причины возврата 0x1, как вести журнал и как ограничивать повторный запуск — в том порядке, в котором с этим обычно сталкиваются на практике.
Предпосылки этой статьи
| Пункт | Содержание |
|---|---|
| Целевая ОС | Статья рассчитана на Планировщик заданий Windows 10 / 11 и Windows Server 2016 и новее (линейка Task Scheduler 2.0). Если в среде ещё остались задачи от старой команды at, начните с их инвентаризации |
| Кому адресовано | Сотрудники ИТ и разработки, которые умеют писать PowerShell или пакетные файлы, но стопорятся, когда нужно поставить это на периодический запуск |
| Операции | Разбираем и GUI (taskschd.msc), и модуль PowerShell ScheduledTasks. Снимков экрана нет. Вместо них имена вкладок, полей и кнопок даны как в самой программе — откройте Планировщик заданий и читайте параллельно |
| Доменная среда | gMSA (раздел 3.3) — только про домен. В рабочей группе этот фрагмент можно пропустить |
1. Сначала — выводы
- Большинство проблем с Планировщиком заданий возникает не из-за скрипта, а из-за расхождения в понимании того, «от чьего имени и в какой сессии выполняется задача». Как только вы выбираете «Выполнять независимо от того, выполнен ли вход пользователя в систему», проектируйте решение исходя из того, что оно работает в мире, отличном и от интерактивной сессии, и от окружения при входе.1
- Расследование ситуации «не запускается» начинается с вкладки «Журнал» (History) и журнала событий. Журнал задач по умолчанию выключен, поэтому до ввода в эксплуатацию обязательно включите «Включить журнал всех задач».2
0x1в поле «Результат последнего выполнения» — это не ошибка Планировщика заданий, а значит, что запущенная программа сама вернула код завершения 1. Причина на стороне скрипта: сначала спроектируйте коды завершения и собственный механизм журнала.3- Базовая форма вызова PowerShell-скрипта —
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "полный путь", а пути внутри скрипта стройте от$PSScriptRoot. Именно здесь легко попасть в классическую ловушку: в поле «Начать в» кавычки ставить нельзя. - Чтобы задача не умерла молча после смены пароля, нужно продумать учётную запись выполнения (инвентаризация служебных учётных записей, в домене — рассмотрение gMSA).4
- Контроль повторного запуска (по умолчанию — «Не запускать новый экземпляр»), ограничение времени выполнения (по умолчанию 3 дня) и условие питания (по умолчанию — только от сети) — типичные параметры, которые незаметно остаются на значениях по умолчанию. Задавайте их явно при регистрации.5
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 19, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Устройство Планировщика заданий — триггеры, действия, условия, параметры
Задача Планировщика заданий состоит из четырёх основных элементов.
| Элемент | Содержание | Где чаще всего ломается |
|---|---|---|
| Триггер | Когда запускается (время, вход в систему, событие и т. д.) | Что делать, если пропущен триггер по времени (StartWhenAvailable, см. ниже) |
| Действие | Что выполняется (программа, аргументы, начальная папка) | Кавычки в аргументах, ошибка в начальной папке |
| Условия | При каких обстоятельствах выполнение разрешено (питание, сеть, простой) | По умолчанию включено «только при питании от сети» |
| Параметры | Поведение во время выполнения (повторный запуск, ограничение времени, повторные попытки) | Эксплуатацию начинают, не проверив значения по умолчанию |
Задачу, созданную в GUI (taskschd.msc), можно экспортировать в XML. Если определения задач хотите вести в Git или раздать одну и ту же задачу на несколько машин, лучше описать её кодом: экспорт XML плюс schtasks /Create /XML либо модуль PowerShell ScheduledTasks (New-ScheduledTaskAction / New-ScheduledTaskTrigger / New-ScheduledTaskSettingsSet / Register-ScheduledTask).5
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"' `
-WorkingDirectory 'C:\Jobs'
$trigger = New-ScheduledTaskTrigger -Daily -At '06:00'
# Условие питания по умолчанию: «запуск только от сети, остановка при переходе на батарею».
# Если задание должно работать и на ноутбуке или полевом устройстве, разрешите это здесь явно
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
-MultipleInstances IgnoreNew `
-ExecutionTimeLimit (New-TimeSpan -Hours 2) `
-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
# Учётные данные берём через Get-Credential, чтобы пароль не светился на экране
$cred = Get-Credential -UserName 'DOMAIN\svc-batch' -Message 'Учётные данные учётной записи выполнения'
Register-ScheduledTask -TaskName 'KS-Cleanup-Logs' `
-Action $action -Trigger $trigger -Settings $settings `
-User $cred.UserName -Password $cred.GetNetworkCredential().Password
Если определение задачи оформлено как код, проверенное на тестовой машине можно перенести в продакшен как есть, и не возникнет ситуации «непонятно, с какими параметрами это вообще работает».
Для развёртывания на нескольких машинах хорошо зарекомендовал себя и другой способ: экспортировать задачу, собранную в GUI, в XML и раздать её через schtasks.
rem Экспортируем задачу, созданную на тестовой машине
schtasks /Query /TN "KS-Cleanup-Logs" /XML > KS-Cleanup-Logs.xml
rem Импортируем на каждой машине (учётная запись выполнения и пароль указываются при регистрации)
schtasks /Create /TN "KS-Cleanup-Logs" /XML KS-Cleanup-Logs.xml /RU DOMAIN\svc-batch /RP *
В XML входят все триггеры, условия и параметры, поэтому, положив его в репозиторий, можно ревьюить определения задач и смотреть diff. И наоборот: если вручную регистрировать одну и ту же задачу через GUI на десяти машинах, рано или поздно появится «блудная задача» с настройками, которые отличаются ровно на одной машине. Безопаснее перевести определение в код ещё до того, как число машин перевалит за два десятка.
3. Учётная запись выполнения и тип входа — главный источник инцидентов
Параметры в свойствах задачи — «Выполнять только для вошедших в систему пользователей» и «Выполнять независимо от того, выполнен ли вход пользователя в систему» — внутри это выбор типа входа (LogonType). Если понимание этого расходится с реальностью, вы будете блуждать, не сумев объяснить большинство случаев «вручную работает, по расписанию — нет».1
3.1 Чем отличаются три режима
| Выбор | Внутренний механизм | Особенности и ограничения |
|---|---|---|
| Выполнять только для вошедших в систему пользователей | Интерактивный токен (InteractiveToken) | Окно видно на экране во время сеанса. Пока пользователь не вошёл, задача вообще не запускается |
| Выполнять независимо от того, выполнен ли вход пользователя в систему | Сохранённый пароль (Password) | Пароль сохраняется при регистрации. Экран не показывается (неинтерактивно). Смена пароля приводит к сбою запуска |
| То же + «Не хранить пароль» | S4U | Пароль не сохраняется, но взамен нет доступа к сетевым ресурсам и к зашифрованным файлам (EFS)1 |
Типичные инциденты на практике выглядят так.
- Скрипт, который ходит в общую папку, зарегистрировали с «Не хранить пароль» (S4U). В локальном тесте всё работало, в продакшене отказывал именно доступ к общей папке. У S4U нет сетевых учётных данных.
- Задачу зарегистрировали как «Выполнять независимо от того, выполнен ли вход пользователя в систему», через несколько месяцев истёк срок действия доменного пароля и его сменили. С этого момента задача постоянно завершалась ошибкой входа (
0x8007052E), но никто этого не заметил. - Задачу для запуска GUI-приложения зарегистрировали как «независимо от входа». Приложение фактически запускалось, но окно нигде не показывалось, и это приняли за «не работает». Задача идёт в неинтерактивной сессии — обработка, которой нужен интерактивный экран, в такой конфигурации в принципе не работает.
Кроме того, учётной записи в режиме Password или S4U нужно право «Вход в качестве пакетного задания» (SeBatchLogonRight). Группе Administrators оно выдано по умолчанию, но если служебной делают обычную пользовательскую учётную запись, проверьте и локальную политику безопасности.6
3.2 От какой учётной записи запускать
- SYSTEM: не требует управления паролем и обладает широкими правами, но эти права слишком широки. Для локального обслуживания удобно, но задания, которые трогают бизнес-данные, не стоит без разбора запускать от SYSTEM. Когда права администратора действительно нужны, разобрано в статье «Когда в Windows нужны права администратора — UAC, защищённые области и как это отличить на этапе проектирования».
- Выделенная служебная учётная запись (обычный пользователь): можно сузить права до минимума, но при каждой смене пароля задачу нужно обновлять. Срок действия пароля и инвентаризацию задач ведите как один процесс.
- gMSA (групповая управляемая учётная запись службы): в доменной среде это первый кандидат. Пароль автоматически ведёт контроллер домена, поэтому проблема «задача умирает при смене пароля» исчезает как класс. Планировщик заданий поддерживает запуск от имени gMSA.4
Флажок «Выполнять с наивысшими правами» означает запуск с административным (повышенным) токеном из пары, которую разделяет UAC. Для заданий, которым права администратора не нужны, его не ставьте.
3.3 Минимальная процедура регистрации задачи на gMSA
Раз gMSA назван «первым кандидатом», покажем, как его реально зарегистрировать. Задачи Планировщика заданий официально входят в число конфигураций, которые поддерживают gMSA.4
Предпосылки такие.4
- Уровень функциональности домена и леса — Windows Server 2012 или новее
- В домене уже создан корневой ключ KDS (создание можно подтвердить событием ID 4004 в журнале Operational службы
KdsSvc) - Создавать и вести gMSA может член Domain Admins или Enterprise Admins
- Имя gMSA должно быть уникально в пределах леса, а не только домена. Если такое же имя есть в другом домене, создание не удастся
Шагов три. 1–2 делает администратор домена, 3 — на каждой машине, где будет работать задача.
# --- 1. На стороне домена: создать gMSA. Хосты, которым можно получать пароль, задаём группой безопасности ---
# В <SecurityGroup> указывают группу, куда входят компьютерные учётные записи серверов с задачей
New-ADServiceAccount -Name 'svc-batch' -DNSHostName 'svc-batch.contoso.local' `
-PrincipalsAllowedToRetrieveManagedPassword 'GG-BatchHosts'
# --- 2. На каждой машине с задачей: установить gMSA и проверить, что пароль получается ---
Install-ADServiceAccount -Identity 'svc-batch'
Test-ADServiceAccount -Identity 'svc-batch' # True — можно использовать
Третий шаг — регистрация задачи. Два ключевых момента.
# --- 3. На машине с задачей: зарегистрировать задачу с gMSA в качестве субъекта ---
$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"' `
-WorkingDirectory 'C:\Jobs'
$trigger = New-ScheduledTaskTrigger -Daily -At '06:00'
# Момент 1: в конце имени учётной записи ставят $ (форма имени gMSA)
# Момент 2: LogonType — Password. Параметр -Password при этом не передают
# (пароль gMSA ведёт контроллер домена, хост его получает сам)
$principal = New-ScheduledTaskPrincipal -UserId 'CONTOSO\svc-batch$' `
-LogonType Password -RunLevel Limited
Register-ScheduledTask -TaskName 'KS-Cleanup-Logs' `
-Action $action -Trigger $trigger -Principal $principal
У New-ScheduledTaskPrincipal для -LogonType допустимы None / Password / S4U / Interactive / Group / ServiceAccount / InteractiveOrPassword.7 Как видно из таблицы в 3.1, у S4U нет доступа к сетевым ресурсам — для задания, которое ходит в общую папку, его не стоит выбирать походя.
Два уточнения. Во-первых, этому gMSA тоже нужно право «Вход в качестве пакетного задания» (тот же разговор, что в конце 3.1: gMSA от него не освобождает).6 Во-вторых, schtasks.exe рассчитан на передачу пароля учётной записи через /RP, и официальной процедуры регистрации на gMSA для него нет. Если используете gMSA, регистрацию задачи надёжнее вести через PowerShell Register-ScheduledTask. Если сочетаете это со схемой из главы 2 «экспорт XML и раздача через schtasks», субъект (principal) перезаписывайте уже PowerShell.
4. Как диагностировать ситуацию «не запускается»
4.1 Сначала включите журнал, потом ищите причину
Пункт «Включить журнал всех задач» на правой панели Планировщика заданий по умолчанию выключен. Пока журнал выключен, в записях не остаётся даже сам факт сбоя. Включите его до ввода в эксплуатацию. Порядок такой.
- Запустите Планировщик заданий (
taskschd.mscили поиск «Планировщик заданий» в меню «Пуск»). Нужны права администратора. - В дереве слева выберите самый верхний узел «Планировщик заданий (локальный)». Если выбрана отдельная задача, этот пункт не появится.
- В списке «Операции» справа нажмите «Включить журнал всех задач». Если журнал уже включён, пункт выглядит как «Отключить журнал всех задач».
Фактически журнал — это журнал событий Журналы приложений и служб > Microsoft > Windows > TaskScheduler > Operational.2 Вкладка «Журнал» отдельной задачи — это тот же журнал, отфильтрованный по этой задаче, поэтому если нужно смотреть несколько задач по времени, открывайте Просмотр событий.
Базовая диагностика хорошо работает, если просто идти по порядку из руководства Microsoft по устранению неполадок.2
- Проверить скрипт отдельно — до помещения в задачу убедиться, что сам скрипт полностью отрабатывает в условиях, близких к учётной записи выполнения (по возможности через
runasили на тестовой машине). - Посмотреть столбец состояния и вкладку «Журнал» — понять, сработал ли триггер вообще или задача стартовала и упала. Если триггер не сработал — проверить настройки триггера и условия (питание, сеть) и убедиться, что само действие работает при ручном запуске (правой кнопкой → «Выполнить»).
- Временно переключить на «Выполнять только для вошедших в систему пользователей» — если после этого заработало, причину можно сузить до неинтерактивной сессии или учётных данных (предыдущая глава).
4.2 Что на вкладке «Журнал» на что указывает — шпаргалка по идентификаторам событий
У каждой строки вкладки «Журнал» (и журнала Operational) есть идентификатор события. По этому ID сразу видно, докуда выполнение дошло. Нормальный один запуск задачи обычно идёт в порядке «начало (100) → запуск процесса (129) → начало действия (200) → завершение действия (201) → завершение (102)».
| Идентификатор события | Смысл сообщения | Что подозревать, когда оно появилось |
|---|---|---|
| 106 | Пользователь зарегистрировал задачу8 | Запись о регистрации. Отправная точка, когда нужно понять, «кто и когда менял» |
| 140 / 141 | Пользователь обновил / удалил задачу8 | Поиск виновного в «вчера ещё работало» начинается здесь |
| 113 | Задачу зарегистрировали, но часть триггеров задачу не запускает8 | Дефект определения триггера. Предупреждение уже на этапе регистрации |
| 116 | Конфигурацию задачи сохранить удалось, но учётные данные для выполнения сохранить не удалось8 | Учётная запись выполнения и пароль. Если это событие есть, задача, разумеется, не поедет |
| 100 | Задачу начали9 | Если его нет, либо триггер вообще не сработал, либо старт сорвался на 101 |
| 101 | Задачу начать не удалось. С кодом ошибки9 | Триггер сработал, запуск нет. Смотрите учётные данные (0x8007052E и т. п.) и права. 100 при этом нет |
| 129 | Задачу запустили с идентификатором процесса9 | Процесс создан. Дальше — уже история скрипта |
| 200 / 201 | Действие (action) начали / действие завершилось9 | 201 значит «запущенная программа завершилась», а не успех или неудачу содержимого. В текущих Windows у 201 в тексте и в данных события есть код возврата (ResultCode) — его и смотрят (раздел 4.3) |
| 202 | Планировщик заданий не смог завершить действие. С кодом ошибки9 | Сбой на стороне Планировщика заданий. Не факт, что оно появится, когда программа завершилась с 0x1 |
| 203 | Не удалось даже запустить действие. С кодом ошибки9 | Ошибка пути к исполняемому файлу, неверное «Начать в», нехватка прав |
| 102 | Задача штатно завершилась9 | Конец нормальной цепочки |
| 111 | Задачу остановили, потому что превышен лимит времени выполнения9 | Достигнуто «время до остановки» (по умолчанию 3 дня). Глава 7 |
| 322 | Не запустили, потому что уже выполняется другой экземпляр той же задачи10 | Сработал контроль повторного запуска. Предыдущий запуск ещё не кончился. Глава 7 |
| 323 | Остановили выполняющийся экземпляр, чтобы запустить новый9 | У MultipleInstances стоит «останавливать существующий экземпляр» |
| 327 | Экземпляр остановили, потому что питание перешло на батарею9 | Условие питания (4.4). Часто на ноутбуках и полевых устройствах |
| 328 | Экземпляр остановили, потому что компьютер вышел из простоя9 | Включено условие простоя |
| 329 | Экземпляр остановили из-за тайм-аута задачи9 | Как и 111, повод пересмотреть лимит времени |
| 330 | Экземпляр остановили по запросу пользователя9 | Кто-то остановил вручную |
Три правила чтения.
- Если нет 100, сначала смотрите, нет ли 101 (задачу начать не удалось). Если 101 есть, триггер сработал, а запуск нет — смотрите код ошибки и учётную запись выполнения (глава 3). Если нет и 101, причина раньше задачи (триггер, условия, задача отключена); 322 прямо говорит, что предыдущий экземпляр ещё не кончился.
- Если 100 есть, а 102 нет, запуск был, завершения нет. 111 / 329 — вышло время, 203 — не удалось даже запустить действие, 327 / 328 — выполняющийся экземпляр остановили условием питания или простоя (327 и 328 — это запись «остановили», а не «не начали», поэтому они идут после 100).
- Если есть и 100, и 102, а результат странный, зона ответственности Планировщика заданий отработана до конца. Дальше можно идти только по журналу скрипта (глава 6).
flowchart TD
S["Не запускается или странный результат"] --> Q1{"Есть событие 100 (начало)?"}
Q1 -->|"нет"| Q1b{"Есть событие 101?"}
Q1b -->|"есть"| A0["Триггер сработал, запуск нет<br/>смотрите код ошибки 101 и<br/>учётную запись выполнения (глава 3)"]
Q1b -->|"нет"| A1["Причина раньше задачи<br/>триггер, условия, отключение (4.4)<br/>322 — предыдущий экземпляр ещё идёт"]
Q1 -->|"есть"| Q2{"Есть событие 102 (штатное завершение)?"}
Q2 -->|"нет"| B["Запуск был, завершения нет<br/>203 = не удалось запустить действие<br/>111, 329 = вышло время<br/>327, 328 = остановили питанием или простоем<br/>202 = сбой на стороне Планировщика заданий"]
Q2 -->|"есть"| C["Планировщик заданий отработал до конца<br/>если 0x1 — скрипт вернул<br/>код завершения 1<br/>дальше только журнал скрипта (глава 6)"]
Рис. 1: По наличию событий 100 и 102 поиск уходит либо в настройки задачи, либо в скрипт
Ещё одна частая путаница. Даже если программа завершилась ненулевым кодом вроде 0x1, для Планировщика заданий это «запустили и завершили», поэтому выходит 201 (ACTION_SUCCESS). 202 — событие «Планировщик заданий не смог завершить действие», а не корзина для ненулевого кода программы.9 Поэтому, ища 0x1, события 202 можно и не найти. Смотреть нужно код возврата 201 и столбец «Результат последнего выполнения» задачи (раздел 4.3). ResultCode у 201 и «Результат последнего выполнения» не всегда совпадают, поэтому надёжнее, как в главе 6, самому записывать код завершения на стороне скрипта.
4.3 Как читать «Результат последнего выполнения»
| Значение | Смысл |
|---|---|
0x0 |
Нормальное завершение (запущенная программа вернула код 0) |
0x1 |
Запущенная программа вернула код завершения 1 (это не ошибка самого Планировщика заданий) |
0x41300 |
Ожидание следующего запланированного запуска (SCHED_S_TASK_READY) |
0x41301 |
Выполняется сейчас (SCHED_S_TASK_RUNNING) |
0x41303 |
Ещё ни разу не выполнялась (SCHED_S_TASK_HAS_NOT_RUN) |
0x8007010B |
Неверно указана начальная папка («Начать в»). Типичный симптом кавычек в этом поле |
0x8007052E |
Сбой входа. Сохранённый пароль устарел, нет права и т. п. |
Семейство 0x413xx — коды состояния Планировщика заданий, 0x8007xxxx — коды ошибок Windows, а небольшие значения вроде 0x1 или 0x2 — коды завершения самой запущенной программы.3 Если это различать, с самого начала не ищете причину не там (в настройках задачи или в скрипте).
flowchart TB
R["Значение в «Результат последнего выполнения»"]
R -->|"небольшое значение вроде 0x0 / 0x1 / 0x2"| P["Код завершения самой запущенной программы<br/>→ смотреть скрипт"]
R -->|"0x413xx"| ST["Код состояния Планировщика заданий<br/>(ожидание, выполнение, ещё не запускалась)<br/>→ это само по себе не сбой"]
R -->|"0x8007xxxx"| Q{"Действие запустилось?<br/>(есть 201, нет 203?)"}
Q -->|"не запустилось"| W["Код ошибки Windows<br/>(неверная начальная папка, сбой входа и т. п.)<br/>→ смотреть настройки задачи"]
Q -->|"запустилось"| W2["Код завершения, который вернул дочерний процесс.<br/>Приложение может вернуть его в формате HRESULT<br/>→ смотреть скрипт"]
Рис. 2: В одном поле смешаны значения трёх разных систем. При этом 0x8007xxxx нельзя судить только по префиксу: если действие уже запустилось, это значение, которое вернул дочерний процесс, — сверяйте события 201 и 203, чтобы понять источник
4.4 Следите за значениями условий и параметров по умолчанию
- Условие питания: по умолчанию включено «Запускать задачу только при питании компьютера от электросети». Если тестовая машина — ноутбук, получите «невоспроизводимый баг», который проявляется только от батареи. Кроме того, начиная с Windows 10, пока включена экономия заряда, срабатывание триггеров многих задач откладывается.11
- Пропущенное время запуска: если компьютер был выключен и назначенное время прошло, по умолчанию задача не выполнится до следующего расписания. Либо явно включите «Немедленно запускать задачу, если пропущен запланированный запуск» (
-StartWhenAvailable), либо на уровне проектирования решите, допустим ли пропуск.5 - Выход из сна: для ночного задания на ПК, который может уходить в сон, отдельно решите, нужен ли параметр «Выводить компьютер из спящего режима для выполнения этой задачи» (
-WakeToRun).
Сведём, где в GUI лежат перечисленные параметры, по вкладкам. Это диалог, который открывается правой кнопкой по задаче → «Свойства».
| Вкладка | Что здесь задают | Где в статье |
|---|---|---|
| Общие | Имя задачи, учётная запись выполнения («Изменить пользователя или группу»), «Выполнять только для вошедших в систему пользователей» / «Выполнять независимо от того, выполнен ли вход пользователя в систему», «Не хранить пароль», «Выполнять с наивысшими правами» | Глава 3 |
| Триггеры | Когда запускать. Через «Создать» добавляют время, вход, событие | Раздел 4.5 |
| Действия | Три поля: «Программа или сценарий», «Добавить аргументы (необязательно)», «Начать в». Кавычки в «Начать в» не ставить — это про это поле | Глава 5 |
| Условия | Условие питания (только от сети / останавливать при переходе на батарею), условие простоя, условие сети | Список выше |
| Параметры | «Немедленно запускать задачу, если пропущен запланированный запуск», «Правило, применяемое, если задача уже выполняется», «Время до остановки» | Список выше и глава 7 |
| Журнал | Список событий этой задачи. По умолчанию выключен и пуст, пока не включите по процедуре из 4.1 | Разделы 4.1–4.2 |
4.5 На что смотреть в самом проектировании триггеров
Бывает, что «не запускается» на самом деле — расхождение между замыслом и устройством триггера.
- «Каждое 31-е число месяца» в месяцах без 31-го дня не срабатывает. Для обработки конца месяца безопаснее проектировать под замысел «последний день месяца»: например, обрабатывать прошлый месяц в начале нового или проверять дату в самом скрипте.
- Время — это местное время машины, на которой зарегистрировали задачу. Если раздать один и тот же XML на машины в зарубежных офисах или, реже, на сервер в UTC, время выполнения разъедется по площадкам. Зафиксируйте в требованиях, что имеется в виду: «6 утра по японскому времени везде» или «6 утра по местному времени каждой площадки».
- Стоит насторожиться, если Планировщик заданий начинают использовать для коротких повторов (например, каждые 5 минут). Как инструмент «пакетной обработки раз в сутки» он хорош, но как только нужен поминутный опрос или непрерывный мониторинг — это уже область постоянно работающего процесса (глава 8 ниже).
- Триггеры по событиям сильны, но сначала убедитесь, что целевое событие действительно стабильно пишется в журнал. Конфигурация, которая стартует по конкретному ID в журнале приложений, может незаметно перестать работать, если обновление приложения изменит, как события пишутся. Триггер по времени плюс проверка условия внутри скрипта часто в итоге проще сопровождать.
5. Типичные причины завершения с 0x1 и правильный вызов PowerShell
0x1 — это только результат «скрипт завершился неудачей»; причина — в различиях среды выполнения. Между ручным запуском и запуском по расписанию отличаются в основном следующие моменты.
- Другой текущий каталог: если не указать «Начать в», задача идёт, например, из
C:\Windows\System32. Скрипты с относительными путями ломаются именно здесь. В скрипте собирайте пути от$PSScriptRoot, в задаче укажите рабочую папку в «Начать в». При этом в поле «Начать в» кавычки ставить нельзя — путь пишут без кавычек даже с пробелами (иначе задача завершится с0x8007010B). - Другие переменные окружения и профиль: считайте, что переменные, которые задают сценарии входа или пользовательский профиль, и подключённые сетевые диски (например, X:) в неинтерактивной сессии просто не существуют. Используйте UNC-пути напрямую (
\\server\share\...), а-NoProfileубирает различия, связанные с профилем. - Другая политика выполнения: даже если у пользователя стоит
RemoteSigned, у служебной учётной записи она может быть не задана. Явно указывайте-ExecutionPolicy Bypassв аргументах задачи. - У утилиты особое соглашение о кодах завершения: например,
robocopyвозвращает 1, когда «все файлы успешно скопированы». Обёртка, которая пробрасывает код как есть, может показывать0x1при нормальной работе — или наоборот. Соглашение о кодах завершения каждой внешней команды проверяйте обязательно.
Для robocopy в официальной таблице кодов завершения 0–7 — комбинации «без сбоя», а 8 и выше означают, что во время копирования был хотя бы один сбой.12 То есть пробрасывать как есть нельзя: нужен обёртка, которая нормализует в 0/1.
# Аргументы принимаем сами. Не опираться молча на переменные вызывающей стороны
param(
[Parameter(Mandatory)][string]$Source,
[Parameter(Mandatory)][string]$Destination
)
# robocopy кладёт код завершения в $LASTEXITCODE.
# Даже при $ErrorActionPreference = 'Stop' ненулевое завершение
# нативной команды исключением не становится — судить нужно самим
robocopy $Source $Destination /E /R:2 /W:5 /NP
$rc = $LASTEXITCODE
if ($rc -ge 8) {
Write-Error "robocopy завершился сбоем. Код завершения: $rc"
exit 1
}
# 0–7 — без сбоя. Что произошло, пишем в лог, Планировщику заданий сообщаем успех
Write-Host "robocopy завершился нормально. Код завершения: $rc"
exit 0
Главная строка — -ge 8. Если написать if ($rc -ne 0), успешное копирование (1) тоже станет сбоем. Это и есть природа классического обращения «каждое утро приходит уведомление, что резервное копирование не удалось, а файлы на самом деле скопированы».
Базовая форма вызова PowerShell такая.
Программа или сценарий: pwsh.exe (для Windows PowerShell — powershell.exe)
Добавить аргументы: -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Jobs\Cleanup-Logs.ps1"
Начать в: C:\Jobs (без кавычек)
-File вместо -Command используют не только потому, что экранирование аргументов становится проще, но и потому, что exit n внутри скрипта напрямую становится кодом завершения процесса, и успех или неудачу можно читать по «Результату последнего выполнения» в Планировщике заданий. И сам скрипт проектируйте так, чтобы при успехе явно возвращать 0, при неудаче — ненулевое значение.
# Ставим Stop, чтобы и «неостанавливающие ошибки» командлетов попадали в catch.
# Без этого сбой Copy-Item может пройти незамеченным и закончиться exit 0
$ErrorActionPreference = 'Stop'
try {
Main
exit 0
}
catch {
Write-Error $_
exit 1
}
Где перехватывать исключения и как их фиксировать, подробнее разобрано в статье «Где в обработке исключений ставить catch и логирование».
6. Журнал нужно вести самим
Журнал Планировщика заданий говорит только, «запустилась ли задача и какой был код завершения». Что именно и до какого момента сделал скрипт, должен записывать сам скрипт.
Как минимум, вместо перенаправления через аргументы задачи (синтаксис перенаправления в поле «Аргументы» Планировщика заданий не работает, потому что выполняется не через оболочку) проще всего вести транскрипт внутри скрипта.
$logDir = 'C:\Jobs\logs'
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
Start-Transcript -Path (Join-Path $logDir ("cleanup-{0:yyyyMMdd}.log" -f (Get-Date))) -Append
try {
# основная обработка
}
finally {
Stop-Transcript
}
Как не дать самим логам бесконечно копиться (ротация, архивирование) — ровно то, что описано в «PowerShell на практике — безопасная автоматизация анализа логов, архивирования и отчётов», и это можно взять как есть.
Если идти дальше, рассмотрите запись в журнал событий Windows. В отличие от файлового журнала, плюс в том, что успех или сбой попадает туда, куда эксплуатация и так смотрит (Просмотр событий, существующие средства мониторинга).
# --- Один раз при установке, с правами администратора (в установщике или скрипте первоначальной настройки) ---
if (-not [System.Diagnostics.EventLog]::SourceExists('KS-Jobs')) {
[System.Diagnostics.EventLog]::CreateEventSource('KS-Jobs', 'Application')
}
# --- Тело задания (под учётной записью с минимальными правами) только пишет.
# SourceExists может потребовать права администратора на обход всех журналов, поэтому во время выполнения его не вызывайте.
# Блок ниже предполагается внутри обработчика сбоя (catch) функции Main ---
catch {
$err = $_
try {
[System.Diagnostics.EventLog]::WriteEntry('KS-Jobs',
"Cleanup-Logs failed: $($err.Exception.Message)",
[System.Diagnostics.EventLogEntryType]::Error, 1001)
}
catch {
# Невозможность записи в журнал не должна скрывать исходный сбой
Write-Warning "Не удалось записать в журнал событий: $_"
}
exit 1
}
Два уточнения. Во-первых, для регистрации источника событий (CreateEventSource) нужны права администратора. Если смешать регистрацию с телом задания, при первом сбое в продакшене, где задание идёт под учётной записью с минимальными правами, получите двойной сбой: попытка регистрации бросает исключение, и нужное событие так и не пишется. Как выше, регистрацию выносите на сторону установки, во время выполнения только пишите. Во-вторых, если, как в примерах регистрации задач из этой статьи, движок — pwsh.exe, командлеты New-EventLog / Write-EventLog времён Windows PowerShell 5.1 недоступны (команда не найдена, и скрипт целиком падает). Прямой вызов классов .NET, как показано, работает и в 5.1, и в 7.
Поверх этого стоит механизм, который «доносит сбой до человека», — почта или Teams / Slack. Тогда не случится история, когда о нескольких месяцах простоя узнают только на инвентаризации. Сложная инфраструктура уведомлений не нужна: нескольких строк, которые при сбое делают POST на webhook, достаточно. Уведомления при каждом успехе, наоборот, рано или поздно перестают читать: успехи лучше сводить к еженедельной сводке, а объектами обнаружения делать сбои и «не выполнилось» (устаревшее время последнего запуска). Критерии того, что писать в журнал, тоже разобраны в «Где в обработке исключений ставить catch и логирование».
7. Контроль повторного запуска и длительного выполнения
Что будет, если предыдущий запуск ещё не кончился, а уже подошло следующее время по расписанию? Это задаёт параметр «Правило, применяемое, если задача уже выполняется» на вкладке параметров; в PowerShell ему соответствует -MultipleInstances.5
| Значение | Поведение | Куда подходит |
|---|---|---|
| IgnoreNew (по умолчанию в GUI: не запускать новый экземпляр) | Если выполнение уже идёт — пропустить новый запуск | Идемпотентные периодические пакетные задания в целом. Начинайте с этого |
| Queue | Если выполнение уже идёт — запустить по очереди после завершения | Сводки, где пропуски недопустимы |
| Parallel | Запускать параллельно | В принципе избегать. Только если параллельная безопасность гарантирована |
Заодно поставьте «Время до остановки» (-ExecutionTimeLimit, по умолчанию 3 дня) в реалистичное значение (примерно в 2–3 раза больше ожидаемого времени выполнения) — это убережёт от ситуации, когда зависший процесс тянет за собой ещё и задание следующего дня.5
Важно, что IgnoreNew и Queue защищают только внутри одного определения задачи. Они не предотвращают конфликт, если тот же скрипт вызывает другая задача или если во время разбора инцидента кто-то запускает его вручную. Если к одному ресурсу (файл, БД, внешняя система) ведёт несколько путей, взаимоисключение нужно и на стороне скрипта. Классический приём — именованный мьютекс.
$mutex = New-Object System.Threading.Mutex($false, 'Global\KS-Cleanup-Logs')
$acquired = $false
try {
try {
$acquired = $mutex.WaitOne(0)
}
catch [System.Threading.AbandonedMutexException] {
# Предыдущее задание умерло, не отпустив мьютекс (превышено
# «время до остановки» задачи, kill процесса, отключение питания и т. п.).
# В этом случае WaitOne не возвращает false, а бросает исключение,
# и владение уже перешло сюда. Если не поймать и выйти, следующие
# запуски будут падать здесь же, и задание больше никогда не поедет
$acquired = $true
Write-Warning 'Предыдущий запуск завершился аварийно. Проверьте незавершённую работу перед продолжением.'
# Здесь проверить, не остались ли недописанные файлы или
# половинчатые записи, и только потом идти в основную обработку
}
if (-not $acquired) {
Write-Warning 'Завершение, потому что уже выполняется другой экземпляр.'
exit 0 # 0, если «не выполнилось» не должно считаться сбоем; иначе ненулевое
}
# основная обработка
}
finally {
if ($acquired) { $mutex.ReleaseMutex() }
$mutex.Dispose()
}
AbandonedMutexException сообщает, что прежний владелец исчез, не освободив объект, и к моменту исключения владение уже у вас. Если здесь сделать exit, объект так и не освободится, в следующий раз снова будет то же исключение, и задание навсегда перестанет работать. Правильно: поймать, проверить состояние прерванной работы и продолжить.
Префикс Global\ в имени даёт взаимоисключение и между разными сессиями (задача другого пользователя и ручной запуск и т. п.). Ждать ли захват (передавая тайм-аут в WaitOne) или сразу отступать — решайте по характеру задания. Имейте в виду: именованный объект с Global\ виден любому на машине. На общем сервере, куда входят несколько пользователей, если по злому умыслу или случайно одноимённый мьютекс перехватят первым, задание может пропускаться вечно (причём при exit 0 это выглядит как нормальная работа). В такой среде либо настройте ACL мьютекса (MutexSecurity) и сузьте, кто может его захватывать, либо как минимум фиксируйте «не удалось захватить, пропущено» в уведомлениях и журнале событий из предыдущего раздела, чтобы мониторинг видел серию пропусков. Взаимоисключение при обмене через файлы подробно разобрано в «Интеграция через файлы: блокировки, атомарный claim и практические рекомендации».
8. Когда пора отойти от Планировщика заданий — граница со службами постоянного выполнения
Планировщик заданий не универсален. По мере роста требований наступает момент, когда лучше сменить механизм, чем продолжать насильно использовать текущий.
| Требование | Подходящий механизм |
|---|---|
| Плановая пакетная обработка до нескольких раз в день | Планировщик заданий |
| Триггеры — смесь людей, событий и времени, нужно видеть весь поток целиком | Power Automate (отдельная статья) |
| Поминутный опрос, непрерывный мониторинг, обработка очередей | Служба Windows / постоянно работающий процесс |
| Нужно хранить состояние между обработками, тонко управлять повторами и backoff | Служба Windows / постоянно работающий процесс |
Как только начинается опрос через «задачу каждые 5 минут», каждый запуск платит за создание процесса и загрузку модулей, плюс нужен механизм сохранить предыдущее состояние, например в файл, — по сути вы заново по кускам реализуете постоянно работающий процесс. На этом этапе логичнее сделать процесс постоянным через Generic Host и BackgroundService в .NET. Паттерн реализации — в статье «Зачем использовать .NET Generic Host и BackgroundService в десктопных приложениях».
И наоборот, специально превращать ежемесячную или ежедневную пакетную обработку в службу с собственным таймером — тоже избыточно. Граница, которая почти всегда верна: если «интервал от часа и больше, обработка независима и не хранит состояние» — Планировщик заданий; как только начинаете выходить за эти рамки — рассматривайте переход на постоянно работающий процесс.
9. Чек-лист перед вводом в эксплуатацию
Перед регистрацией задачи имеет смысл один раз пройти по этим пунктам.
- Определена ли учётная запись выполнения (не выбран ли SYSTEM по инерции? В домене рассмотрен ли gMSA?)
- Понятны ли ограничения типа входа (при S4U нет сетевого доступа; при Password — определён ли порядок при смене пароля?)
- Протестирован ли скрипт отдельно в условиях, близких к учётной записи выполнения?
- Вызывается ли скрипт в форме
-NoProfile -NonInteractive -ExecutionPolicy Bypass -File? - Построены ли пути внутри скрипта от
$PSScriptRoot/ UNC (нет ли зависимости от подключённых дисков или относительных путей)? - Не стоят ли кавычки в поле «Начать в»?
- Продуманы ли коды завершения (0 — успех / ненулевое — неудача; проверены ли соглашения о кодах завершения внешних команд)?
- Включён ли журнал задач? Есть ли собственный журнал скрипта и уведомления о сбоях?
- Явно ли заданы условие питания, StartWhenAvailable, повторный запуск и ограничение времени выполнения?
- Сохранено ли определение задачи в репозитории как XML или PowerShell-скрипт?
10. Итог
Работа с Планировщиком заданий не заканчивается написанием скрипта: стабильная эксплуатация начинается только после того, как продуманы три предпосылки — учётная запись выполнения, сессия и значения по умолчанию. Иными словами, если один раз при регистрации учесть моменты из этой статьи — выбор типа входа, включение журнала, проектирование кодов завершения и журналирования, явное указание повторного запуска и ограничения времени, — дальше задача будет удивительно необременительной в сопровождении.
«Вручную работает, по расписанию — нет» — почти всегда следствие различий в сессии и окружении. Прежде чем наугад крутить настройки, посмотрите на вкладке «Журнал», докуда дошло выполнение, и сверху вниз пройдите шаги диагностики из этой статьи.
Похожие статьи
- PowerShell на практике — безопасная автоматизация анализа логов, архивирования и отчётов
- Тесты PowerShell на Pester — практическая схема, которая делает эксплуатационные скрипты устойчивее к поломкам
- Автоматизация бизнес-процессов в Power Automate — облачные потоки, потоки на компьютере и обработка ошибок
- Когда в Windows нужны права администратора — UAC, защищённые области и как это отличить на этапе проектирования
Смежные области консультирования
KomuraSoft LLC (合同会社小村ソフト) проводит ревью проектирования автоматизации бизнес-процессов на базе PowerShell и Планировщика заданий, а также консультирует по восстановлению периодических заданий, которые «вроде работают, но никто не может это починить».
- Техническая консультация и ревью проектирования
- Разработка приложений для Windows
- Расследование причин сбоев
- Связаться с нами
Справочные материалы
-
Microsoft Learn, logonType Simple Type (Task Scheduler). Определения типов входа. О том, что при S4U пароль не сохраняется, но взамен нет доступа к сети и к зашифрованным файлам. ↩ ↩2 ↩3
-
Microsoft Learn, Troubleshoot issues with scheduled tasks not running. Порядок диагностики — автономная проверка скрипта → состояние и журнал → смена параметров безопасности, а также расположение журнала событий TaskScheduler Operational. ↩ ↩2 ↩3
-
Microsoft Learn, Task Scheduler error and success constants. Определения кодов состояния и ошибок, включая SCHED_S_TASK_READY (0x41300), SCHED_S_TASK_RUNNING (0x41301), SCHED_S_TASK_HAS_NOT_RUN (0x41303). ↩ ↩2
-
Microsoft Learn, Manage group Managed Service Accounts. О том, что пароль gMSA ведёт контроллер домена, а хост его получает; что задачи Планировщика заданий поддерживают gMSA; что уровень функциональности домена и леса должен быть Windows Server 2012 или новее; что нужен корневой ключ KDS (подтверждается событием ID 4004 в журнале Operational службы KdsSvc); что имя gMSA должно быть уникально в лесу; о
-PrincipalsAllowedToRetrieveManagedPasswordуNew-ADServiceAccountи оInstall-ADServiceAccount/Test-ADServiceAccountна каждом хосте. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, New-ScheduledTaskSettingsSet. Параметры объекта настроек задачи, включая MultipleInstances (Parallel / Queue / IgnoreNew), StartWhenAvailable и ExecutionTimeLimit (по умолчанию 3 дня). ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Security Contexts for Tasks. О контексте безопасности задачи и о том, что для выполнения задачи, зарегистрированной с Password / S4U, нужно право «Вход в качестве пакетного задания». ↩ ↩2
-
Microsoft Learn, New-ScheduledTaskPrincipal. О том, что
-UserIdзадаёт учётную запись выполнения,-LogonType— способ входа (None/Password/S4U/Interactive/Group/ServiceAccount/InteractiveOrPassword), а-RunLevelпринимаетLimitedиHighest. ↩ -
Microsoft Learn (архив), General Task Registration. Определения сообщений событий Microsoft-Windows-TaskScheduler 106 (регистрация задачи), 113 (зарегистрировали, но часть триггеров не запускает), 116 (конфигурацию сохранили, учётные данные — нет), 140 (обновление), 141 (удаление). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn (архив), Task Monitoring and Control. События Microsoft-Windows-TaskScheduler 100 (начало задачи), 102 (штатное завершение), 111 (остановка по превышению времени), 129 (запуск с PID), 200/201 (начало и завершение действия), 202/203 (не удалось завершить / запустить действие), 323 (остановка ради нового экземпляра). В частности, у 201 символьное имя
ACTION_SUCCESSи текст «Task Scheduler successfully completed task … and action …», у 202 — «Task Scheduler failed to complete the … instance of the … task with action … The error value is: …»; 202 означает, что Планировщик заданий сам не смог завершить действие. Текущие Windows выдают 201 версии 2, в тексте есть «… with return code N», в данных события —ResultCode(этот код может не совпадать с «Результатом последнего выполнения» задачи). Также определения сообщений 327 (остановка из-за перехода на батарею), 328 (остановка из-за выхода из простоя), 329 (тайм-аут), 330 (остановка по запросу пользователя). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 -
Microsoft Learn (архив), Event ID 322 — Task Properties. О том, что событие 322 (символьное имя
NEW_INSTANCE_IGNORED) означает «не запустили, потому что уже выполняется другой экземпляр той же задачи», и о порядке пересмотра условий и параметров. ↩ -
Microsoft Learn, What’s New in Task Scheduler. О том, что начиная с Windows 10, пока включена экономия заряда, срабатывание триггеров неинтерактивных задач откладывается. ↩
-
Microsoft Learn, robocopy. Таблица кодов завершения (0 — нечего копировать, 1 — все файлы успешно скопированы, 2 и далее — комбинации дополнительных и несовпадающих файлов) и то, что 8 и выше означают хотя бы один сбой во время копирования. ↩
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Окончание драйверов принтера Windows ── как готовить печать форм и этикеток в бизнес-приложениях
Microsoft поэтапно прекращает сопровождение драйверов принтера v3/v4; с июля 2026 IPP class driver предпочтут. Что исчезает в Windows pro...
Обработка ошибок и retry в PowerShell — от ловушки нерабочего try/catch до exit code и типовых схем повтора
На практике разбираем различие завершающих и незавершающих ошибок PowerShell, ловушку, из-за которой не срабатывает try/catch, стандартны...
Политика выполнения PowerShell и подпись скриптов — как отказаться от Bypass в роли постоянной заглушки
Политика выполнения PowerShell — не граница безопасности, а защитный механизм. В статье разобраны различия RemoteSigned и других политик,...
Практическое руководство по Process Monitor (ProcMon) — за 10 минут находим «настройки не читаются» и ACCESS DENIED
Неисправности вроде «настройки поменял, а эффекта нет» разбирают в Process Monitor (ProcMon) по реальным обращениям к файлам и реестру. В...
Дата, время и часовые пояса в бизнес-приложениях — ловушки DateTime, хранение в UTC и тесты
После переноса сервера время сдвигается на 9 часов. Разбираем, откуда берутся такие сбои: Kind у DateTime и неявные преобразования. Дальш...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что означает 0x1 в поле «Результат последнего выполнения» Планировщика заданий?
- 0x1 — это не ошибка самого Планировщика заданий, а значит, что запущенная программа вернула код завершения 1. Причина на стороне скрипта. Типичные отличия ручного запуска от запуска по расписанию — текущий каталог, переменные окружения и профиль, политика выполнения. Также стоит учитывать соглашение о кодах завершения внешних команд вроде robocopy, которая может возвращать 1 даже при успехе. Полезно различать семейства: 0x413xx — коды состояния Планировщика заданий, 0x8007xxxx — коды ошибок Windows; тогда не ищете причину не там.
- Почему скрипт работает вручную, но не работает через Планировщик заданий?
- Почти наверняка дело в различиях учётной записи выполнения, сессии и окружения. Если выбрать «Выполнять независимо от того, выполнен ли вход пользователя в систему», задача идёт в неинтерактивной сессии: подключённых сетевых дисков и переменных окружения пользовательского профиля там нет. Кроме того, при варианте «Не хранить пароль» (S4U) нет доступа к сетевым ресурсам. Для диагностики полезны: вкладка «Журнал», автономная проверка скрипта, временное переключение на «Выполнять только для вошедших в систему пользователей». Журнал задач по умолчанию выключен — включите его до ввода в эксплуатацию.
- Как правильно вызывать PowerShell-скрипт из Планировщика заданий?
- В поле программы указывают pwsh.exe (или powershell.exe), аргументы — в форме -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "полный путь". Если использовать -File, а не -Command, exit n внутри скрипта становится кодом завершения процесса, и по нему можно судить об успехе. Пути внутри скрипта стройте от $PSScriptRoot, а в поле «Начать в» кавычки ставить нельзя (иначе задача завершится с 0x8007010B). Сам скрипт проектируйте так, чтобы при успехе явно возвращать 0, при неудаче — ненулевое значение.
- Как не допустить остановки задачи из-за смены пароля?
- Если зарегистрировать задачу как «Выполнять независимо от того, выполнен ли вход пользователя в систему», пароль сохраняется, и после его смены задача продолжает падать с ошибкой входа (0x8007052E). В доменной среде первый кандидат — gMSA (групповая управляемая учётная запись службы): пароль автоматически ведёт контроллер домена, и сама эта проблема исчезает. Если используете выделенную служебную учётную запись, ведите срок действия пароля и инвентаризацию задач как один процесс. Дополнительно стоит слать уведомление по почте или в Teams при сбое, чтобы не пропустить месяцы простоя.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.