Перейти к основному содержимому

Триггеры — расписание и наблюдатели

Разработчику Архитектору Инженеру
Загрузка PowerShell…

Маршрут
124 — модель125 — блоки и модулиэта статья → практика в 2.05/112.


Два типа фоновых скриптов

ТипКак работаетПримеры задач
Запланированный (scheduled)Стартует по календарю, завершаетсяБэкап ночью, отчёт раз в неделю, очистка temp
Наблюдатель (watcher)Работает постоянно или опрашивает частоНовый файл в папке, порог диска, очередь сообщений

Оба типа — триггеры из модели автоматизации. Код действий один и тот же; меняется только способ запуска.

Сначала скрипт без планировщика
Любой .ps1 должен отрабатывать вручную с -NoProfile -File и возвращать exit 0 / exit 1. Планировщик лишь повторяет то, что уже проверено человеком.


Три вопроса перед настройкой триггера

1. Зависимости

  • Какие модули нужны (Import-Module в начале скрипта)?
  • Установлены ли они для той УЗ, от которой пойдёт задача (системная vs пользовательская)?
  • Нужны ли разные версии одного модуля для двух скриптов на одном хосте?

Install-Module внутри рабочего скрипта не используют — установка при подготовке сервера или образа.

2. Место выполнения

ОшибкаПоследствие
Скрипт Azure AD на машине без сетиТаймаут, ложный алерт
Создание пользователя AD на одном DC, почта Exchange на другом — без репликации«Пользователь не найден»
Задача, требующая локального WMI/CIM, запущена удалённоПустой или неверный результат

Скрипт запускают там, где есть данные и права, а не «где удобнее».

3. Контекст (учётная запись)

КонтекстКогда
Пользователь (доменная УЗ в планировщике)AD, SQL с SQL-auth, сетевые шары под пользователем
SYSTEM / LocalServiceЛокальные службы, реестр машины, до входа пользователя
УЗ службы (gMSA / dedicated)Продакшен-автоматизация с минимальными правами

Модули, установленные только в профиль пользователя, недоступны задаче, стартующей от SYSTEM — ставьте -Scope AllUsers или AllUsers для Windows.


Запланированный скрипт в Windows

Полный пример Register-ScheduledTask — в 2.05/112. Краткая схема:

$action = New-ScheduledTaskAction `
-Execute 'pwsh.exe' `
-Argument '-NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Archive-Logs.ps1"' `
-WorkingDirectory 'C:\Scripts'

$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At '02:00'

Register-ScheduledTask -TaskName 'Archive-Logs' -Action $action -Trigger $trigger
Параметр запускаЗачем
-NoProfileБез личных алиасов и медленного профиля
-ExecutionPolicy BypassТолько для этой сессии; не меняет политику машины
-WorkingDirectoryОтносительные пути и логи рядом со скриптом
-StartWhenAvailableДогнать пропуск, если сервер был выключен

Linux / macOScron или systemd timer с pwsh:

0 2 * * 0 /usr/bin/pwsh -NoProfile -File /opt/scripts/archive-logs.ps1 >> /var/log/archive-logs.log 2>&1

Подробнее про cron и systemd — скрипты Unix.

CI (Jenkins, GitHub Actions) — ещё один вид расписания: pipeline по cron или webhook; см. DevOps.


Скрипт-наблюдатель и исполнитель

Надёжный паттерн из практики автоматизации — разделение ролей:

Наблюдатель (лёгкий, долгоживущий)
→ обнаружил событие
→ запускает Исполнитель (тяжёлый .ps1 с param)
→ ждёт exit code / таймаут

Исполнитель (Process-Inbound.ps1) — обычный скрипт с param, try/catch, exit.

Наблюдатель — минимальная логика ожидания:

# Watch-Inbound.ps1 — упрощённый опрос (без FileSystemWatcher)
$folder = 'C:\Inbound'
$stateFile = "$PSScriptRoot\.last-scan"
$executor = Join-Path $PSScriptRoot 'Process-Inbound.ps1'

while ($true) {
$known = if (Test-Path $stateFile) { Get-Content $stateFile } else { @() }
$current = Get-ChildItem -LiteralPath $folder -Filter '*.csv' | Select-Object -ExpandProperty Name
$new = Compare-Object -ReferenceObject $known -DifferenceObject $current |
Where-Object SideIndicator -eq '=>' |
Select-Object -ExpandProperty InputObject

foreach ($name in $new) {
$full = Join-Path $folder $name
& pwsh -NoProfile -File $executor -Path $full
if ($LASTEXITCODE -ne 0) {
Write-Warning "Исполнитель завершился с кодом $LASTEXITCODE для $name"
}
}

$current | Set-Content $stateFile
Start-Sleep -Seconds 60
}

FileSystemWatcher (реакция без минутного опроса) — пример в 2.05/112.

Корректное завершение наблюдателя

  • Обрабатывайте Ctrl+C и остановку службы: trap или Register-EngineEvent -SourceIdentifier PowerShell.Exiting.
  • Не запускайте второй исполнитель, пока первый не завершился (флаг $busy или очередь).
  • Логируйте каждый запуск исполнителя с меткой времени.

Сравнение способов запуска

СпособПлатформаПлюсыМинусы
Планировщик WindowsWindowsВстроен, GUI, Register-ScheduledTaskКонтекст УЗ, нет sub-minute cron
cron / systemdLinuxПросто, привычно админамНужен установленный pwsh
FileSystemWatcherWindows (.NET)Быстрая реакция на файлДолгоживущий процесс, отладка сложнее
Опрос в циклеЛюбаяПростой кодЗадержка, лишний CPU
Jenkins / GHAСервер CIИстория, артефакты, секреты CIИнфраструктура

Чек-лист триггера

#Вопрос
1Скрипт проходит ручной запуск с теми же аргументами, что у планировщика?
2Модули установлены для УЗ задачи?
3Пути абсолютные или $PSScriptRoot?
4Есть журнал и exit 1 при ошибке?
5Для watcher — что при двойном срабатывании и при падении исполнителя?
6Секреты берутся из хранилища, а не из .ps1? — 111

Дальше

ТемаМатериал
Remoting, массовый запуск2.05/112
Секреты, SecretManagement127
Конфигурация JSON128
Тесты перед продакшеном111 — Pester, 112 — Pester

См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").