Планировщик заданий в Windows Vista, Windows Server 2008 и в более новых версиях оснащен дополнительными функциями. Однако возможности команды Schtasks, предназначенной для управления запланированными к исполнению заданиями из командной строки, не отвечают новому уровню функциональности. Schtasks больше не позволяет изменять сохраненные учетные данные назначенных заданий, как в прежних версиях операционной системы. Это становится проблемой, если изменен пароль учетной записи, используемой для запуска нескольких заданий.

Изменение учетных данных назначенного задания

Планировщик заданий в текущих версиях Windows предоставляет несколько полезных новых функций, которые отсутствуют в версиях, предшествующих Vista и Server 2008. Например, помимо запуска программы, можно отправлять сообщения по электронной почте или выводить на экран окно сообщений. Введены также дополнительные параметры настройки времени запуска задания. В частности, можно привязать запуск задания к некому событию, регистрируемому в журнале событий. На консоли планировщика заданий каждое задание имеет отдельную вкладку «Журнал». Все это — заметные усовершенствования центральной системной службы Windows.

Однако с обновлением планировщика заданий не все так просто. Прежде всего, я заметил, что консоль планировщика заданий не позволяет переименовывать задания. Этот недостаток я исправил с помощью сценария PowerShell, позволяющего изменять имена назначенных заданий в более новых версиях (см. статью «Переименование запланированных задач в системах Windows 7, Server 2008 и Vista», опубликованную в Windows IT Pro/RE № 12 за 2011 год). Еще одна отмеченная мной проблема – отсутствие удобного способа вывода списка назначенных заданий на одном и более компьютерах. Как уже говорилось в статье «Готовим отчеты о запланированных задачах с помощью PowerShell», опубликованной в Windows IT Pro/RE № 3 за 2012 год, команда Schtasks с параметром /query с этой задачей не справляется.

В ходе дальнейшего взаимодействия с новым планировщиком заданий я выявил еще одну проблему Schtasks, а именно, невозможность использования этой команды с параметром /change для изменения сохраненных учетных данных (то есть имени пользователя и пароля) назначенного задания из командной строки. При попытке это сделать выдается сообщение об ошибке: «Неверный параметр». Экран 1 иллюстрирует попытку изменения сохраненных учетных данных задания на компьютере с Windows 7. В более ранних версиях синтаксис этой команды работает прекрасно (на экране 2 показано успешное выполнение данной команды в Windows XP).

 

Применение Schtasks /Change в Windows 7
Экран 1. Применение Schtasks /Change в Windows 7

 

 

Применение Schtasks /Change в Windows XP
Экран 2. Применение Schtasks /Change в Windows XP

Выясняется, что если назначенное задание создавалось с помощью Schtasks с параметром /create, то для изменения его сохраненных учетных данных можно использовать Schtasks с параметром /change. Однако для изменения учетных данных заданий, создаваемых из консоли планировщика заданий (как в подавляющем большинстве случаев), команда Schtasks /change не работает. Это ограничение потенциально способно создать проблемы. Изменение учетных данных заданий по одному за раз из консоли планировщика заданий становится тем менее эффективным, чем больше число назначенных заданий.

Сценарий Set-ScheduledTaskCredential.ps1 

· Для преодоления этого ограничения я составил сценарий PowerShell Set-ScheduledTaskCredential.ps1 (см. листинг ). Синтаксис сценария следующий:

Set-ScheduledTaskCredential -TaskName  
[-TaskCredential ] [-ComputerName ]
[-ConnectionCredential ]

· Параметр -TaskName определяет задание, имеющее сохраненные учетные данные. Можно задать несколько имен заданий как массив, но использование групповых символов не допускается. Можно опустить имя параметра –TaskName, если его аргумент (имя задания или список имен заданий) стоит первым в строке команды. В Vista и более новых версиях планировщик заданий поддерживает папки заданий, поэтому необходимо указывать полный путь к заданию (например, Папка заданияИмя задания). Если имя папки опущено, то сценарий предполагает, что задание находится в корневой папке (). Параметр также поддерживает конвейерную передачу.

Параметр -TaskCredential – это объект PSCredential, определяющий сохраняемые учетные данные (имя пользователя и пароль), используемые для запуска задания или заданий. Параметр -TaskCredential – необязательный; если его опустить, то сценарий запросит ввод учетных данных. Параметр TaskCredential аналогичен параметрам Schtasks /change /RU и /RP.

Чтобы изменить учетные данные назначенного задания на удаленном компьютере, укажите имя компьютера с помощью параметра -ComputerName.

Если учетная запись текущего пользователя (служащая для запуска сценария) не обладает полномочиями изменения назначенных заданий, можно с помощью параметра –ConnectionCredential задать объект PSCredential с учетными данными, используемыми для подключения к планировщику заданий. Команду

-ConnectionCredential (Get-Credential) 

· можно задействовать для запроса параметра PSCredential. Параметр –ConnectionCredential аналогичен параметрам Schtasks /change /U и /P.

В отношении сценария Set-ScheduledTaskCredential.ps1 необходимо сделать три оговорки.

— Сценарий не работает для более ранних версий (для операционных систем старше Vista или Server 2008). Чтобы избежать ошибок, сценарий проверяет версию планировщика задний. Если версия слишком старая, выдается ошибка. Это не большая беда, поскольку на более старых платформах можно по-прежнему пользоваться командой Schtasks /change.

— Сценарий выдает ошибку, если назначенное задание не имеет сохраненных учетных данных. Другими словами, если задание не настроено с сохраненными учетными данными, то сценарий не может выполнить их изменение.

— Сценарий работает с использованием объектов сценариев планировщика заданий (см. список в документе «Объекты сценариев планировщика заданий», http://msdn.microsoft.com/en-us/library/windows/desktop/aa383607.aspx). Эти объекты не поддерживают зашифрованные учетные данные, поэтому сценарий временно извлекает пароли используемых объектов PSCredential в виде открытого текста. Эти пароли не отсылаются как открытый текст по сети, но расшифровываются в памяти на время выполнения сценария. Таким образом, существует возможность, что в случае аварийного отказа компьютера, на котором выполняется сценарий, дамп его памяти может содержать копию пароля в виде открытого текста.

Пример использования

Самый простой способ применения сценария – изменение сохраненных учетных данных назначенного задания на текущем компьютере:

PS C:> Set-ScheduledTaskCredential  «My Task»  

· Команда запрашивает учетные данные для задания ‘My Task’. Имя параметра -TaskName опущено.

Как уже говорилось, параметр -TaskName поддерживает конвейерный ввод, поэтому, если требуется изменить сохраненные на компьютере учетные данные нескольких заданий, можно поместить имена этих заданий в текстовый файл, подавая выходной результат выполнения команды Get-Content на вход Set-ScheduledTaskCredential.ps1:

PS C:> Get-Content TaskNames.txt | 
Set-ScheduledTaskCredential 

Параметр -ComputerName поддерживает только одно имя компьютера, однако все же можно подключиться к нескольким компьютерам с помощью команды ForEach-Object:

PS C:>  «server1»,«server2»  | 
>> ForEach-Object { Set-ScheduledTaskCredential ` 
>> -TaskName  «My Task»  -ComputerName $_ } 

· Эта команда позволяет изменить учетные данные для задания ‘My Task’ на компьютерах ‘server1’ и ‘server2’. При выполнении этой команды вы заметите, что запрос на ввод учетных данных задания выдается дважды, поскольку ForEach-Object выполняет сценарий два раза (по одному для каждого имени компьютера). Чтобы устранить это неудобство, вначале создайте объект PSCredential, а затем используйте его с параметром -TaskCredential:

PS C:> $cred = Get-Credential  «MYDOMAINMyUserName»
PS C:>  «server1»,«server2»  | 
>> ForEach-Object { Set-ScheduledTaskCredential ` 
>> -TaskName  «My Task»  -TaskCredential $cred ` 
>> -ComputerName $_ } 

· Первая команда задает создание объекта PSCredential и его сохранение в переменной $cred, а вторая использует этот набор учетных данных для изменения учетных данных назначенного задания ‘My Task’ на компьютерах 'server1’ и ‘server2’.

Если ваша учетная запись не обладает полномочиями изменения назначенных заданий на компьютере, то с помощью параметра –ConnectionCredential задайте учетные данные, используемые для подключения:

PS C:> Set-ScheduledTaskCredential  «My Task»  ` 
>> -ComputerName  «server1»  ` 
>> -ConnectionCredential (Get-Credential)

· Эта команда генерирует два запроса учетных данных. Первый запрос содержит требование ввести учетные данные для параметра -ConnectionCredential, а второй – учетные данные назначенного задания. Выдачи обоих запросов можно избежать, создав объекты PSCredential перед запуском сценария.

Управление учетными данными заданий

· Планировщик заданий в Vista, Server 2008 и более новых версиях включает ряд дополнительных возможностей, но команда Schtasks /change, к сожалению, больше не позволяет изменять сохраненные учетные данные заданий. Используя сценарий Set-ScheduledTaskCredential.ps1, можно не беспокоиться по поводу этого ограничения.

  Листинг.  Сценарий Set-ScheduledTaskCredential.ps1
# Set-ScheduledTaskCredential.ps1
# Written by Bill Stewart (bstewart@iname.com)
#requires -version 2
<#
. SYNOPSIS   Sets the credentials for one or more scheduled tasks on a computer.  . DESCRIPTION   Sets the credentials for one or more scheduled tasks on a computer.  . PARAMETER  TaskName
One or more scheduled task names. Wildcard values are not accepted. This parameter accepts pipeline input.  . PARAMETER  TaskCredential
The credentials for the scheduled task. If you don't specify this parameter, you will be prompted for credentials.  . PARAMETER  ComputerName
The computer name where the scheduled task(s) reside.  . PARAMETER  ConnectionCredential
The credentials to use when connecting to the computer.  . EXAMPLE   PS C:>Set-ScheduledTaskCredential  «My  Scheduled  Task»   This command will prompt for credentials and configure the specified task using those credentials.  . EXAMPLE   PS C:>Set-ScheduledTaskCredential  «Task 1»,«Task 2»  -ComputerName server1
This command will prompt for credentials and configure the named scheduled tasks on the computer server1.  . EXAMPLE   PS C:>Set-ScheduledTaskCredential  «Task 1»,«Task 2»  -ComputerName server1
This command will prompt for credentials and configure the named scheduled tasks on the computer server1.  . EXAMPLE   PS C:>Get-Content TaskNames.txt | Set-ScheduledTaskCredential -ConnectionCredential (Get-Credential)
This command will set scheduled task credentials for all tasks named in the file TaskNames.txt. There will be two credential prompts. The first prompt is to specify credentials to connect to the Task Scheduler service, and the second prompt is to specify credentials to use for the scheduled tasks.
#>   [CmdletBinding(SupportsShouldProcess=$TRUE)]
param(
[parameter(Mandatory=$TRUE,ValueFromPipeline=$TRUE)]
[String[]] $TaskName,
[System.Management.Automation.PSCredential] $TaskCredential,
[String] $ComputerName=$ENV:COMPUTERNAME,
[System.Management.Automation.PSCredential] $ConnectionCredential
)   begin {
$PIPELINEINPUT = (-not  $PSBOUNDPARAMETERS.ContainsKey(«TaskName»))  -and (-not $TaskName)
$TASK_LOGON_PASSWORD = 1
$TASK_LOGON_S4U = 2
$TASK_UPDATE = 4
$MIN_SCHEDULER_VERSION = 0x00010002  # Try to create the TaskService object on the local computer; throw an error on failure
try {
$TaskService = new-object -comobject  «Schedule.Service»  }
catch [System.Management.Automation.PSArgumentException] {
throw $_
}  # Assume $NULL for the schedule service connection parameters unless -ConnectionCredential used
$userName = $domainName = $connectPwd = $NULL
if ($ConnectionCredential) {
# Get user name, domain name, and plain-text copy of password from PSCredential object
$userName =  $ConnectionCredential.UserName.Split(«")[1]  $domainName =  $ConnectionCredential.UserName.Split(»«)[0]  $connectPwd = $ConnectionCredential.GetNetworkCredential().Password
}
try {
$TaskService.Connect($ComputerName, $userName, $domainName, $connectPwd)
}
catch [System.Management.Automation.MethodInvocationException] {
 write-error»Error  connecting to '$ComputerName'  — '$_'«  exit
}
# Returns a 32-bit unsigned value as a version number (x.y, where x is the
# most-significant 16 bits and y is the least-significant 16 bits).
function convertto-versionstr([UInt32] $version) {
$major = [Math]::Truncate($version / [Math]::Pow(2, 0x10)) -band 0xFFFF
$minor = $version -band 0xFFFF
 »$($major).$($minor)«  }  if ($TaskService.HighestVersion -lt $MIN_SCHEDULER_VERSION) {
write-error  (»Schedule  service on '$ComputerName' is version $($TaskService.HighestVersion)  «+ »($(convertto-versionstr($TaskService.HighestVersion))).  The Schedule service must  «+ »be  version $MIN_SCHEDULER_VERSION ($(convertto-versionstr $MIN_SCHEDULER_VERSION))  «+ »or higher.«)  exit
}  # This prevents a scoping problem--if the $TaskCredential variable
# doesn't exist, it won't get created in the correct scope--create
# new variable as a workaround
$NewTaskCredential = $TaskCredential
if (-not $NewTaskCredential) {
$NewTaskCredential =  $HOST.UI.PromptForCredential(»Task Credentials«, »Please  specify credentials for the scheduled  task.«, "», «")  if (-not $NewTaskCredential) {
 write-error»You  must specify  credentials.«  exit
}
}  function set-scheduledtaskcredential2($taskName) {
$rootFolder =  $TaskService.GetFolder(»«)  try {
$taskDefinition = $rootFolder.GetTask($taskName).Definition
}
catch [System.Management.Automation.MethodInvocationException] {
 write-error»Scheduled  task '$taskName' not found on  '$computerName'.«  return
}
$logonType = $taskDefinition.Principal.LogonType
# No need to set credentials for tasks that don't have stored credentials.
if (-not (($logonType -eq $TASK_LOGON_PASSWORD) -or ($logonType -eq $TASK_LOGON_S4U))) {
 write-error»Scheduled  task '$taskName' on '$ComputerName' doesn't have stored  credentials.«  return
}
if (-not  $PSCMDLET.ShouldProcess(»Task  '$taskName' on computer  '$ComputerName'«, »Set  scheduled task  credentials«))  { return }
try {
[Void] $rootFolder.RegisterTaskDefinition($taskName, $taskDefinition, $TASK_UPDATE,
$NewTaskCredential.UserName, $NewTaskCredential.GetNetworkCredential().Password, $logonType)
}
catch [System.Management.Automation.MethodInvocationException] {
 write-error»Error  updating scheduled task '$taskName' on '$computerName'  —  '$_'"
}
}
}   process {
if ($PIPELINEINPUT) {
set-scheduledtaskcredential2 $_
}
else {
$TaskName | foreach-object {
set-scheduledtaskcredential2 $_
}
}