Windows Task Scheduler: Advanced Triggers, Conditions and Hidden Tasks

7 min read

Advanced Windows Task Scheduler configuration. Event-based triggers, conditions, run-on-demand without UAC, find hidden tasks and debug failed scheduled jobs.

Share:TelegramX

Beyond basic scheduling, Task Scheduler has powerful trigger types and execution conditions that most admins never use.


Event-Based Triggers

Run a task when a specific event appears in Event Log:

# Trigger on failed logon attempt (Event ID 4625)
$trigger = New-ScheduledTaskTrigger -OnEvent `
  -Subscription '<QueryList><Query><Select Path="Security">*[System[EventID=4625]]</Select></Query></QueryList>' `
  -Delay "00:01:00"  # wait 1 minute after event

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument '-Command "Send-MailMessage -To admin@company.com -Subject \"Failed Login Detected\" -From server@company.com -SmtpServer smtp.company.com"'

Register-ScheduledTask -TaskName "Alert-FailedLogin" `
  -Action $action -Trigger $trigger -RunLevel Highest

# Trigger on new USB device connected (Event ID 2003)
$trigger = New-ScheduledTaskTrigger -OnEvent `
  -Subscription '<QueryList><Query><Select Path="Microsoft-Windows-DriverFrameworks-UserMode/Operational">*[System[EventID=2003]]</Select></Query></QueryList>'

Event Triggers via XML (Most Reliable Method)

For event triggers that need to survive edits or be portable across PCs, defining the task as XML is more reliable than building it purely from New-ScheduledTaskTrigger -OnEvent:

$xml = @"
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <EventTrigger>
      <Subscription><![CDATA[
        <QueryList>
          <Query Id="0" Path="Security">
            <Select Path="Security">*[System[EventID=4625]]</Select>
          </Query>
        </QueryList>
      ]]></Subscription>
    </EventTrigger>
  </Triggers>
  <Actions Context="Author">
    <Exec>
      <Command>powershell.exe</Command>
      <Arguments>-File C:\Scripts\alert-login-fail.ps1</Arguments>
    </Exec>
  </Actions>
</Task>
"@
Register-ScheduledTask -Xml $xml -TaskName "AlertOnFailedLogin" -Force

You can also pass event data (like the specific computer or event ID that fired) into the triggered script:

<Arguments>-File C:\Scripts\handler.ps1 -EventID $(EventID) -Computer $(Computer)</Arguments>

Access these in the script via $args or named parameters.


Retry on Failure

$settings = New-ScheduledTaskSettingsSet `
  -RestartCount 3 `                      # Retry 3 times on failure
  -RestartInterval "00:05:00" `          # Wait 5 minutes between retries
  -StartWhenAvailable $true              # Run if the scheduled time was missed (PC was off)

Idle and Workload Conditions

# Only run when PC has been idle for 10 minutes
$settings = New-ScheduledTaskSettingsSet `
  -RunOnlyIfIdle `
  -IdleDuration "00:10:00" `
  -IdleWaitTimeout "04:00:00"  # wait up to 4 hours for idle

# Stop if PC wakes from idle
$settings = New-ScheduledTaskSettingsSet `
  -RunOnlyIfIdle -StopIfGoingOffIdle

# Only run on AC power
$settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable

Register-ScheduledTask -TaskName "IdleTask" `
  -Action $action -Trigger $trigger -Settings $settings

Run Without UAC Prompt

# Run as SYSTEM (no UAC, no user required)
$principal = New-ScheduledTaskPrincipal `
  -UserId "NT AUTHORITY\SYSTEM" `
  -LogonType ServiceAccount `
  -RunLevel Highest

Register-ScheduledTask -TaskName "SilentAdminTask" `
  -Action $action -Trigger $trigger -Principal $principal

Pass Arguments to Scripts

# Script that accepts parameters
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument '-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command "& C:\Scripts\backup.ps1 -Source C:\Data -Dest D:\Backup -Verbose"'

Export and Import Tasks (Backup or Move to Another PC)

# Export a task to XML
Export-ScheduledTask -TaskName "NightlyMaintenance" | Out-File "C:\TaskBackup.xml"

# Import it (on this PC or another)
Register-ScheduledTask -Xml (Get-Content "C:\TaskBackup.xml" | Out-String) `
  -TaskName "NightlyMaintenance" -Force

Export before making major changes to a task — restoring from XML is much faster than rebuilding it from scratch.


Find and Audit Hidden Tasks

Malware often creates tasks in unusual locations:

# Tasks in root (user-created) — most legitimate tasks
Get-ScheduledTask | Where-Object {$_.TaskPath -eq "\"} |
  Select-Object TaskName, State, @{n='Action';e={$_.Actions.Execute}}

# Tasks NOT in \Microsoft\ paths
Get-ScheduledTask | Where-Object {$_.TaskPath -notlike "*\Microsoft\*"} |
  Select-Object TaskPath, TaskName, @{n='Action';e={$_.Actions.Execute}}

# Tasks running from temp or user profile (suspicious)
Get-ScheduledTask | Where-Object {
  $_.Actions.Execute -like "*\Temp\*" -or
  $_.Actions.Execute -like "*\AppData\*" -or
  $_.Actions.Execute -like "*\Users\*"
} | Select-Object TaskName, @{n='Executable';e={$_.Actions.Execute}}

Task History and Debugging

# Enable history if disabled
wevtutil sl Microsoft-Windows-TaskScheduler/Operational /e:true

# View recent task runs (all tasks)
Get-WinEvent -LogName "Microsoft-Windows-TaskScheduler/Operational" -MaxEvents 50 |
  Where-Object {$_.Id -in @(100,101,102,103,200,201)} |
  Select-Object TimeCreated, Id,
    @{n='Task';e={$_.Properties[0].Value}},
    @{n='Result';e={$_.Properties[1].Value}} |
  Format-Table -AutoSize

# Why did a task fail?
Get-ScheduledTaskInfo -TaskName "DailyScript" |
  Select-Object LastRunTime, LastTaskResult, NextRunTime

# Task result codes
# 0x0 = Success
# 0x1 = Incorrect function
# 0x41301 = Still running
# 0x41303 = Not yet run
# 0x41306 = Task stopped

Disable/Pause All Tasks (Emergency)

# Disable all non-Microsoft tasks
Get-ScheduledTask | Where-Object {$_.TaskPath -notlike "*\Microsoft\*"} |
  Disable-ScheduledTask

# Re-enable
Get-ScheduledTask | Where-Object {$_.TaskPath -notlike "*\Microsoft\*"} |
  Enable-ScheduledTask

Summary

Event triggers with <QueryList> XML. Idle conditions with -RunOnlyIfIdle. RestartCount/RestartInterval for automatic retry on failure. Run silently as SYSTEM with SYSTEM principal. Audit all non-Microsoft tasks for malware. Export tasks to XML before major changes. Enable history with wevtutil sl for debugging. Result 0x0 = success.

For basic setup — GUI walkthrough, common trigger types, everyday examples — see How to Use Task Scheduler in Windows 10 and 11: Full Guide.

Frequently Asked Questions

My task shows LastTaskResult 0x1 — what does that mean?

0x1 = the PowerShell/batch script returned exit code 1. Usually means the script encountered an error. Add -ErrorAction Stop and proper error handling to get useful exit codes.

Task runs fine manually but fails on schedule — common causes?

  1. Working directory wrong — use full paths in scripts
  2. Environment variables different for SYSTEM vs user
  3. Missing modules — add explicit Import-Module in the script
  4. Network drives not available when SYSTEM runs
  5. Execution policy — add -ExecutionPolicy Bypass to the PowerShell action

Can I run a scheduled task under a user account without that user being logged in?

Yes — set logon type to Password and provide credentials. The account needs "Log on as batch job" right: secpol.msc → Local Policies → User Rights Assignment.

Related articles

💬 Leave a comment

Comments are moderated before publishing.

← All articles