Overview
This article explains advanced automation with Windows Task Scheduler:
conditional triggers, event-log–driven execution, registering and controlling tasks via PowerShell, and security-minded operational practices.
Variable notation
| Variable | Example | Note |
|---|---|---|
<<TASK_NAME>> |
Daily-Backup |
Task name |
<<SCRIPT_PATH>> |
C:\Scripts\backup.ps1 |
Script path to execute |
<<EVENT_ID>> |
4625 |
Event ID that triggers the task |
<<USERNAME>> |
Administrator |
User that runs the task |
<<TASK_PATH>> |
\MyCompany\Maintenance |
Task folder path in the library (optional) |
Step 1: Register tasks and set the basics
When registering a task with PowerShell, you combine three primary cmdlets:
| Cmdlet | Role |
|---|---|
New-ScheduledTaskAction |
Defines the program/script to run |
New-ScheduledTaskTrigger |
Defines when to run (time/event) |
Register-ScheduledTask |
Registers the full task (Action + Trigger + Settings) |
① Key options for New-ScheduledTaskAction
| Option | Description | Example |
|---|---|---|
-Execute |
Executable | "powershell.exe" |
-Argument |
Command-line arguments | "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File <<SCRIPT_PATH>>" |
-WorkingDirectory |
Working directory | "C:\Scripts" |
-Id |
Identifier when multiple actions exist | "Action1" |
💡 To configure multiple actions, create several
New-ScheduledTaskActionobjects and pass them as an array.
② Key options for New-ScheduledTaskTrigger
| Option | Description | Example |
|---|---|---|
-Daily |
Run daily | Combine with -At 3:00AM |
-Weekly |
Run weekly | -DaysOfWeek Monday,Wednesday |
-Once |
Run once | -At (Get-Date).AddHours(1) |
-AtStartup |
Run at system startup | — |
-AtLogOn |
Run at user logon | — |
-RepetitionInterval |
Repeat interval | (New-TimeSpan -Minutes 30) |
-RepetitionDuration |
Repeat duration | (New-TimeSpan -Days 1) |
-RandomDelay |
Jitter | (New-TimeSpan -Minutes 5) |
-StartBoundary / -EndBoundary |
Validity window | "2025-01-01T00:00:00" |
⚠️
-RepetitionIntervaland-RepetitionDurationare only valid for certain trigger types (e.g., Daily/Once).
③ Key options for New-ScheduledTaskSettingsSet (optional)
| Option | Description | Example |
|---|---|---|
-AllowStartIfOnBatteries |
Allow on battery power | $false |
-DontStopIfGoingOnBatteries |
Keep running if power switches to battery | $false |
-StartWhenAvailable |
Run as soon as possible | $true |
-Hidden |
Hide the task | $true |
-RunOnlyIfNetworkAvailable |
Require network connectivity | $true |
-ExecutionTimeLimit |
Max run time | (New-TimeSpan -Hours 2) |
-MultipleInstances |
Multi-instance policy (IgnoreNew/Parallel/Queue) | "IgnoreNew" |
-RestartCount / -RestartInterval |
Retry count and interval | 3, (New-TimeSpan -Minutes 5) |
💡
New-ScheduledTaskSettingsSetbuilds the object passed to-Settingsso you can centrally control power, retry, and network conditions.
④ Key options for Register-ScheduledTask
| Option | Description | Example |
|---|---|---|
-TaskName |
Name to register | "Daily-Backup" |
-TaskPath |
Library folder | "\MyCompany\Maintenance" |
-Action |
Predefined action object | $action |
-Trigger |
Predefined trigger object | $trigger |
-Settings |
Additional settings (power, retry, etc.) | $settings |
-Description |
Admin description | "Daily maintenance backup task" |
-User |
Run account | "SYSTEM" or "Administrator" |
-RunLevel |
Privilege level | Highest |
-Force |
Overwrite existing | — |
💡 A complete task is Action + Trigger + Settings combined.
⑤ Example configuration
# Define the action
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NoProfile -ExecutionPolicy Bypass -File <<SCRIPT_PATH>>"
# Define the trigger (daily at 03:00)
$trigger = New-ScheduledTaskTrigger -Daily -At 3:00AM
# Register the task
Register-ScheduledTask -TaskName "<<TASK_NAME>>" `
-TaskPath "<<TASK_PATH>>\Daily" `
-Action $action -Trigger $trigger `
-User "<<USERNAME>>" -Description "Run every day at 03:00"
⑥ Operational notes
- Use
-ExecutionPolicy Bypassonly in controlled internal environments; in production prefer signed scripts. - Run as SYSTEM or a dedicated service account (principle of least privilege).
- Minimum
-RepetitionIntervalis 1 minute; default upper bound for-RepetitionDurationis often 1 day. - Use
-Forceto overwrite existing tasks. - Use
-RunLevel Highestonly when admin privileges are required.
Step 2: Event-log triggers (with XML template)
New-ScheduledTaskTrigger does not currently expose an event trigger (e.g., -OnEvent).
To use event-based triggers, import an XML task definition.
Register:
$xml = @'
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Author><<USERNAME>></Author>
<Description>Run script on Security Event ID <<EVENT_ID>></Description>
</RegistrationInfo>
<Triggers>
<EventTrigger>
<Enabled>true</Enabled>
<Subscription><![CDATA[
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID=<<EVENT_ID>>)]]</Select>
</Query>
</QueryList>
]]></Subscription>
</EventTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>SYSTEM</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -File "<<SCRIPT_PATH>>"</Arguments>
</Exec>
</Actions>
</Task>
'@
Register-ScheduledTask -TaskName "<<TASK_NAME>>" -TaskPath "<<TASK_PATH>>\OnEvent" -Xml $xml -User "SYSTEM"
Step 3: Combine triggers with run conditions
Define Conditions (power, network, idle) using New-ScheduledTaskSettingsSet.
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries:$false `
-DontStopIfGoingOnBatteries:$false `
-StartWhenAvailable:$true `
-Hidden:$false `
-RunOnlyIfNetworkAvailable:$true `
-ExecutionTimeLimit (New-TimeSpan -Hours 2)
Register-ScheduledTask -TaskName "<<TASK_NAME>>_Cond" `
-TaskPath "<<TASK_PATH>>\Conditional" `
-Action $action -Trigger $trigger -Settings $settings `
-User "<<USERNAME>>" -Description "Conditional task (requires network / AC power)"
Common condition patterns
| Condition | Setting | Description |
|---|---|---|
| AC power only | -AllowStartIfOnBatteries:$false |
Do not run on battery |
| Network required | -RunOnlyIfNetworkAvailable:$true |
Skip when network is down |
| Idle-only | XML or COM | Native PowerShell support is limited |
| Max run time | -ExecutionTimeLimit |
Prevents run-away executions |
| Retry policy | XML or repeated triggers | Some limits when using only PowerShell |
Step 4: Manage existing tasks
# List tasks
Get-ScheduledTask | Where-Object TaskPath -like "<<TASK_PATH>>*"
# Enable / disable
Enable-ScheduledTask -TaskName "<<TASK_NAME>>"
Disable-ScheduledTask -TaskName "<<TASK_NAME>>"
# Inspect runtime state
Get-ScheduledTaskInfo -TaskName "<<TASK_NAME>>" |
Select-Object TaskName, NextRunTime, LastRunTime, LastTaskResult
Enable the Operational log
The Microsoft-Windows-TaskScheduler/Operational channel may be disabled by default.
Enable it for troubleshooting and detailed history:
wevtutil sl Microsoft-Windows-TaskScheduler/Operational /e:true
Step 5: Troubleshooting and log checks
# Latest execution result
Get-ScheduledTaskInfo -TaskName "<<TASK_NAME>>"
# Operational log filtered by task name
Get-WinEvent -LogName "Microsoft-Windows-TaskScheduler/Operational" -MaxEvents 50 |
Where-Object { $_.Message -match "<<TASK_NAME>>" } |
Select-Object TimeCreated, Id, Message | Format-Table -AutoSize
Frequent causes and remedies
| Cause | Fix |
|---|---|
| Insufficient privileges | Review run account permissions / use a service account |
| Execution Policy blocks | Use signed scripts or -ExecutionPolicy Bypass in controlled envs |
Missing TaskPath / description |
Set -TaskPath and -Description for clarity |
| Event trigger not firing | Re-check XML and event filter |
| Conflicting triggers/conditions | Revisit trigger logic and Settings |
Step 6: Recommendations
- Run as SYSTEM or a dedicated service account; enforce least privilege.
- Add logging and exception handling in scripts to record success/failure.
- Template XML task definitions and version them in Git (e.g., GitHub).
- Periodically monitor with
Get-ScheduledTaskInfoand alert on anomalies. - Remove obsolete tasks with
Unregister-ScheduledTaskto keep the library tidy.
Summary
Beyond simple schedules, Task Scheduler enables powerful automation through event-driven triggers, conditional run controls, and PowerShell integration.
Managing definitions and monitoring as code improves reproducibility, governance, and operational security.
