Hey, Scripting Guy! How Can I Create a Scheduled Task on a Number of Computers?

Hey, Scripting Guy! How Can I Create a Scheduled Task on a Number of Computers?

  • Comments 1
  • Likes

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have to be able to create a simple scheduled task via script on a number of computers. I do not need to do anything fancy, but it needs to be quick and easy to do. Do you have any ideas that can help me?

- JW

SpacerHey, Scripting Guy! Answer

Hi JW,

The word for the day is blue. We have a nice pot of green tea, and we crushed some organic blueberries and placed them in the tea. It adds a nice color and flavor to the tea. We are also listening to Avenue Blue. The sky is a deep blue, and the birds are singing. Somewhere in the distance, a wind chime is punctuating the breeze and adding a nice relaxed tempo to the world outside our office. In the midst of this tranquility, you would like to create a quick, scheduled job? No problem.

This week we are talking about scheduled tasks. There are at least three different ways that you can work with scheduled tasks. This “Hey, Scripting Guy!” article provides an example of working with scheduled tasks via WMI. This script lists scheduled tasks created via WMI. We also have a variety of scheduled task scripts from the Community-Submitted Scripts Center. The Schedule.Service API is discussed in two articles. The first article talks about creating a task and setting the trigger. The second article discusses organizing tasks. This article discusses executing scheduled tasks via the Schedule.Service API. And the task scheduler is documented on MSDN.

JW, we created the CreateScheduledTask.ps1 script for you to use to create quick, scheduled jobs. This script uses the WMI class Win32_ScheduledJob, which supports the older scheduled-job engine. This engine does not have the flexibility of the Schedule.Service API, but what it lacks in flexibility it makes up for in simplicity. The CreateScheduledTask.ps1 script is seen here:

param(
      $computer=$env:computername,
      $command,
      $startTime,
      [switch]$repeat,
      $daysOfWeek,
      $DaysOfMonth,
      [switch]$interactWithDesktop,
      [switch]$help
      ) #end param

function Help
{
 $descriptionText= `
@"
 NAME: CreateScheduledTask.ps1
 DESCRIPTION:
 Creates a scheduled job on a local or remote 
 machine. The scheduled job can run once, or
 be scheduled to run repeatedly on a particular
 day of the week, or on a particular day of the
 month. NOTE: requires administrator rights

 PARAMETERS: 
 -computer computer upon which to run the command
 -command the command to run
 -startTime the date and time to run the command
 -repeat when present, causes job to repeat
 -daysOfWeek the day of the week to run the job < 1-7>
 -daysOfMonth the day of the month to run the job < 1 - 31 >
 -interactWithDeskTop causes job to interact with desktop
 -help prints help description and parameters file
"@ #end descriptionText

$examplesText= `
@"

 SYNTAX:
 CreateScheduledTask.ps1 

 Displays an error missing parameter, and calls help

 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'

 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process does not interact with the desktop. 

 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -interactWithDesktop

 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process interacts with the desktop.

 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -repeat -daysOfWeek 4

 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process does not interact with the desktop. The job runs
 repeatedly every thursday at the same time.

 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -repeat -daysOfMonth 26

 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process does not interact with the desktop. The job runs
 repeatedly every month on the 26th at the same time.
"@
 $descriptionText ; $examplesText
} #end help

Function ScheduleJob()
{
 if($daysOfWeek) { $daysOfWeek = [math]::pow(2,$daysOfWeek -1) }
 if($daysOfMonth){ $daysOfMonth = [math]::pow(2,$daysOfMonth -1) }
 $sJob = [wmiclass]"\\$computer\root\cimv2:win32_scheduledjob"
 if($startTime) { $startTime = $sJob.ConvertFromDateTime($startTime) }
 if(!$startTime) { "you must supply a time for the job to run" ; funhelp }
 $errRTN = $sJob.Create($command,$startTime,$repeat,$daysOfWeek,$daysOfMonth,$interactWithDesktop)
 if($errRTN.returnValue -eq 0) 
   { 
    "$command successfully created as job $($errRTN.jobID) on $computer"
   } #end if
 ELSE
   {
     "Failed to create $command as a scheduled job on $computer. Error $($errRTN.returnvalue"
   }
} #end ScheduleJob
# *** Entry point to script ***
if($help)      { help; exit }
if($command)   { ScheduleJob }
if(!$command) { "Missing command ... " ; help; exit }

JW, do not let the length of the script scare you. It contains mostly text that is used for the Help strings. The first thing we need to do is to define some command-line parameters. This will allow us a lot of flexibility when running the script. To do this, we use the param statement. The param statement is followed by a set of parentheses; each of the parameters is assigned on its own line followed by a comma. Each parameter does not have to be on its own line; however, doing so makes it easier to read and to understand when a large number of parameters are involved. Three of the parameters are switched parameters. A switched parameter is a parameter that does not take effect unless it is present on the command line. We create a parameter by using the switch statement inside a pair of square brackets. The switch statement as used here is really a system value type. The param statement for our script is shown here:

param(
      $computer=$env:computername,
      $command,
      $startTime,
      [switch]$repeat,
      $daysOfWeek,
      $DaysOfMonth,
      [switch]$interactWithDesktop,
      [switch]$help
      ) #end param

Because we have a large number of command-line parameters, I consider it a best practice to include some Help for the script. To do this, we will create a help function to provide the Help for the script. This help function will only be called when the script is run with the -help argument from the command line. The help function displays text on the command line that consists of two rather large here-strings. To create a here-string, we begin with an ampersand and a double quotation mark. The here-string ends with a pair of double quotation marks followed by an ampersand. We then store the here-string in a variable. After we have created our two help-strings, we print both of them out and exit the help function. The $descriptionText here-string is shown here:

function Help
{
 $descriptionText= `
@"
 NAME: CreateScheduledTask.ps1
 DESCRIPTION
 Creates a scheduled job on a local or remote 
 machine. The scheduled job can run once, or
 be scheduled to run repeatedly on a particular
 day of the week, or on a particular day of the
 month. NOTE: requires administrator rights
 PARAMETERS: 
 -computer computer upon which to run the command
 -command the command to run
 -startTime the date and time to run the command
 -repeat when present, causes job to repeat
 -daysOfWeek the day of the week to run the job < 1-7>
 -daysOfMonth the day of the month to run the job < 1 - 31 >
 -interactWithDeskTop causes job to interact with desktop
 -help prints help description and parameters file
"@ #end descriptionText

We now create another here-string. Once again we begin the here-string with the ampersand and the double quotation mark. We store the here-string in a variable named $examplesText. This is seen here:

$examplesText= `
@"
 SYNTAX:
 CreateScheduledTask.ps1
 Displays an error missing parameter, and calls help
 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process does not interact with the desktop.
 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -interactWithDesktop
 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process interacts with the desktop
 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -repeat -daysOfWeek 4
 Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
 machine. The notepad process does not interact with the desktop. The job runs
 repeatedly every thursday at the same time.
 CreateScheduledTask.ps1  -command notepad -startTime '1/26/2008 6:18 pm'
 -repeat -daysOfMonth 26
Creates a scheduled job to run notepad at 6:18 pm Jan 26, 2008 on the local
machine. The notepad process does not interact with the desktop. The job runs
 repeatedly every month on the 26th at the same time.
"@

To print out the help strings, we only need to print the contents of the two variables. The first variable contains the description information. The semicolon is used to separate the two different commands that would normally appear on their own individual lines. This convention simply shows that we are printing both items out. It does not change the behavior of the script. The help function ends with the curly bracket. This is shown here:

$descriptionText ; $examplesText
} #end help

When the help function is called, either by running the script with the –help switch or by running the script with no switches at all, the Help information is displayed:

Image of the displayed Help information

 

After we have completed the help function, it is time to create the scheduled job function. This is the main function for our script. The scheduled job function begins with the function keyword and the name of the function. It then continues with an open curly bracket and an if statement. The if statement is used to evaluate the value that is contained in the $daysOfWeek variable. If a value for the $daysOfWeek variable is present when the script is launched, the if statement will be executed. The script block uses the static method pow. The pow method accepts two parameters. The first parameter is the number base and the second parameter is the number itself. An example of using this method is seen here:

PS C:\> [math]::pow(2,3)
8

The reason we need to use this method is because of the way the scheduled job method needs to accept the input value. Essentially what we are doing is taking a numeric value and converting it to base two. For more information, take a look at this article.

Function ScheduleJob()
{
 if($daysOfWeek) { $daysOfWeek = [math]::pow(2,$daysOfWeek -1) }

When a value for the $daysofweek variable is present, it means it was supplied on the command line. We use an if statement to detect its presence. If we find a value, the script block will be executed. The syntax is exactly the same used for the $daysofweek variable. This command is seen here:

if($daysOfMonth){ $daysOfMonth = [math]::pow(2,$daysOfMonth -1) }

We now need to create an instance of the WMI class Win32_scheduledJob. To do this we use the WMI class accelerator. We specify the computer name and the WMI namespace. The WMI object that is returned is stored in the variable $sjob. This syntax is shown here:

$sJob = [wmiclass]"\\$computer\root\cimv2:win32_scheduledjob"

We now need to create the date time value that is required by the scheduled job class. To do this we use the ConvertFromDateTime method. The ConvertFromDateTime method will accept a string that represents the date and the time that you wish the scheduled job to occur. It will then convert that string into the acceptable format for WMI. This command is shown here:

if($startTime) { $startTime = $sJob.ConvertFromDateTime($startTime) }

We are now going to check to see if a value has been supplied for the starting time of the scheduled job. If no value is present, we’ll print out a message and call the help function as seen here:

if(!$startTime) { "you must supply a time for the job to run" ; help }

It is time to call the create method of the scheduled job class and pass the parameters to it. The first parameter is the command we wish to execute. The command is contained in the $command variable. The second parameter is the starting time of the scheduled job; this value contains the converted value that is stored in the $startTime value. The third parameter, $repeat, tells the command whether or not we wish to repeat the job. The $daysOfWeek variable contains the days of the week on which we wish the job to occur. This value only takes effect if the repeat flag has been selected. The $daysOfMonth value is used to tell the job on which days of the month we wish for it to occur. Once again this value only takes effect if the repeat flag has been used. The $interactWithDeskTop variable is a Boolean value that tells the job engine whether or not to allow access to the desktop. If this value is not used, a program such as Notepad.exe will not be visible when launched by the scheduled task. WMI will return a value that tells us if the command was successful. The value of zero means there were no errors; any other value would indicate an error during the method call. The method call is seen here:

$errRTN = $sJob.Create($command,$startTime,$repeat,$daysOfWeek,$daysOfMonth,$interactWithDesktop)

We want to evaluate the return code. To do this we look at the return value that is stored in the $errRTN variable. If the return code is 0, it means the command completed successfully. This is shown here:

if($errRTN.returnValue -eq 0)
   {
    "$command successfully created as job $($errRTN.jobID) on $computer"
   } #end if

On the other hand, any number other than 0 indicates that an error occurred. We therefore print out a message that the command failed, as well as the error number itself. This is seen here:

ELSE
   {
     "Failed to create $command as a scheduled job on $computer. Error $($errRTN.returnvalue"
   }
} #end ScheduleJob

 

When the script is run and it is successful, we are presented with the confirmation seen here:

Image of the confirmation that the script ran successfully

 

One of the things we do not like about the script is the number of command-line parameters that are required to create a scheduled job. There are two things that can be done; the first is to use partial parameters when calling the script. As seen in the following image, the first attempt failed because –c is ambiguous. It could mean computer or command. The second attempt works:

Image of the failed first attempt

 

The second thing that could be done is to add the values for the command line as variables in the script. You would do this just before the entry point to the script.

$command = "Notepad"
$startTime = "3/30/2009 11:50 am"
$interactWithDeskTop = $true

In this manner, you would be able to run the script directly without needing to type any information on the command line. The entry point of the script is used to check the command-line parameters. If the script was run with the –help parameter, the help function will be called and the script will exit. If the script was run and no value was given to the script for the –command parameter and the –help parameter was not supplied, the script displays a message that states that it is missing a command and it calls the help function and then exits. If the –help parameter is not supplied and if the command parameter was supplied, the scheduled job function is called. This is seen here:

if($help)      { help; exit }
if($command)   { ScheduleJob }
if(!$command) { "Missing command ... " ; help; exit }

When the script has run, you can use the At command to see the status of the scheduled jobs. This is seen here:

Image of the At command used to see the status of scheduled jobs

 

We have come to the end of our discussion of using the legacy task scheduler via Windows PowerShell. As you can see, it is easy to use but is somewhat limited in its options. Join us tomorrow as we continue our series of articles about scheduling tasks. Until tomorrow, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Nice to meet you while you were here in Atlanta.

    There seems to be times and reasons why running a powershell command or script on a remote computer as a scheduled job is a neccesity. I have been trying to come up with a way to do this on all my servers, most are powershell v2.  I want it to run right away, here I am tying to have it start in 1 minute.  

    This is what I have come up with so far, but it doesn't work yet.  

    # getservices.ps1 contents:Get-service > C:\services.txt

    $computer = "$env:computername"

    $command = "powershell.exe -executionpolicy unrestricted -noprofile -file C:\getservices.ps1"

    $startTime = (Get-Date).AddMinutes(1)

    $sJob = [wmiclass]"\\$computer\root\cimv2:win32_scheduledjob"

    $startTime = $sJob.ConvertFromDateTime($startTime)

    $errRTN = $sJob.Create($command,$startTime)