Query Details

MDE Schedulted Tasks

Query

# MDE - Scheduled Task Execution

![KQL](https://img.shields.io/badge/language-KQL-blue.svg)
![Status: Testing](https://img.shields.io/badge/status-testing-blue.svg)

## Query Information

### Description

Get an overview of scheduled Tasks running in your environment...

#### References

- none

### Author

- **Alex Verboon**

## KQL

All scheduled Task executions on servers

```kql
let devicescope = (DeviceInfo
| summarize arg_max(TimeGenerated,*) by DeviceId
| where OnboardingStatus == 'Onboarded'
| where OSPlatform startswith "WindowsServer"
|  project DeviceName, DeviceId);
let runningtasks = (
DeviceProcessEvents
| where ActionType == @"ProcessCreated"
| where InitiatingProcessFileName =~ "svchost.exe"
| where InitiatingProcessCommandLine has "Schedule"
| extend TaskRunContext = AccountName
| project
   Timestamp,
   DeviceName,
   DeviceId,
   TaskRunContext,
   AccountDomain,
   AccountSid,
   FileName,
   ProcessCommandLine,
   InitiatingProcessFileName,
   InitiatingProcessCommandLine,
   InitiatingProcessAccountName
| order by Timestamp desc);
devicescope
| join kind=leftouter (runningtasks)
on $left. DeviceId == $right. DeviceId
| project-away DeviceId1, DeviceId1, DeviceName1, DeviceId
```

// powershell, cmd and cscript only
| where FileName in ('cmd.exe','powershell.exe',"cscript.exe")

// only show custom accounts
| where TaskRunContext !in ('system','local service','network service')


Scheduled Task Intervals

```kql
let Lookback = 7d;
DeviceProcessEvents
| where Timestamp > ago(Lookback)
| where InitiatingProcessFileName =~ "svchost.exe"
| where InitiatingProcessCommandLine has "Schedule"
//
// Process started by Task Scheduler
//
| extend RunAsAccount = strcat(AccountDomain, @"\", AccountName)
| project
    Timestamp,
    DeviceName,
    RunAsAccount,
    AccountSid,
    ExecutedFile = FileName,
    CommandLine = ProcessCommandLine,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine,
    InitiatingProcessAccountName,
    ProcessId,
    InitiatingProcessId
//
// Calculate execution intervals
//
| sort by DeviceName asc, CommandLine asc, RunAsAccount asc, Timestamp asc
| serialize
| extend
    PreviousTimestamp = prev(Timestamp),
    PreviousDeviceName = prev(DeviceName),
    PreviousCommandLine = prev(CommandLine),
    PreviousRunAsAccount = prev(RunAsAccount)
| where DeviceName == PreviousDeviceName
    and CommandLine == PreviousCommandLine
    and RunAsAccount == PreviousRunAsAccount
| extend IntervalMinutes = datetime_diff("minute", Timestamp, PreviousTimestamp)
//
// Summarize schedule behavior
//
| summarize
    FirstSeen = min(PreviousTimestamp),
    LastSeen = max(Timestamp),
    ExecutionCount = count() + 1,
    MinIntervalMinutes = min(IntervalMinutes),
    AvgIntervalMinutes = round(avg(IntervalMinutes),2),
    MaxIntervalMinutes = max(IntervalMinutes),
    ObservedIntervals = make_set(IntervalMinutes,20)
    by
    DeviceName,
    RunAsAccount,
    AccountSid,
    ExecutedFile,
    CommandLine
//
// Human readable schedule guess
//
| extend InferredSchedulePattern = case(
    AvgIntervalMinutes between (0 .. 2), "Every few minutes",
    AvgIntervalMinutes between (14 .. 16), "Every ~15 minutes",
    AvgIntervalMinutes between (29 .. 31), "Every ~30 minutes",
    AvgIntervalMinutes between (55 .. 65), "Every ~1 hour",
    AvgIntervalMinutes between (115 .. 125), "Every ~2 hours",
    AvgIntervalMinutes between (350 .. 370), "Every ~6 hours",
    AvgIntervalMinutes between (710 .. 730), "Every ~12 hours",
    AvgIntervalMinutes between (1430 .. 1450), "Daily",
    AvgIntervalMinutes between (10000 .. 10120), "Weekly",
    strcat("~Every ", tostring(AvgIntervalMinutes), " minutes")
)
| project
    DeviceName,
    RunAsAccount,
    AccountSid,
    ExecutedFile,
    InferredSchedulePattern,
    ExecutionCount,
    FirstSeen,
    LastSeen,
    MinIntervalMinutes,
    AvgIntervalMinutes,
    MaxIntervalMinutes,
    ObservedIntervals,
    CommandLine
| order by ExecutionCount desc
```

Explanation

This KQL query is designed to provide an overview of scheduled tasks running on Windows Server devices in your environment. It consists of two main parts:

  1. Scheduled Task Executions on Servers:

    • The query first identifies devices that are onboarded and running Windows Server.
    • It then looks for processes that were created by the Task Scheduler (svchost.exe with "Schedule" in the command line).
    • It filters for processes that are specifically cmd.exe, powershell.exe, or cscript.exe.
    • It excludes tasks run by system accounts like 'system', 'local service', and 'network service', focusing on custom accounts.
    • The result is a list of scheduled tasks executed by custom accounts, showing details like the task's context, command line, and initiating process.
  2. Scheduled Task Intervals:

    • This part of the query analyzes task execution over the past 7 days.
    • It calculates the intervals between task executions to infer the scheduling pattern.
    • It summarizes the execution behavior, including the first and last seen times, execution count, and interval statistics (minimum, average, maximum).
    • It attempts to guess a human-readable schedule pattern (e.g., "Every ~15 minutes", "Daily").
    • The output includes details like the device name, account running the task, executed file, inferred schedule pattern, and command line.

Overall, this query helps you understand which scheduled tasks are running, who is running them, and how frequently they are executed on your Windows Server devices.

Details

Alex Verboon profile picture

Alex Verboon

Released: June 29, 2026

Tables

DeviceInfoDeviceProcessEvents

Keywords

DeviceInfoDeviceProcessEventsDeviceNameDeviceIdTaskRunContextAccountDomainAccountSidFileNameProcessCommandLineInitiatingProcessFileNameInitiatingProcessCommandLineInitiatingProcessAccountNameProcessIdTimestampRunAsAccountExecutedFileCommandLineExecutionCountFirstSeenLastSeenMinIntervalMinutesAvgIntervalMinutesMaxIntervalMinutesObservedIntervalsInferredSchedulePattern

Operators

letsummarizearg_maxbywherestartswithprojectextendorder byjoinkindonproject-awayin!inagohasstrcatsort byserializeprevdatetime_diffsummarizeminmaxcountroundavgmake_setcasebetweentostring

Actions