TaskScheduler

From AutoIt Wiki
Jump to navigation Jump to search

The TaskScheduler UDF offers functions to control and manipulate the Windows Task Scheduler.

Structure

The UDF uses the Windows Task Scheduler Object Model

Task Scheduler Object Model

Details can be found here:

Object/Collection Object/Collection Object/Collection Object/Collection Object/Collection Object/Collection Object/Collection
Action Actions IdleSettings NetworkSettings Principal RegisteredTask RegisteredTasks
RegistrationInfo RepetitionPattern RunningTask RunningTasks Settings TaskDefinition Folder
Folders NamedValues NamedValuePair TaskService Trigger Triggers

Each task includes the following components:

  • General Information: Metadata that describes the task, such as the task’s name, description, and location.
  • Triggers: Conditions that schedule task execution at a specific time, or in response to specific criteria.
  • Actions: List of one or more actions to achieve the tasks desired outcome.
  • Security Principals: Defines security credentials, permitted access levels, and system privileges required for task execution.
  • Conditions: Determines when a task can run, i.e only running when the targeted host is idle, or connected to power.
  • Settings: Configures how the tasks run, including when to restart a failed action, and how long the task is permitted to run.
  • History: Logs task execution data.

How to create a task

There's more than one way to skin a cat - I mean: to create a task. This list is sorted from easy to hard:

  1. Use the Task Scheduler GUI and create the task ;-)
  2. Use the wrapper functions provided by the UDF
  3. Select a task in the Task Scheduler GUI and export it to a file as XML. Modify the created XML source file and import it using the Task Scheduler GUI
  4. Export the XML source of an existing task using _TS_TaskExportXML to memory or to a file, modify this XML source and create a new task using _TS_TaskImportXML
  5. Retrieve the properties of an existing task to memory, modify the properties and create the new task by using _TS_TaskCreate, _TS_TaskPropertiesSet and _TS_TaskRegister
  6. Retrieve the properties of an existing task as AutoIt array definition, copy the array from the console to your script, modify the properties and create a new task using _TS_TaskCreate, _TS_TaskPropertiesSet and _TS_TaskRegister
  7. Create a task from scatch by using the functions of the UDF

Use the Task Scheduler GUI

The web is full of videos and tutorials describing how to create tasks using the Task Scheduler GUI (example).

Use Wrapper functions

Call the wrapper functions provided by the UDF in the following sequence:

  • _TS_Wrapper_TaskCreate: Create the TaskDefinition object and set a few general properties
  • _TS_Wrapper_PrincipalSet: Set the principal properties of a task like logon type, run level
  • _TS_Wrapper_TriggerLogon: Set a trigger to start a task when a user logs on. More trigger functions are available
  • _TS_Wrapper_ActionCreate: Set the action to be executetd whent a task gets started
  • _TS_Wrapper_TaskRegister: Register the task in a folder and pass some additional properties


Example (for better readability all error checking has been removed):

#include <TaskScheduler.au3>

; Prepare start and end date of the trigger. Format must be YYYY-MM-DDTHH:MM:SS
Global $sStartDateTime = _DateAdd("n", 2, _NowCalc()) ; Start two minutes from now
$sStartDateTime = StringReplace($sStartDateTime, "/", "-")
$sStartDateTime = StringReplace($sStartDateTime, " ", "T")
Global $sEndDateTime = _DateAdd("M", 4, _NowCalc()) ; End 4 months from now
$sEndDateTime = StringReplace($sEndDateTime, "/", "-")
$sEndDateTime = StringReplace($sEndDateTime, " ", "T")

; Connect to the Task Scheduler Service
Global $oService = _TS_Open()

; Create task "Test_Logon" in folder "\Test" to run Notepad 2 minutes after the user logged on
Global $oTaskDefinition = _TS_Wrapper_TaskCreate($oService, "Test-Description", "Test-Doku" & @CRLF & "Line 2")
_TS_Wrapper_PrincipalSet($oTaskDefinition, $TASK_LOGON_INTERACTIVE_TOKEN)
_TS_Wrapper_TriggerLogon($oTaskDefinition, 2, $sStartDateTime, $sEndDateTime)
_TS_Wrapper_ActionCreate($oTaskDefinition, "Notepad")
_TS_Wrapper_TaskRegister($oService, "Test", "Test-Logon", $oTaskDefinition, Default, Default, $TASK_LOGON_INTERACTIVE_TOKEN)

Export/Modify/Import XML

The web is full of videos and tutorials describing how to export and import a task as XML using the Task Scheduler GUI (example).

Create task using _TS_TaskExportXML/_TS_TaskImportXML

Please have a look at the _TS_TaskCopy function in the UDF. This function shows how to create a new task using both XML inport/export functions.
The XML export does not contain the password if the LogonType is set to $TASK_LOGON_PASSWORD or any other logon type that requires a password. The password must be supplied at registration time.

Example (for better readability all error checking has been removed):

#include <TaskScheduler.au3>
Global $oService = _TS_Open()
; Import task "\test\Test-Task" as XML to an array
Local $aSourceTaskXML = _TS_TaskExportXML($oService, "\Test\Test-Task")
; Modify $aSourceTaskXML as desired here
; Create the new task in the same folder with name "Test-Task-NEW" from the modified array
Global $oTask = _TS_TaskImportXML($oService, "\Test\Test-Task-NEW", 2, $aSourceTaskXML)

Retrieve existing task properties to memory

Retrieve the properties of an existing task using _TS_TaskPropertiesGet into an array, modify this array and create a new task from this array.

Example (for better readability all error checking has been removed):

#include <TaskScheduler.au3>
#include <Array.au3>

Global $sFolder = "\Test"
Global $sTaskName = "Test-Logon"

; Connect to the Task Scheduler Service
Global $oService = _TS_Open()

; Retrieve the properties of an existing task and translate the array to a string
Global $aProperties = _TS_TaskPropertiesGet($oService, $sFolder & "\" & $sTaskName, 2, True)
Global $sProperties = _ArrayToString($aProperties, @TAB)

; Modify the Trigger Delay property from 2 minutes to 5 minutes. Recreate an array from the string
$sProperties = StringReplace($sProperties, "TRIGGERS|Delay|PT2M", "TRIGGERS|Delay|PT5M")
$aProperties = StringSplit($sProperties, @TAB, $STR_NOCOUNT)

; Create a new task
; Create the Task Definition object
Global $oTaskDefinition = _TS_TaskCreate($oService)

; Set the task properties
_TS_TaskPropertiesSet($oTaskDefinition, $aProperties)

; Create a logon trigger
Global $oTrigger = _TS_TriggerCreate($oService, $oTaskDefinition, $TASK_TRIGGER_LOGON, "Logon-Trigger")
_TS_TaskPropertiesSet($oTrigger, $aProperties)

; Create an Action
Global $oAction = _TS_ActionCreate($oService, $oTaskDefinition, $TASK_ACTION_EXEC, "Logon-Action")
_TS_TaskPropertiesSet($oAction, $aProperties)

; Register the task with a new name
Global $oTask = _TS_TaskRegister($oService, $sFolder, $sTaskName  & "-3", $oTaskDefinition)

_TS_Close()

Retrieve existing task properties to AutoIt array definition

Retrieve the properties of an existing task using _TS_TaskPropertiesGet as an AutoIt array definition, grab the output from the SciTE console, copy it to your script, modify the array and create a new task from this array.

Example (for better readability all error checking has been removed):

#include <TaskScheduler.au3>
#include <Array.au3>

Global $sFolder = "\Test"
Global $sTaskName = "Test-Logon"

; Connect to the Task Scheduler Service
Global $oService = _TS_Open()

; Retrieve the properties of an existing task and translate the array to a string
Global $aProperties = _TS_TaskPropertiesGet($oService, $sFolder & "\" & $sTaskName, 3, True)

and you will get something like this

Global $aProperties[] = [ _
"TASK|Name|Test-Logon", _
"TASK|Enabled|True", _
"TASK|LastRunTime|19991130000000", _
"TASK|LastTaskResult|267011", _
"TASK|NextRunTime|18991230000000", _
"TASK|Path|\Test\Test-Logon", _
"TASK|State|3", _
"ACTIONS|Path|CMD", _
"PRINCIPAL|ID|Author", _
"PRINCIPAL|LogonType|3", _
"PRINCIPAL|UserId|xyz", _
"REGISTRATIONINFO|Author|xyz", _
"REGISTRATIONINFO|Date|2019-10-02T13:39:03", _
"REGISTRATIONINFO|Description|Test-Description", _
"REGISTRATIONINFO|Date|2019-10-02T13:39:03", _
"REGISTRATIONINFO|URI|\Test\Test-Logon", _
"SETTINGS|AllowDemandStart|True", _
"SETTINGS|AllowHardTerminate|True", _
"SETTINGS|Compatibility|2", _
"SETTINGS|DisallowStartIfOnBatteries|True", _
"SETTINGS|Enabled|True", _
"SETTINGS|ExecutionTimeLimit|PT72H", _
"SETTINGS|MultipleInstances|2", _
"SETTINGS|Priority|7", _
"SETTINGS|StopIfGoingOnBatteries|True", _
"IDLESETTINGS|IdleDuration|PT10M", _
"IDLESETTINGS|StopOnIdleEnd|True", _
"IDLESETTINGS|WaitTimeout|PT1H", _
"TRIGGERS|Enabled|True", _
"TRIGGERS|Id|_TS_Wrapper_TriggerLogon_1", _
"TRIGGERS|StartBoundary|2019-10-02T13:41:03", _
"TRIGGERS|EndBoundary|2020-02-02T13:39:03", _
"TRIGGERS|ExecutionTimeLimit|PT5M", _
"TRIGGERS|Type|9", _
"TRIGGERS|Delay|PT2M", _
"TRIGGERS|UserId|domain\user" _
"]

Copy this array to your script and modify the properties as needed. Then create a new task as described in the section above.

Create task from scratch

Please have a look at _TS_TaskCreate.au3 for a step by step instruction.

History

The Windows Task Scheduler does not offer an API to read the Task history.

The Microsoft Event Viewer provides the history records here: Event Viewer (Local) / Applications and Services Logs / Microsoft / Windows / TaskScheduler / Optional

Error and Success Constants

When an error occurs, the Task Scheduler APIs return one of the following error codes as an HRESULT value.

Functions

_TS_Open

Connects to the Task Scheduler Service of the local computer or allows to control and manipulate the Task Scheduler on another computer.

When connecting to another computer you must ensure the following before you begin (more details can be found here):

  • Your computer and the remote computer must be part of a domain or a Workgroup
  • You must know the IP Address of the remote computer
  • You must have the login credentials for the remote computer and the login credential must be part of the Administrators group on the remote computer
  • You may want to ensure that your Firewall allows “Remote Scheduled Tasks Management”

Examples how to connect to another computer (thanks to AdamUL for testing):

; Connect to a computer in AD where the current user is in a the local Admininstrators group 
; on that computer, via domain or local group, or directly. Use AD computer name.
Global $oService = _TS_Open("COMPUTERNAME")

; Connect to a computer in AD where the entered user (AdminUser) is an AD user, and is in the 
; local Administrators group on that computer, via domain or local group, or directly. Use AD computer name.
Global $oService = _TS_Open("COMPUTERNAME", "AdminUser", "AD", "Password") 

; Connect to a computer in AD where the entered local user (Administrator) is NOT an AD user, 
; and is in the local Administrators group on that computer, via local group, or directly. Use AD computer name.
Global $oService = _TS_Open("COMPUTERNAME", "Administrator", ".", "Password") 

; Non-domain computer where the entered local user (Administrator) is in the local Administrators group 
; on that computer. Use IP address or DNS name to connect.
Global $oService = _TS_Open("192.168.0.1", "Administrator", ".", "Password")

Debugging connection problems
At the bottom of this github page you will find a tool named "Task Scheduler Configuration Troubleshooter". It analyzes possible connection problems and tells you what to do or even tries to solve the problem.

_TS_TaskExportXML

The XML export does not contain the password if the LogonType is set to $TASK_LOGON_PASSWORD or any other logon type that requires a password. The password must be supplied at registration time.

_TS_TaskImportXML

The XML import does not contain the password if the LogonType is set to $TASK_LOGON_PASSWORD or any other logon type that requires a password. The password must be supplied at registration time.

_TS_TaskRegister

This function finally creates or updates the Task from a Task Definition object.

As a first step you have to make sure that the Task Definition is free from errors. Please have a look at _TS_TaskValidate to check the Task Definition for known errors.

Then you need to call _TS_TaskRegister with the correct parameters.

Here's a list of things we have learned while using the UDF:

  • To register a task to run as an Admin you probably need to run the script that creates the task as an Admin.
  • A Task Definition created from XML (_TS_TaskImportXML or _TS_TaskCopy) does not contain the password used to create the source Task. Hence the password must be supplied by the registration function.
  • When setting the logon type while creating the Principal sub-object of the Task Definition you have to set the logon type to $TASK_LOGON_NONE (or letting it default) to not overwrite the Principal setting.

_TS_TaskUpdate

After you have modified a Task you need to make the changes permanent by calling _TS_TaskUpdate or _TS_TaskRegister.