Skip to content

ParseScheduledTasksXML Hands on Labs

Start in the browser with a short investigation on fictional workstation WS-042. Practise the PowerShell command, read the CSV, check the source XML and match an action start event. Then try the Full Lab with supplied evidence in a Windows VM.

Interactive Lab

Beginner · about 10 minutes · no installation. A PowerShell alert appeared after WS-042 restarted. Find the task behind it and explain what the evidence shows. Start in PowerShell: open Show command, enter it at the prompt and press Enter. Then read the CSV, task XML and log. Each step tells you what to do next.

The console follows the PowerShell screenshot and the maintainer's script. ParseScheduledTasksXML runs in PowerShell. Timeline Explorer is the separate application used to view its CSV. The browser recreates the actions needed for this exercise with fictional evidence.

Full Lab

Windows VM / real parser

ParseScheduledTasksXML Full Lab

Three guided activities

Case SUSA-PST-072 / follow the task

Find the task. Check the log. Explain what happened.

A PowerShell alert appeared after workstation WS-042 restarted. Use the real parser to read four collected task files. Find the enabled PowerShell task, check when it starts, and leave a short finding for the next analyst.

Beginner · about 30 minutes. Expand one step at a time. Each activity ends with a small result you can save.

Tick a checkpoint after saving its files. This page remembers your ticks in this browser; it cannot check your VM.

Investigation brief

Case questionWhich task could start PowerShell after a restart, and does the log show its action starting?
Your evidence

Four task XML files, a file hash manifest, a readable event table and an inert script sample. All describe fictional WS-042.

Real case note: This pack is fictional. You will run the real parser on real XML files. The event table contains synthetic excerpts, not an EVTX log. In an incident, use a collected Tasks folder and original logs from the affected computer.

Activity 1: Prepare the case

Your first winOpen the training pack and check that its six evidence files match the manifest.
1. Unpack the evidence into a new case folder

Keep the downloaded ZIP. It is your source package. These commands check its SHA-256, a fingerprint of the file's contents, then extract it. Use a new folder if you have already done this lab.

$pstCase = 'C:\Cases\SUSA-PST-072'
$pstPack = "$env:USERPROFILE\Downloads\SUSA-PST-072-training.zip"
$pstTool = 'C:\Tools\ParseScheduledTasksXML\ParseScheduledTasksXML.ps1'
if (-not (Test-Path -LiteralPath $pstTool -PathType Leaf)) { throw 'Install the parser first, or correct pstTool.' }
if (Test-Path -LiteralPath $pstCase) { throw 'Case folder exists. Choose a new pstCase folder.' }
$pstPackHash = (Get-FileHash -LiteralPath $pstPack -Algorithm SHA256).Hash
if ($pstPackHash -ne 'BDDAE2E12131BCBD9367F3697052EB448EDDEBBA0B4BE2765BF40711FC80259B') {
    throw 'Training ZIP hash does not match. Download the pack again.'
}
Expand-Archive -LiteralPath $pstPack -DestinationPath $pstCase -ErrorAction Stop
New-Item -ItemType Directory -Path "$pstCase\derived", "$pstCase\notes" -ErrorAction Stop | Out-Null
$pstPackHash | Set-Content -LiteralPath "$pstCase\notes\pack-sha256.txt"
Get-ChildItem -LiteralPath "$pstCase\source\Tasks" -File | Select-Object Name, Length

Look for: four files under source\Tasks. They have no .xml extension, but contain XML. derived is the separate folder for the parser's CSV; notes is for your saved checks.

2. Check the file hashes and record the tool

A manifest lists the supplied files and their hashes. Check it before parsing, then save the check and your tool details.

$pstManifest = Import-Csv -LiteralPath "$pstCase\manifest.csv"
$pstChecked = @(foreach ($pstItem in $pstManifest) {
    $pstFile = Join-Path $pstCase $pstItem.RelativePath
    $pstActual = (Get-FileHash -LiteralPath $pstFile -Algorithm SHA256 -ErrorAction Stop).Hash
    [pscustomobject]@{
        RelativePath = $pstItem.RelativePath
        Expected = $pstItem.SHA256
        Actual = $pstActual
        Match = ($pstActual -eq $pstItem.SHA256)
    }
})
$pstChecked | Export-Csv -LiteralPath "$pstCase\notes\integrity-check.csv" -NoTypeInformation
$pstChecked | Select-Object RelativePath, Match | Format-Table -AutoSize
if ($pstChecked.Count -ne 6 -or @($pstChecked | Where-Object Match -EQ $false).Count) {
    throw 'One or more files failed the check. Keep the result and inspect the pack.'
}
Get-FileHash -LiteralPath $pstTool -Algorithm SHA256 |
    Export-Csv -LiteralPath "$pstCase\notes\parser-sha256.csv" -NoTypeInformation
@(
    'Case: SUSA-PST-072 / fictional training evidence'
    'Evidence host: WS-042 / event times: UTC'
    "Analyst VM: $env:COMPUTERNAME"
    "Analyst time zone: $((Get-TimeZone).Id)"
    "Review started: $(Get-Date -Format o)"
    "Parser: $pstTool"
    'Parser source: https://github.com/vikas891/My-Scripts/blob/master/General/ParseScheduledTasksXML.ps1'
) | Set-Content -LiteralPath "$pstCase\notes\case-context.txt"

Look for: six rows with Match set to True. This checks the four task files, the event table and the script sample. Add the parser's download date or Git commit to case-context.txt.

A hash is missing or does not match

Check that you downloaded the ZIP from this page and extracted it into the folder named in $pstCase. Do not edit source files to make them match. Keep the failed check, download a fresh pack and choose a new case folder before retrying. Matching hashes check this training copy, not the authenticity of a real incident acquisition.

Ready to parsePause here if needed. Keep the case folder and the same PowerShell window.

Next: turn the four task files into a CSV.

Continue to parsing

Activity 2: Find the task

Your next winSave the parser output and find the enabled task that runs Updater.ps1.
Compare before choosing

Two tasks mention PowerShell. Check whether both are enabled and whether they run the same script.

Returning after Activity 1?

Open Windows PowerShell in the same VM. Set these two paths to your saved case and installed tool, then continue below. Do not extract the pack again.

$pstCase = 'C:\Cases\SUSA-PST-072'
$pstTool = 'C:\Tools\ParseScheduledTasksXML\ParseScheduledTasksXML.ps1'
1. Run the parser and save the console session

-Path points to the task files. -Output points to an existing folder for the CSV. A transcript saves your command and the console messages.

Start-Transcript -LiteralPath "$pstCase\notes\parser-session.txt" -ErrorAction Stop
& $pstTool -Path "$pstCase\source\Tasks" -Output "$pstCase\derived"
Stop-Transcript

Look for: the CSV's full path in yellow. Its filename ends with _ParsedScheduledTasks_XML.csv; the prefix is the run time in UTC. The parser reads XML and exports fields. It does not run the commands described in the tasks.

The parser does not run

Confirm that $pstTool points to ParseScheduledTasksXML.ps1, the source folder contains the four files, and derived exists. For a policy error, follow the process only session in Install and open. Keep the error in your notes. If the transcript is still active after a failure, enter Stop-Transcript before retrying.

2. Count the CSV rows and save its hash

An export message does not guarantee that every file parsed. This script suppresses individual parsing errors, so check the result.

$pstCsvFiles = @(Get-ChildItem -LiteralPath "$pstCase\derived" -File -Filter '*_ParsedScheduledTasks_XML.csv')
if ($pstCsvFiles.Count -ne 1) { throw 'Expected one CSV. Check the derived folder and identify the run you want.' }
$pstCsv = $pstCsvFiles[0].FullName
$pstRows = @(Import-Csv -LiteralPath $pstCsv)
$pstInputCount = @(Get-ChildItem -LiteralPath "$pstCase\source\Tasks" -File -Recurse).Count
"Source files: $pstInputCount / CSV rows: $($pstRows.Count)"
Get-FileHash -LiteralPath $pstCsv -Algorithm SHA256 |
    Export-Csv -LiteralPath "$pstCase\notes\csv-sha256.csv" -NoTypeInformation
if ($pstInputCount -ne 4 -or $pstRows.Count -ne 4) { throw 'Expected four files and four rows in this pack. Inspect the inputs and output.' }
$pstRows | Select-Object Task_Name, Principals_UserContext, Enabled | Format-Table -AutoSize

Look for: Source files: 4 / CSV rows: 4 and four task names. Equal counts are a useful check; you will also compare the selected row with its XML.

The count is different, or I parsed twice

Keep the output and transcript. Check each source file is readable XML. For multiple CSVs, read the timestamped names and transcript to select the correct run. Set $pstCsv to that file's full path and load it with $pstRows = @(Import-Csv -LiteralPath $pstCsv). Keep any retry as a separate run; do not overwrite your first output or transcript. Do not copy this pack's expected count to a real case.

3. Find PowerShell in Timeline Explorer and save the row

Open Timeline Explorer, choose File → Open, and select the CSV in derived. Type powershell in Find. Read the two matching rows, scrolling right to see Enabled and Action_Arguments.

Find the row where Enabled is true and the command runs C:\ProgramData\SystemHealth\Updater.ps1. Principals_UserContext is S-1-5-18, the SYSTEM account. The other PowerShell task is disabled. Then save the full selected row:

$pstLead = @($pstRows | Where-Object Task_Name -EQ '\SystemHealthUpdate')
if ($pstLead.Count -ne 1) { throw 'Expected one SystemHealthUpdate row. Check the CSV.' }
$pstLead | Export-Csv -LiteralPath "$pstCase\notes\selected-task.csv" -NoTypeInformation
$pstLead | Format-List Task_Name, Principals_UserContext, LogonType, Enabled, Action_Arguments

Look for: \SystemHealthUpdate, S-1-5-18, ServiceAccount, true, and the full PowerShell command. A name that sounds like maintenance is not proof that the task is legitimate or malicious.

Your task lead is savedYou can stop here and return to the saved files. You do not need to parse again.

Next: check when the task starts and match the log entry.

Continue to the evidence checks

Activity 3: Check and explain

Your final winConnect the task definition to the log and save a finding another analyst can check.
Returning in a new PowerShell window?

Set $pstCase to your saved case folder. Activity 3 reads the saved files; no second parser run is needed.

$pstCase = 'C:\Cases\SUSA-PST-072'
1. Read the original task XML

The CSV does not include triggers. Read the task file as text; do not import it into Task Scheduler.

Get-Content -LiteralPath "$pstCase\source\Tasks\SystemHealthUpdate"

Look for: BootTrigger, S-1-5-18, Enabled=true, powershell.exe and the same arguments as selected-task.csv. BootTrigger means Windows startup. The XML shows what the task was set to do, not that it ran. The Author and Date values inside XML do not prove who created the task or the actual creation time.

2. Match the task with an action start entry

The supplied table is a synthetic event excerpt, not an EVTX file. It represents entries from Microsoft-Windows-TaskScheduler/Operational on the same fictional workstation. Keep both matching entries so you can compare registration with an action start.

$pstEvents = Import-Csv -LiteralPath "$pstCase\supporting\TaskScheduler-Operational.csv"
$pstMatches = @($pstEvents | Where-Object TaskName -EQ '\SystemHealthUpdate')
$pstMatches | Export-Csv -LiteralPath "$pstCase\notes\matched-events.csv" -NoTypeInformation
$pstMatches | Format-Table TimeCreatedUTC, EventId, TaskName, ActionName -AutoSize

Look for: two rows. Event 106 at 03:00:00Z records registration. Event 200 at 03:10:04Z records the PowerShell action starting. Match the task name and action, not just the event ID. No completion event or process log is supplied, so script success remains unknown.

3. Inspect the sample and write a short finding

Read the script sample without running it:

Get-Content -LiteralPath "$pstCase\supporting\Updater.ps1"

Look for: comments only. This is an inert stand in, not the script from a real alert. It cannot show whether a real payload stole data or did anything harmful.

Paste this template into Notepad, fill it with your actual results, and save it as notes\handover.txt. A clearly explained missing result is better than an invented success.

Case: SUSA-PST-072 / fictional WS-042
Question: Which task could start PowerShell after a restart?
Source ZIP and checked files:
Parser source, download date or commit, and SHA-256:
Parser command and saved CSV path:
Source-file count, CSV-row count, and CSV SHA-256:
Selected task: name, account, Enabled and complete command.
XML: trigger and source task-file path.
Log: matching task, action, event ID and UTC time.
Finding: What does the definition show? What does the log add?
Limits: What is still unknown? Note any failed checks.
Next check: What original script or process/PowerShell logs are needed?

A finding you can support: the XML defines an enabled SYSTEM task that starts PowerShell at boot; the matching training entry records that action starting. It does not establish script completion, harmful behavior or a human creator. In a real case, read the original script and check process and PowerShell logs next.

Optional: what else would you check in a real incident?

Keep the original Task Scheduler EVTX and compare the same task instance across start and completion events. Check the original script's contents, hash and path, and process or PowerShell logs. For missing or hidden tasks, compare the collected XML with registry TaskCache records. The task name, SYSTEM account or a missing log entry alone is not a verdict. ZIP extraction also changes the working files' timestamps; do not treat this pack's TaskFile_LastModifiedTime as an original endpoint time.

Case closeout4. Check your saved files before closing the VM
Keep the case first

Check the three groups in the checklist below. Open handover.txt and confirm another analyst can locate the CSV, selected task and matching event rows.

Copy the source ZIP and the complete case folder outside the VM. Open the copied handover and CSV before restoring the VM snapshot. Close Timeline Explorer and PowerShell when finished. No task was registered or started by this lab, so there is no training task to delete.

Ready for the next analystFinish with the files you actually saved. Browser ticks are reminders, not proof of completed analysis.

Finish by checking the case folder someone else will receive.

Review the evidence checklist

Full Lab evidence checklist

Case folder handoverSave the two checkpoints in each activity.
  • Source and checksThe original ZIP, source/, supporting/, manifest.csv, README.txt and the context, integrity and parser hash notes.
  • Parser resultThe timestamped CSV in derived/, parser-session.txt, csv-sha256.csv and selected-task.csv.
  • Findingmatched-events.csv and handover.txt with the XML trigger, UTC event time, gaps and a next check.

Your finish line: a saved task lead, the XML and log checks, and a finding someone else can review.