Powershell script to list scheduled tasks on remote systems
Solution 1
I finally wrote a script that suits my needs. This script will 'scan' all the servers listed in AD, searching in the c:\Windows\System32\tasks folder for xml files. Then it will write the value of the UserID xml node of each file, in the final CSV file.
Not yet perfect but totally working to list all tasks of all servers, and log which user account is used to run them.
<#
.Synopsis
PowerShell script to list all Scheduled Tasks and the User ID
.DESCRIPTION
This script scan the content of the c:\Windows\System32\tasks and search the UserID XML value.
The output of the script is a comma-separated log file containing the Computername, Task name, UserID.
#>
Import-Module ActiveDirectory
$VerbosePreference = "continue"
$list = (Get-ADComputer -LDAPFilter "(&(objectcategory=computer)(OperatingSystem=*server*))").Name
Write-Verbose -Message "Trying to query $($list.count) servers found in AD"
$logfilepath = "$home\Desktop\TasksLog.csv"
$ErrorActionPreference = "SilentlyContinue"
foreach ($computername in $list)
{
$path = "\\" + $computername + "\c$\Windows\System32\Tasks"
$tasks = Get-ChildItem -Path $path -File
if ($tasks)
{
Write-Verbose -Message "I found $($tasks.count) tasks for $computername"
}
foreach ($item in $tasks)
{
$AbsolutePath = $path + "\" + $item.Name
$task = [xml] (Get-Content $AbsolutePath)
[STRING]$check = $task.Task.Principals.Principal.UserId
if ($task.Task.Principals.Principal.UserId)
{
Write-Verbose -Message "Writing the log file with values for $computername"
Add-content -path $logfilepath -Value "$computername,$item,$check"
}
}
}
The output is a comma-separated file generated on your desktop, like this one :
> SRV028,CCleanerSkipUAC,administrator
> SRV029,GoogleUpdateTaskMachineCore,System
> SRV030,GoogleUpdateTaskMachineUA,System
> SRV021,BackupMailboxes,DOMAIN\administrator
> SRV021,Compress&Archive,DOMAIN\sysScheduler
Solution 2
Hey thought I'd share a modified version of the script posted by Ob1lan. The modification helps find tasks in nested folders and lists the status of the task as well as details included in the original.
$Computers = (get-adcomputer -filter {operatingsystem -like "*server*"}).name
$ErrorActionPreference = "SilentlyContinue"
$Report = @()
foreach ($Computer in $Computers)
{
if (test-connection $Computer -quiet -count 1)
{
#Computer is online
$path = "\\" + $Computer + "\c$\Windows\System32\Tasks"
$tasks = Get-ChildItem -recurse -Path $path -File
foreach ($task in $tasks)
{
$Details = "" | select ComputerName, Task, User, Enabled, Application
$AbsolutePath = $task.directory.fullname + "\" + $task.Name
$TaskInfo = [xml](Get-Content $AbsolutePath)
$Details.ComputerName = $Computer
$Details.Task = $task.name
$Details.User = $TaskInfo.task.principals.principal.userid
$Details.Enabled = $TaskInfo.task.settings.enabled
$Details.Application = $TaskInfo.task.actions.exec.command
$Details
$Report += $Details
}
}
else
{
#Computer is offline
}
}
$Report | ft
NOTE: If you have many servers, this will take a long time to run. If you'd like to run it in parallel, you can use the invoke-parallel script (Google it), which is significantly faster:
. \\server\path\to\invoke-parallel.ps1
$Computers = (get-adcomputer -filter {operatingsystem -like "*server*"}).name
$ErrorActionPreference = "SilentlyContinue"
$Scriptblock =
{
$path = "\\" + $_ + "\c$\Windows\System32\Tasks"
$tasks = Get-ChildItem -recurse -Path $path -File
foreach ($task in $tasks)
{
$Details = "" | select ComputerName, Task, User, Enabled, Application
$AbsolutePath = $task.directory.fullname + "\" + $task.Name
$TaskInfo = [xml](Get-Content $AbsolutePath)
$Details.ComputerName = $_
$Details.Task = $task.name
$Details.User = $TaskInfo.task.principals.principal.userid
$Details.Enabled = $TaskInfo.task.settings.enabled
$Details.Application = $TaskInfo.task.actions.exec.command
$Details
}
}
$Report = invoke-parallel -input $Computers -scriptblock $Scriptblock -throttle 400 -runspacetimeout 30 -nocloseontimeout
$Report | ft
Example output:
Solution 3
Watch out: caveat: better use -literalpath, like:
$TaskInfo = [xml](Get-Content -literalpath $AbsolutePath)
or get-content
will return nothing if your tasknames or foldernames happen to contain any wildcards or regex chars, like in my case "[' and "]"...
Yes, Powershell does seem to not only interpret literal strings, but also the contents of string type variables.
Solution 4
I used this command to list all tasks:
Invoke-Command -ComputerName "computername" -Credential Get-Credential {schtasks.exe}
Related videos on Youtube
Ob1lan
Updated on September 18, 2022Comments
-
Ob1lan over 1 year
I want to write a PowerShell script that lists all Scheduled Tasks on remote systems, and includes the user account which will be used to run each task.
The local system is running Windows 7, with PowerShell 3.0. The remote systems range from Server 2003 to 2008 R2, with PowerShell versions from 2.0 to 3.0.
What PowerShell commands or functions can I use for this task?
-
Ob1lan over 9 yearsThanks, but I already know this command. The problem is PowerShell remoting can't be performed on all the computers in the network due to our security policy. I'm on another script way more simple.
-
Tilo over 5 yearshere a PS version without read XML: stackoverflow.com/a/53123962/1747983
-
mwfearnley over 5 yearsNote: Patrick E has posted a comment (as an Answer: superuser.com/a/1368312/19792), suggesting to use
-LiteralPath
in theGet-Content
command. I can't comment on its validity though. -
mwfearnley over 5 yearsI'm presuming this was downvoted because it should be a comment on the above answer(s) that use just
Get-Content $AbsolutePath
? I'm not good with PowerShell, but I'd like to cancel the downvote for you, if you can give a citation for the above.