1

I've inherited a server that runs Windows scheduled tasks for several hundred various processes (mostly it kicks off custom PowerShell scripts) - the schedules for these tasks can be as frequently as every 15 minutes, or as infrequently as once per year on a specific date.

How can I query task scheduler using PowerShell (or really any other means also) to determine what if any tasks will be running between a specific date-time range in the future? This is necessary so we can for example schedule and perform maintenance on this server, and others that it interacts with.

halfer
  • 19,824
  • 17
  • 99
  • 186
  • Welcome to stack**overflow**. For a question about the task schedule in MS Windows, I don't think tagging [tag:task] or [tag:schedule] adds value to tagging [tag:taskscheduler]. – greybeard Dec 31 '20 at 16:44
  • Here it is in wmi: `get-ciminstance -ClassName MSFT_ScheduledTask -Namespace ROOT\Microsoft\Windows\TaskScheduler` – js2010 Dec 31 '20 at 21:11

2 Answers2

1

Here is a script to get the next running jobs

Get-ScheduledTask |
   Foreach-object { Get-ScheduledTaskInfo $_} |
   where-object {!($_.NextRunTime -eq $null)} |
   Select-object Taskname, NextRunTime |
   Sort-object -property NextRunTime
Popofe
  • 11
  • 1
  • The next runtime wouldn't predict what's running at a given future time. The duration of the tasks isn't accounted for, nor is the repetition or other triggers etc... – Steven Dec 31 '20 at 19:09
1

I should start out by mentioning this is a deceptively complex topic. There are few reasons for this, including but not limited to:

  1. Scheduled Tasks as objects can be very complex with widely variable schedules, repetition intervals, other trigger types, and a plethora of other configuration points.
  2. The non-GUI, meaning CLI, cmdlet, and/or API tools have changed over the years. And, it's not always easy to string them together to solve the complexities cited in point 1.

WARNING: This report is going to be very tough to write. Before you embark on a challenge like this you should look for any preexisting tools that may inventory or lend other transparency to the Task Scheduler.

Also, it may be better to define the maintenance window as a matter of policy. Then you can configure jobs to avoid the window. At a glance, this strikes me as much easier than writing a difficult program to assist in the cherry-picking of a time slot.

If you choose to move forward here are a few tools that may help:

  1. Schtasks.exe: A surprisingly capable CLI tool that's been around a long time.
  2. The TaskScheduler PowerShell module: A PowerShell module written around scheduling COM objects. It was originally part of the Windows 7 PowerShell pack, but these days you can get it through the PowerShell Gallery. The easiest way is through typical module cmdlets like Find-Module & Install-Module
  3. The newer ScheduledTasks module installed by default on later Windows systems. It's Written around Cim (WMI) and is installed by default in later versions of PowerShell / Windows.

CAUTION: It's easy to confuse the latter 2 tools. Not only are the names are very similar, but there are overlap and similarity between the commands they contain. For example, both modules have a Get-Scheduledtask cmdlet. Two ways to deal with the confusion:

  1. When importing a Module with the Import-Module cmdlet use the -Prefix parameter to add a prefix to the none part of the cmdlet. then use that prefix when calling the cmdlet thereafter.
  2. Call cmdlets with a qualified name like TaskScheduler\GetScheduledTasks

Now to get at the scheduling data. In my experience, the needed details are only exposed through the task's XML definition. Again, you're going to bump up against the complexity that comes with a wide range of scheduling and/or trigger options. You can read about the XML schema here

Here are some examples of how to access the XML data:

schtasks.exe /query /s pyexadm1 /tn <TaskName> /XML

In this case, you'll have to do additional string manipulation to isolate the XML then cast it to [XML] so you can work with it in a typical PowerShell manner. Obviously, there will be challenges to leveraging this tool more broadly. However, it's very handy to know for quick checks and work, especially where the next tool is not immediately available.

Note: if you don't cite the /TN argument all tasks will be returned. While the next method is easier, it's good to know this approach, it will be handy while you are developing.

The next example uses the older TaskScheduler module (#2 above):

$TaskXML = [XML](TaskScheduler\Get-ScheduledTask -ComputerName <ComputerName>-Name <TaskName>).XML

Note: Above assumes no prefix was used. So, you must cite the source module to prevent confusion with the ScheduledTask module.

This example loads the XML text and converts it to an XmlDocument object in a single line. Then you can access data about the task like below:

$TaskXML.Task.Triggers.CalendarTrigger

This may yield output like:

StartBoundary       Enabled ScheduleByWeek
-------------       ------- --------------
2020-09-14T08:00:00 true    ScheduleByWeek

You can run this in mass by leveraging the pipeline, which might look something like below:

$XMLTaskData = 
TaskScheduler\Get-ScheduledTask -ComputerName <ComputerName> -Recurse | 
ForEach-Object{ [XML]$_.XML }

In the above pipeline example the resulting $XMLTaskData is an array each element of which is a respective XML task definition.

Note: Use of the -Recurse switch parameter. Given the high number of tasks, I wouldn't be surprised if they were organized into subfolders.

Similarly, you can also use the Export-ScheduledTask cmdlet from the ScheduledTasks module:

$TaskXML = [XML](Export-ScheduledTask -CimSession <ComputerName> -TaskName <TaskName>)

And you can leverage the pipeline like this:

$XMLTaskData = 
Get-ScheduledTask -CimSession <ComputerName> | 
Export-ScheduledTask | 
ForEach-Object{ [XML]$_ }

Like the other piped example, this results in an array of XML task definitions.

Note: In this case, there is no -Recurse parameter. You can specifically cite paths though.

With any of these approaches you obviously need some familiarity with working with XML objects in PowerShell, but there are tons of tutorials or other resources for that.

Again, the complexity here is in dealing with many trigger types and scheduling paradigms. On your road to getting a Minimally Viable Program (MVP), you may want to use these techniques to inventory the existing tasks. That can help you prioritize your development process.

A final point; knowing when a task is going to run may be quite different than know when it's running. For example, a task may run a 1:00 PM, but the duration of the job is variable and unaccounted for. That strikes me as very difficult to contend with. You may need another procedure to look for task completion events in the event logs. You may also need to consider execution time limits which can be found in the XML data.

halfer
  • 19,824
  • 17
  • 99
  • 186
Steven
  • 6,817
  • 1
  • 14
  • 14
  • I will dig in with Schtasks.exe; Thus far i've only been experimenting with the module (only knew of this one). you'll be happen to know i've already instituted a blackout window, but that doesnt help me with the existing 10~15 years worth of processes sadly, and we're not able to just go reschedule all of them due to a variety of constraints unfortunately. This just seems like it would be a common problem; I supposed that i am surprised there is not a more native solution for this. – mrfennecfox Dec 31 '20 at 19:22
  • Agree, it's def not always easy to unravel even after a policy is defined. That there is no native solution doesn't surprise me. MS does this a lot, they give just enough and let 3rd parties (or other MS products) fill in the gaps. Actually given the number of jobs you may want to look into that. There are probably dozens of vendors in the scheduling & orchestration space.MS System Center Orchestrator is an example. A company called Advanced System Concepts has a product called Active Batch I worked with a long time ago. – Steven Dec 31 '20 at 19:39
  • Added some pipeline examples. If you think this is a helpful answer, I'd appreciate it if you can accept it by clicking the check to the left of the question. Let me know if you have any questions. – Steven Dec 31 '20 at 20:17