Report Scheduled Tasks on servers that have local or domain accounts configured

For one of our customers, I needed to create a report of all Scheduled Tasks on their servers with a local or domain account configured. They needed this report because they are switching to more strict Group Policies and they needed to know what user accounts should have the “Log on as a batch job” right. In this blog post, I will show you how to create that report šŸ™‚

Challenges

Creating a report should be easy I thought, the Get-ScheduledTask should display the account configured. But it doesn’t do that, unfortunately, so…. Schtasks.exe should do that right? Started creating a script using that and… It has issues connecting to servers, throwing ‘Cant retrieve XML…’ errors, and couldn’t get that to work reliably.

But… Scheduled Tasks are basically XML files that are stored in C:\Windows\System32\Tasks šŸ™‚ So I started to create a script that parses those files and that gave me the results I wanted, I could now retrieve the Server, Task Name, and the credentials when the task was configured to run whether the user is logged on or not.

Running the script

When executing the “Scheduled Tasks inventory.ps1” script, it will search for all Computer Accounts with a Windows Server operating system and parse all Task Schedules for credentials and skips things like System, Local Service, etc.

In the example below it runs it against my Windows Server 2022 Domain Controller:

When done, it saves a CSV to the location specified at the top of the script, the report lists all Scheduled Tasks found that not running as a built-in/system account:

The script

Below is the script that I made, changed the $CSVlocation to the location and filename you prefer šŸ™‚

$total = @()
$CSVlocation = 'C:\Temp\ScheduledTasks.csv'
foreach ($server in Get-ADComputer -Filter * -Properties OperatingSystem | Where-Object OperatingSystem -Match 'Windows Server' | Sort-Object Name) {

    try {
        $scheduledtasks = Get-ChildItem "\\$($Server.name)\c$\Windows\System32\Tasks" -Recurse -File -ErrorAction Stop
        Write-Host ("Retrieving Scheduled Tasks list for {0}" -f $server.Name) -ForegroundColor Green
    }
    catch {
        Write-Warning ("Unable to retrieve Scheduled Tasks list for {0}" -f $server.Name)
        $scheduledtasks = $null
    }

    foreach ($task in $scheduledtasks | Sort-Object Name) {
        try {
            $taskinfo = (Get-Content -Path $task.FullName -ErrorAction stop)
            Write-Host ("Processing Task {0} on {1}" -f $task.Name, $server.name)
        }
        catch {
            Write-Warning ("Could not read {0}" -f $task.FullName)
            $taskinfo = $null
        }
        
        if ($taskinfo.Task.Settings.Enabled -eq 'true' `
                -and $taskinfo.Task.Principals.Principal.GroupId -ne 'NT AUTHORITY\SYSTEM' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'AnyUser' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'Authenticated Users' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'AllUsers' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'LocalAdmin' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'LocalService' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'LocalSystem' `
                -and $taskinfo.Task.Principals.Principal.Id -ne 'Users' `
                -and $taskinfo.Task.Principals.Principal.LogonType -ne 'InteractiveToken' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'Administrators' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'EVERYONE' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'INTERACTIVE' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'LOCAL SERVICE' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'NETWORK SERVICE' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'NT AUTHORITY\SYSTEM' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'SYSTEM' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'S-1-5-18' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'S-1-5-19' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'S-1-5-20' `
                -and $taskinfo.Task.Principals.Principal.UserId -ne 'USERS' `
                -and $taskinfo.Task.Triggers.LogonTrigger.Enabled -ne 'True' 
        ) {
            $foundtasks = [PSCustomObject]@{
                Server    = $Server.name
                TaskName  = $task.Name
                RunAsUser = $taskinfo.Task.Principals.Principal.UserId
            }    
            $Total += $foundtasks
        }
    }
}

$Total | Sort-Object Server, TaskName | Export-CSV -NoTypeInformation -Delimiter ';' -Encoding UTF8 -path $CSVlocation

Download the script(s) from GitHub here

4 thoughts on “Report Scheduled Tasks on servers that have local or domain accounts configured

  1. You can also use WMI, then you don’t need to parse xml files:

    get-ciminstance -ComputerName [put computer name here] -Namespace ‘ROOT\Microsoft\Windows\TaskScheduler’ -Query ‘SELECT * FROM MSFT_ScheduledTask’

    • That’s also an option, it does retrieve the Principal security settings that I needed. I think I queried the Win32_ScheduledJob class which only returned the AT-like schedules and that I stopped there…

      It would be great if the Get-ScheduledTask cmdlet would show the credentials šŸ™

  2. I had a similar need myself and I approached the problem from the schtasks.exe command line tool. I wrote a Powershell wrapper function called Get-ScheduledTaskUser which is part of my PoshFunctions module on the Powershell Gallery.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.