В Windows есть механизм под названием 'Планировщик заданий' (Task Scheduler). Основная задача планировщика - выполнение задач в определенный момент времени или при определенном действии. В этой статье будет рассмотрены варианты работы с ним через Powershell. На примере мы создадим задание в виде скрипта с Powershell, изменим и удалим его используя только команды.
Работа планировщика в Powershell
Отличительной чертой работы Powershell является то, что мы должны создать каждый из объектов (результат команд) планировщика отдельно, а затем объединить их с помощью 1 команды. Сами объекты делятся на следующие:
- Action (Действие) - определяет что мы должны запустить. Действия проявляются как программа (например браузер) с аргументами (открыть определенный сайт). В одной задаче может быть до 32 действий;
- Trigger (Триггер) - это событие при котором должно запуститься действие. Событие может быть привязано к времени или каким-то процессом в системе (включение компьютера, вход пользователя и т.д.). Время можно устанавливать как определенное, например в 14:00, так и интервальное - каждые 2 часа. Так же как и действий триггеров может быть несколько.
- Settings (Настройки) - дополнительные условия обработки задач. Это может быть перезапуск в случае сбоя задачи или самоудаление если задача не используется.
- Security Options (Параметры безопасности) - определяют привилегии и пользователя от имени которого будут запущены задания.
3 и 4 пункт не является обязательными при создании задачи т.к. уже имеет настройки по умолчанию.
За создание задач в планировщике отвечает модуль ScheduledTasks, который имеет следующие команды:
Get-Command -Module ScheduledTasks
Создание скрипта для тестирования
Что бы продемонстрировать работу планировщика я создам скрипт, который будет сохранять логи с журнала Windows в папку 'logs'. Ниже находится сам скрипт:
# Папка с логами
$log_dir = 'C:\logs\'
# Полный путь до файла с генерацией имени файла в виде даты
$log_file = $log_dir + "AppLog_$(Get-Date -format 'dd-mm-yyyy').xml"
# Получаем 10 последних логов приложений
$logs = Get-WinEvent -MaxEvents 10 -LogName application
# Сохраняем результат предыдущей команды в файл в формате XML
Export-CliXml -InputObject $logs -Path $log_file -Force
Команды выше не должны выдавать каких либо ошибок. Ошибка может быть, например, с несуществующей папкой 'logs'. Сам скрипт я сохранил по пути 'C:\scheduler_task.ps1'. Его запуск не выдает каких-то ошибок:
.\scheduler_task.ps1
У вас так же будет создан файл в папке 'logs'.
Создание выполняемого действия для планировщика
Для создания 'action' мы должны указать программу и аргументы. В качестве программы будет сам интерпретатор 'powershell.exe', а аргументом будет путь до скрипта. Как говорилось раньше у нас будет несколько объектов (результат команд) и все их нужно будет объединить. Что бы это было возможным мы должны поместить результаты работы в переменные:
$task_action = New-ScheduledTaskAction `
-Execute 'powershell.exe' `
-Argument '-File C:\scheduler_task.ps1'
Мы так же можем указать параметр 'WorkingDirectory', который определяет откуда и будет запущена программа. Учитывая, что 'powershell.exe' виден через системные переменные использовать параметр 'WorkingDirectory' нам не требуется.
Действия выше аналогичны следующим настройкам в интерфейсе (т.е. так мы обычно добавляем скрипт Powershell в планировщик):
Создание временного события - триггера
Следующим мы определим, когда мы будем запускать задачу. Это делается с помощью команды 'New-ScheduledTaskTrigger'. Эта команда имеет множество параметров, которые делятся на системные события и временные.
Системные события, которые мы можем использовать, следующие:
- AtLogOn - во время входа пользователя в систему;
- AtStartup - во время запуска системы.
Аргументы связанные со временем:
- At - точное время выполнение скрипта;
- Daily - ежедневно;
- DaysInterval - интервал в днях. Если указать цифру 1, то подразумевается, что задача будет запускаться ежедневно. Если указать 2 - то задача будет запускаться через день;
- DaysOfWeek - день недели, когда будет выполнен запуск. Возможны варианты: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday;
- Once - запуск будет выполнен единожды;
- Weekly - запуск по неделям;
- WeeksInterval - интервал между неделями;
- RandomDelay - указывает задержку между запусками. Задержка определяется случайно от указанного значения. Принимает не число, а объект TimeSpan;
- RepetitionDuration - Срок действия задачи. Принимает не число, а объект TimeSpan.
- RepetitionInterval - время через которое задача будет повторяться. Принимает не число, а объект TimeSpan.
Большую часть команд, связанных со временем, мы можем сочетать вместе. Так, например, мы создадим триггер выполнится однажды в 13:00:
$task_trigger = New-ScheduledTaskTrigger -Once -At 13:00
Еще несколько примеров:
# Ежедневно в 14:00
New-ScheduledTaskTrigger -Daily -At 14:00
# Запуск каждые 2 дня в 14:00
New-ScheduledTaskTrigger -Daily -DaysInterval 2 -At 14:00
# Каждый 2-ой понедельник в 14:00
New-ScheduledTaskTrigger -Weekly -WeeksInterval 2 -DaysOfWeek Monday -At 14:00
Меня интересует ежедневный запуск задачи с периодичностью в 5 минут. Для 'RepetitionInterval', который устанавливает такие интервалы, нужно использовать дополнительную команду 'New-TimeSpan'. В этой команде определим сам интервал:
$time = New-TimeSpan -Minutes 5
$task_trigger = New-ScheduledTaskTrigger -Once -At 0:00 -RepetitionInterval $time
Регистрация задачи в планировщике
Теперь, после выполнения минимальных требований в виде 'action' и 'trigger', мы должны объединить эти объекты. Это делается с помощью команды 'Register-ScheduledTask'. Дополнительно мы можем определить название и описание создаваемого объекта:
$name = 'Сбор логов'
$description = 'Сбор логов приложений каждые 5 минут в папку c:\logs'
Register-ScheduledTask -TaskName $name `
-Description $description `
-Action $task_action `
-Trigger $task_trigger
Учитывайте, что вы можете использовать несколько триггеров и действий. Для этого вы должны перечислить их через запятую.
TaskPath указывает где была создана задача. В примере выше это произошло в корне планировщика:
Вы можете использовать аналогичный параметр для регистрации задачи в другой директории:
Register-ScheduledTask -TaskName 'SomeTask' `
-TaskPath '\Microsoft' `
-Action $action1,$action2 `
-Trigger $trigger1,$trigger2
Способа узнать путь два:
- Через графический интерфейс;
- Через команду 'Get-ScheduledTask' (будет рассмотрена ниже).
В powershell есть еще одна команда, которая может участвовать в создании задач "New-ScheduledTask". Основное отличие такой команды в том, она не регистрирует (добавляет) задачу в сервис планировщика. Если бы мы использовали обе команды это бы выглядело так:
# Объединение объектов
$task = New-ScheduledTask -Action $action1,$action2 `
-Trigger $trigger1,$trigger2
# Их активация
Register-ScheduledTask -TaskName 'SomeTask' `
-TaskPath '\Microsoft' `
-InputObject $task
Запуск и получение дополнительной информации
Для запуска задачи, не зависимо от триггеров, используется команда 'Start-ScheduledTask':
Start-ScheduledTask -TaskName 'Сбор логов'
У нас так же есть 2 команды, которые возвращают информацию о задачах.
Первая команда просто возвращает список задач, которые есть в каталогах планировщика их статус и путь:
Get-ScheduledTask
У команды запуска и получения задач из планировщика можно использовать параметры:
- TaskName - имя объекта;
- TaskPath - путь до объекта.
Get-ScheduledTask -TaskPath '\'
Более полезная команда следующая, так как вернет немного больше информации:
Get-ScheduledTaskInfo -TaskName 'Сбор логов'
Само собой все эти команды можно использовать в конвейере:
Get-ScheduledTask -TaskName 'Сбор логов' | Start-ScheduledTask
Если задача занимает много времени и ее нужно остановить - можно выполнить следующие действия:
Stop-ScheduledTask 'Сбор логов'
Фильтрация через Where-Object в Powershell с примерами
Изменение пользователя, параметров безопасности и уровня запуска
Создавая задачи в планировщике мы имеем блок 'Security Options', который чаще используется с настройками по умолчанию. В этом блоке определяются следующие параметры:
- Пользователь, от имени которого будет выполнен запуск;
- Выполняется ли задача для вошедших в систему пользователей;
- Привилегии запуска (от пользователя/администратора);
- Совместимость.
В графическом интерфейсе эти параметры настраиваются в следующем блоке:
Так же как и в случае с созданием задачи, в случае ее изменения мы создаем разные объекты (результаты команд), которые затем привязываем через команду 'Set-SchedukedTask'.
С помощью следующих команд мы изменим пользователя, повысим права и изменим совместимость:
# Изменяем пользователя и повышаем права запуска
$task_user = New-ScheduledTaskPrincipal -UserId '.\admin' -RunLevel Highest
# Изменяем совместимость
$task_settings = New-ScheduledTaskSettingsSet -Compatibility 'Win8'
# Добавляем объекты созданные выше к существующей задаче
Set-ScheduledTask -TaskName 'Сбор логов' -Principal $task_user -Settings $task_settings
Если у вас будет следующая ошибка, то скорее всего она связана с неверным пользователем:
- Set-ScheduledTask : No mapping between account names and security IDs was done.
Само собой эти же объекты можно использовать при создании (регистрации) задачи в планировщике:
$name = 'Task 3'
Register-ScheduledTask -TaskName $name `
-Action $task_action `
-Trigger $task_trigger `
-Principal $task_user `
-Settings $task_settings
Учитывайте, что у New-ScheduledTaskSettingsSet около 30 возможных параметров (работа при отключенной батареи, максимальное количество перезапусков и т.д.). В примере выше рассмотрено лишь несколько.
Изменение триггеров и действий
С помощью Set-ScheduledTask так же добавляются и изменяются триггеры и действия. Изменение будут заключаться в полной замене существующих триггеров и действий у задач. Вам просто нужно создать объект по аналогии с тем, как это делалось в предыдущих разделах:
$trigger = New-ScheduledTaskTrigger -Daily -At 14:00
$action = New-ScheduledTaskAction -Execute Calc.exe
Set-ScheduledTask -TaskName 'Сбор логов' -Action $action -trigger $trigger
Бэкап, удаление и восстановление задач планировщика с Powershell
Удобной возможностью использования Powershell является импорт и экспорт задач планировщика. По сути вы можете создать шаблон, который можно использовать на разных компьютерах без особой работы с кодом. По умолчанию, сам Powershell, не предоставляет прямой возможности бэкапа и восстановления, но это можно сделать через другие команды.
Резервное копирование
Для бэкапа мы можем использовать команду Export-CliXml. Для этого нам нужно будет получить задачу и использовать конвейер:
Get-ScheduledTask -TaskName 'Сбор логов' | Export-Clixml 'C:\LogTaskBackup.xml'
Для экспорта в Powershell так же есть 'Export-ScheduledTask', но нет аналогичной команды импорта. Что бы импортировать такие файлы-задачи мы можем использовать только GUI. Из-за этого она не приведена в примере выше.
Удаление, отключение и включение
При удалении задачи, по умолчанию, запрашивается подтверждение. Что бы этого не было добавляется ключ 'Confirm':
Unregister-ScheduledTask -TaskName 'Сбор логов' -Confirm:$False
Если требуется только отключить задачу используйте Disable:
Disable-ScheduledTask -TaskName 'Сбор логов'
Включение:
Enable-ScheduledTask -TaskName 'Сбор логов'
Восстановление
Восстановление выполняется в несколько шагов. Первое - мы должны выполнить импорт XML документа в Powershell:
$task = Import-Clixml -Path 'C:\LogTaskBackup.xml'
На одном из сайтов я прочитал, что следующий параметр нужно изменять на "Interactive", так как без него не будет работать задача, но у меня этот параметр не изменялся при экспорте:
$task.Principal
Этот параметр связан со входом пользователя через GUI. Аналогичное название в интерфейсе планировщика задач "Выполнять только для пользователей, вошедших в систему". Если этот параметр у вас отличается - вы сможете изменить его так:
$task.Principal.LogonType = 'Interactive'
Далее нам нужно зарегистрировать эту задачу определяя каждый из параметров отдельно:
Register-ScheduledTask `
-TaskName $task.TaskName `
-Action $task.Actions `
-Trigger $task.Triggers `
-Settings $task.Settings `
-Principal $task.Principal `
-User 'administrator' `
-Password '123'
Как создавать команды и функции в Powershell вызывать их и передавать параметры
Анализ выполнения и завершения, а так же сравнение
Используя предыдущие команды мы можем выполнить некоторую автоматизацию. Например так мы можем увидеть результат выполненных задач:
Get-ScheduledTask | Get-ScheduledTaskInfo | select *
Часть этих кодов имеет следующую расшифровку (был использован автоматический переводчик):
- 0 - операция успешно завершена.
- 1 - Вызывается неправильная функция или неизвестная функция. 2 Файл не найден.
- 10 - Неправильная среда.
- 267008 - Задача готова к запуску в следующее запланированное время.
- 267009 - В данный момент идет выполнение.
- 267010 - Задача не будет запущена в запланированное время, потому что она отключена.
- 267011 - Запуск еще не был выполнен.
- 267012 - Для этой задачи больше нет запланированных запусков.
- 267013 - Одно или несколько свойств, необходимых для запуска этой задачи по расписанию, не были установлены.
- 267014 - Последний запуск задачи был прерван пользователем.
- 267015 - Либо у задачи нет триггеров, либо существующие триггеры отключены или не установлены.
- 2147750671 - Учетные данные повреждены.
- 2147750687 - Экземпляр этой задачи уже запущен.
- 2147943645 - Служба недоступна (установлен ли флажок «Запускать только при входе пользователя в систему»?).
- 3221225786 - Приложение было закрыто в результате нажатия CTRL + C.
- 3228369022 - Неизвестное программное исключение.
Альтернативное - вы можете открыть интерфейс планировщика и посмотреть какая ошибка отображается там (она будет текстом).
Далее вы можете создать скрипт для поиска сбойных задач. Например меня интересует только задачи со сбоями, а не успешно работающие. Так как я не знаю всех кодов, мне нужно будет исключить только коды успешного выполнения:
# Коды, которые меня не интересуют (успешное выполнение)
$success_sc_codes = @(0, 267008, 267009, 267010, 267011, 267012)
# Получает объект, который содержит детальную информацию по всем процессам
$all_sc_tasks = Get-ScheduledTask | Get-ScheduledTaskInfo
foreach ($task in $all_sc_tasks){
# Если код не соответствует тому что есть в массиве - выводим на экран
if (!($task.LastTaskResult -in $success_sc_codes)){
$task
}
}
Такой же подход можно использовать при поиске странных сервисов-программ. Например у нас есть эталонный компьютер/сервер и на нем созданы все нужные задачи. Всех их мы помещаем в один объект, а затем сравниваем с другими экземплярами:
# Создаем пустой массив
$good_list = [System.Collections.ArrayList]@()
# Получаем существующие задачи
$all_sc_tasks = Get-ScheduledTask
foreach ($task in $all_sc_tasks){
# Помещаем в созданный массив только имена
$good_list.Add($task.TaskName) > Out-Null
}
Далее, через разные средства, мы можем сравнить этот список на других компьютерах:
# Получаем список на другом компьютере
$all_sc_tasks = Get-ScheduledTask
foreach ($task in $all_sc_tasks){
# Ищем имена, которые не соответствуют тому что есть в массиве
if (!($task.TaskName -in $good_list)){
$task
}
}
...
Подписывайтесь на наш Telegram канал
Теги: #powershell