{"id":54063,"date":"2009-04-01T23:09:00","date_gmt":"2009-04-01T23:09:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/04\/01\/hey-scripting-guy-how-can-i-best-work-with-task-scheduler\/"},"modified":"2009-04-01T23:09:00","modified_gmt":"2009-04-01T23:09:00","slug":"hey-scripting-guy-how-can-i-best-work-with-task-scheduler","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-best-work-with-task-scheduler\/","title":{"rendered":"Hey, Scripting Guy! How Can I Best Work with Task Scheduler?"},"content":{"rendered":"<p><H2><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"> <\/H2>\n<P>Hey, Scripting Guy! We are really looking at reducing our IT costs as a group and as a result are seeking out anything that is already built into the operating system that will allow us to automate things. Our company recently had some cutbacks and we simply do not have enough people to be able to do anything more than operate in fire-fighting mode. Task Scheduler in Windows Vista and on Windows Server 2008 looks really powerful, and I have been experimenting with it, but I will need a way to automate it if it is to be of much value. After all, we have thousands of servers and workstations. I looked at the WMI script you wrote yesterday, but I do not see half of the things that I can do with the graphical version of the Task Scheduler.<BR><BR>&#8211; KS<\/P><IMG height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\"><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\"> \n<P>Hi KS,<\/P>\n<P><A href=\"http:\/\/en.wikipedia.org\/wiki\/Don%27t_Worry%2C_Be_Happy\" target=\"_blank\">Don&#8217;t worry, be happy<\/A>. Or as former president of the United States Bill Clinton once said, &#8220;I feel your pain.&#8221; We were captivated by your question. So much so, in fact, that if you have been <A href=\"http:\/\/www.twitter.com\/scriptingguys\/\" target=\"_blank\">following us on Twitter<\/A>, you would have seen that we have been working on today&#8217;s article for more than three days. (There are actually several reasons for this, not the least of which was Ed\u2019s hand surgery. Not a problem because Windows 7 with Office 2007 has <I>awesome<\/I> voice recognition capabilities. In fact we were writing this article using voice recognition until we came down with tonsillitis! Needless to say, it has been a rough week.) Much of the time has been spent poring over the Task Scheduler API documentation <A href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa383614.aspx\" target=\"_blank\">on MSDN<\/A>. Then we got engrossed with playing around with it, and started coming up with way more ideas than we could possibly do in just a couple of days worth of articles. We will probably write a magazine article for <I>TechNet Magazine<\/I>, so we can go into it much more deeply. Let us tell you one thing now that is so cool. We get questions all the time from people who want to run a script when the computer is idle. Guess what? We always say you cannot do that. We lied inadvertantly. You can, using the Task Scheduler in Windows Vista and later! We tested it out, and it really works! This is just one really cool thing you can do.<\/P>\n<TABLE class=\"dataTable\" id=\"EJD\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD class=\"\">\n<P class=\"lastInCell\">This week we are talking about scheduled tasks. There are at least three different ways that you can work with scheduled tasks. <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/sept04\/hey0922.mspx\" target=\"_blank\">This \u201cHey, Scripting Guy!\u201d article<\/A> provides an example of working with scheduled tasks via WMI. <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/msh\/os\/tasks\/ostkms04.mspx\" target=\"_blank\">This script<\/A> lists scheduled tasks created via WMI. We also have <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/csc\/scripts\/os\/tasks\/index.mspx\" target=\"_blank\">a variety of scheduled task scripts<\/A> from the Community-Submitted Scripts Center. The <B>Schedule.Service<\/B> API is discussed in two articles. The <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/topics\/vista\/tasks1.mspx\" target=\"_blank\">first article<\/A> talks about creating a task and setting the trigger. The <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/topics\/vista\/tasks2.mspx\" target=\"_blank\">second article<\/A> discusses organizing tasks. <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/aug07\/hey0830.mspx\" target=\"_blank\">This article<\/A> discusses executing scheduled tasks via the <B>Schedule.Service<\/B> API. And the Task Scheduler is documented <A href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa383614.aspx\" target=\"_blank\">on MSDN<\/A>.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>KS, we decided to write a script called <B>ScheduledTaskFolders.ps1<\/B>. This script does nothing by default, but it is really powerful. We have four separate command lines, each of which is commented out at the bottom of the script. It will list task folders, do a recursive listing of task folders, create a new task folder, or delete a task folder. So this is actually four different scripts in one. It can probably do even more things than that, and in fact as you will see tomorrow, it can. Without further ado, here is the <B>ScheduledTaskFolders.ps1<\/B> script.<\/P><PRE class=\"codeSample\">Function Get-ScheduleService\n{\n  New-Object -ComObject schedule.service\n} #end Get-ScheduleService<\/p>\n<p>Function New-TaskObject($path)\n{ #returns a taskfolder object\n $taskObject = Get-ScheduleService\n $taskObject.Connect()\n if(-not $path) { $path = &#8220;\\&#8221; }\n $taskObject.GetFolder($path)\n} #end New-TaskObject<\/p>\n<p>Function Get-TaskFolder($folder,[switch]$recurse)\n{ #returns a string representing the path to a task folder\n  if($recurse)\n    {\n     $colFolders = $folder.GetFolders(0)\n      foreach($i in $colFolders)\n        {\n          $i.path\n          $subFolder = (New-taskObject -path $i.path)\n           Get-taskFolder -folder $subFolder -recurse\n        }\n    } #end if\n  ELSE\n    {\n     $folder.GetFolders(0) |\n      foreach-object { $_.path }\n     } #end else\n} #end Get-TaskFolder<\/p>\n<p>Function New-TaskFolder($folder,$path)\n{ \n $folder.createFolder($path)\n} #end New-TaskFolder<\/p>\n<p>Function Remove-TaskFolder($folder,$path)\n{\n $folder.DeleteFolder($path,$null)\n} #end Remove-TaskFolder<\/p>\n<p># *** Entry point to the script ***<\/p>\n<p># Get-TaskFolder -folder (New-taskObject -path &#8220;\\microsoft&#8221;)\n# Get-TaskFolder -folder (New-taskObject -path &#8220;\\microsoft&#8221;) -recurse \n# New-TaskFolder -folder (New-taskObject) -path &#8220;\\mred&#8221;\n# Remove-TaskFolder -folder (New-taskObject) -path &#8220;\\mred&#8221;\n<\/PRE>\n<P>We first need to create the <B>Get-ScheduleService<\/B> function. To do this we will use the <B>Function<\/B> keyword and give it the name of <B>Get-ScheduleService<\/B>. We open a pair of curly brackets and use the <B>New-Object<\/B> cmdlet with the <B>&#8211;<\/B><B>C<\/B><B>om<\/B><B>O<\/B><B>bject<\/B> parameter to allow us to create the <B>Schedule.Service<\/B> object. We then close the curly brackets. The <B>Schedule.Service<\/B> object is the main entry point for the Task Scheduler API. We will need to use this object for anything that we want to do with the scheduled tasks. This code is seen&nbsp;here:<\/P><PRE class=\"codeSample\">Function Get-ScheduleService\n{\n  New-Object -ComObject schedule.service\n} #end Get-ScheduleService\n<\/PRE>\n<P>Next we come to the <B>New-TaskObject<\/B> function. It returns a <B>TaskFolder<\/B> object. We use a <B>TaskFolder<\/B> object to allow us to work with folders in scheduled tasks. Scheduled task folders can be a useful way to allow you to organize your scheduled tasks. For example, if you had a number of maintenance-related activities that you would like to run on a scheduled basis, you could create a folder called \u201cMaintenance Tasks.\u201d While this is not required, it certainly makes things easier to find. To create the <B>New-TaskObject<\/B> function, we use the <B>Function<\/B> key word, assign a name, and create a variable to hold the path to the <B>TaskFolder<\/B> object that is returned. We then open a set of curly brackets. Inside the curly brackets, we call the <B>Get-ScheduleService<\/B> function and assign the object that is returned by that function to the <B>$taskObject<\/B> variable. We then use the <B>$task<\/B><B>Ob<\/B><B>ject<\/B> variable, which contains an instance of the <B>schedule service<\/B> object, and we call the <B>connect<\/B> method. This code is shown&nbsp;here:<\/P><PRE class=\"codeSample\">Function New-TaskObject($path)\n{ \n$taskObject = Get-ScheduleService\n$taskObject.Connect()\n<\/PRE>\n<P>Now we need to determine if a path was supplied to the function. If a path was not supplied to the function, we supply <B>\u201c\\\u201d<\/B> to the <B>$path<\/B> variable. The <B>\u201c\\\u201d<\/B> value is used to represent the root of the folder. We use the <B>-not<\/B> operator to see if a value was indeed supplied. After we have a value for the <B>$path<\/B> variable, we then use the <B>GetFolder<\/B> method from the <B>TaskObject<\/B>. This is seen here:<\/P><PRE class=\"codeSample\">if(-not $path) { $path = &#8220;\\&#8221; }\n$taskObject.GetFolder($path)\n} #end New-TaskObject\n<\/PRE>\n<P>It is time to create the <B>Get-TaskFolder<\/B> function. It returns a string representing the path to a task folder. The <B>Get-TaskFolder<\/B> function will accept two parameters. The first parameter is the <B>folder<\/B> parameter and it needs a <B>folder<\/B> object. The second parameter is the <B>recurse<\/B> parameter and it is switched. When the function is run with the <B>recurse<\/B> parameter, it will list the path to all of the folders. The F<B>unction<\/B> keyword function name and parameters are seen&nbsp; here:<\/P><PRE class=\"codeSample\">Function Get-TaskFolder($folder,[switch]$recurse)\n{\n<\/PRE>\n<P>Because there can be several nested folders in our task schedule hierarchy, we need the ability to recurse. A typical task folder hierarchy is shown here:<\/P><IMG height=\"356\" alt=\"Image of a typical task hierarchy\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/april\/hey0401\/hsg-04-01-09-1.jpg\" width=\"500\" border=\"0\"> \n<P>&nbsp;<\/P>\n<P>If the function is called with the recurse parameter, we use the <B>GetFolders<\/B> method from the <B>folder<\/B> object. We store the returned object in the <B>$colfolders<\/B> variable:<\/P><PRE class=\"codeSample\">if($recurse)\n    {\n     $colFolders = $folder.GetFolders(0)\n<\/PRE>\n<P>Because the <B>GetFolders<\/B> method can return a collection of folders, we need to be able to walk through these folders. In order to do this, we use the <B>ForEach<\/B> statement. Inside the <B>ForEach<\/B> statement, we use the <B>$i<\/B> variable as the enumerator, which allows us to work with an individual folder from within the collection. We then return the path from the folder. This is seen&nbsp;here:<\/P><PRE class=\"codeSample\">ForEach($i in $colFolders)\n        {\n          $i.path\n<\/PRE>\n<P>Now we get to the tricky part. Because it is possible that there is more than one folder and more than one subfolder, we will need the ability to recurse. To do this we first create a NewFolder object that is based upon the path from the folder that is stored in the <B>$i<\/B> variable. We do this by calling the <B>New-taskObject<\/B> function and storing the results and the <B>$subfolder<\/B> variable. We can call the <B>Get\u2013taskFolder<\/B> function and pass the <B>$subfolder<\/B> variable to it with the <B>-recurse<\/B> parameter. This is seenhere:<\/P><PRE class=\"codeSample\">$subFolder = (New-taskObject -path $i.path)\n           Get-taskFolder -folder $subFolder -recurse\n        }\n    } #end if\n<\/PRE>\n<P>If the function is not run with the <B>\u2013recurse<\/B> parameter, we need to take alternate action. To take alternate action, we use the <B>ELSE<\/B> statement. Inside the curly brackets, we first call the <B>GetFolders<\/B> method from the <B>$folder<\/B> variable. We pipeline the results to the <B>ForEach-Object<\/B> cmdlet and inside the curly brackets we return the path:<\/P><PRE class=\"codeSample\">ELSE\n    {\n     $folder.GetFolders(0) |\n      ForEach-Object { $_.path }\n     } #end else\n} #end Get-TaskFolder\n<\/PRE>\n<P>We now need to create the <B>New-TaskFolder<\/B> function. After using the <B>Function<\/B> keyword and assigning a name, we define two input parameters. The first contains a <B>folder<\/B> object and the second contains the path to the new folder. Inside the curly brackets we call the <B>createFolder<\/B> method from the <B>$folder<\/B> variable and pass it the path. We then close the curly brackets and the function is done. This is shown&nbsp;here:<\/P><PRE class=\"codeSample\">Function New-TaskFolder($folder,$path)\n{\n $folder.createFolder($path)\n} #end New-TaskFolder\n<\/PRE>\n<P>When a new task folder is created, it will appear as seen here:<\/P><IMG height=\"160\" alt=\"Image of a new task folder\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/april\/hey0401\/hsg-04-01-09-2.jpg\" width=\"500\" border=\"0\"> \n<P>&nbsp;<\/P>\n<P>It is time to create the <B>Remove-TaskFolder<\/B> function. We once again use the <B>Function<\/B> keyword to assign a name to the function and define our input parameters. This function also uses the <B>$folder<\/B> variable and the <B>$path<\/B> variable as input parameters. Inside the curly brackets of the function, we call the <B>DeleteFolder<\/B> method from the <B>folder<\/B> object and pass it the path to the folder that will be removed. This is seen here:<\/P><PRE class=\"codeSample\">Function Remove-TaskFolder($folder,$path)\n{\n$folder.DeleteFolder($path,$null)\n} #end Remove-TaskFolder\n<\/PRE>\n<P>We have listed four possible command lines you may wish to use in the entry point to the script section. The first one lists task folders in a specific location. The second one does the same thing but in a recursive fashion. The third command line creates a new task folder, and the last one removes it. These four command lines are seen here:<\/P><PRE class=\"codeSample\"># Get-TaskFolder -folder (New-taskObject -path &#8220;\\microsoft&#8221;)\n# Get-TaskFolder -folder (New-taskObject -path &#8220;\\microsoft&#8221;) -recurse \n# New-TaskFolder -folder (New-taskObject) -path &#8220;\\mred&#8221;\n# Remove-TaskFolder -folder (New-taskObject) -path &#8220;\\mred&#8221;\n<\/PRE>\n<P>You may be wondering why we wrote this script in this manner. If you look at the syntax of the above commands, you will see that they kind of look like Windows PowerShell commands. This was done intentionally. Because there is no directly executable line of code in the script, you may wonder how it executes. If you wish, you can simply uncomment the line of code you wish to use and run it that way. Or you could dot source the script into your Windows PowerShell console and use the functions as you would any cmdlet. When Windows PowerShell 2.0 is released, you could turn the script into a module. We will talk about this when Windows PowerShell 2.0 is shipped.<\/P>\n<P>Well, KS, I hope you enjoyed today&#8217;s script and are as excited as I am about the many possibilities for the new Task Scheduler. Join us tomorrow when we create scheduled tasks with the Task Scheduler 2.0 API (that is what we were looking at today by the way). Until tomorrow be careful, and watch out for splinters.<\/P>\n<P>&nbsp;<\/P>\n<P><B>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/B><\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! We are really looking at reducing our IT costs as a group and as a result are seeking out anything that is already built into the operating system that will allow us to automate things. Our company recently had some cutbacks and we simply do not have enough people to be able [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[31,32,3,45],"class_list":["post-54063","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-operating-system","tag-scheduled-tasks","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! We are really looking at reducing our IT costs as a group and as a result are seeking out anything that is already built into the operating system that will allow us to automate things. Our company recently had some cutbacks and we simply do not have enough people to be able [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54063","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54063"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54063\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}