{"id":4317,"date":"2013-01-16T00:01:00","date_gmt":"2013-01-16T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/01\/16\/powershell-workflows-job-engine\/"},"modified":"2013-01-16T00:01:00","modified_gmt":"2013-01-16T00:01:00","slug":"powershell-workflows-job-engine","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-workflows-job-engine\/","title":{"rendered":"PowerShell Workflows: Job Engine"},"content":{"rendered":"<p><strong style=\"font-size: 12px\">Summary:<\/strong><span style=\"font-size: 12px\"> Windows PowerShell MVP Richard Siddaway continues his workflow series by talking about the job engine.<\/span>\nMicrosoft Scripting Guy, Ed Wilson, is here. Today, we have the fourth in a series of guest blog posts written by Windows PowerShell MVP and <a href=\"http:\/\/blogs.technet.comhttps:\/\/devblogs.microsoft.com\/scripting\/announcing-the-2012-honorary-scripting-guys\/\" target=\"_blank\">Honorary Scripting Guy<\/a> Richard Siddaway dealing with Windows PowerShell workflow.<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong> &nbsp;&nbsp;The first article, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/12\/26\/powershell-workflows-the-basics.aspx\" target=\"_blank\">PowerShell Workflows: The Basics<\/a><em>, <\/em>introduced the basic concepts of Windows PowerShell workflow. The second article, <a href=\"http:\/\/blogs.technet.comhttps:\/\/devblogs.microsoft.com\/scripting\/powershell-workflows-restrictions\/\" target=\"_blank\">PowerShell Workflows: Restrictions<\/a><em>, <\/em>discussed the restrictions encountered with working with Windows PowerShell workflows. The third article, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2013\/01\/09\/powershell-workflows-nesting.aspx\" target=\"_blank\">PowerShell Workflows: Nesting<\/a>, talks about nesting workflows. You should read those articles prior to reading today&rsquo;s article.\nRichard has written a <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/richard+siddaway\/\" target=\"_blank\">number of guest Hey, Scripting Guy! Blog posts<\/a>, and he has also written two books on Windows PowerShell. His most recent book, <a href=\"http:\/\/www.manning.com\/jones2\/\" target=\"_blank\">PowerShell in Depth<\/a>, is co-written with fellow MVPs Don Jones and Jeffrey Hicks.\nNow, take it away, Richard &hellip;\nRemember the first workflow you saw in this series&mdash;it was a simple &ldquo;Hello World.&rdquo;<\/p>\n<p style=\"padding-left: 30px\">workflow hello {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&#8220;Hello World&#8221;<\/p>\n<p style=\"padding-left: 30px\">}\nYou could run it like this:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; hello<\/p>\n<p style=\"padding-left: 30px\">Hello World<\/p>\n<h2>Running a workflow as a job<\/h2>\n<p>All workflows have the ability to run as a Windows PowerShell Job:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; hello -AsJob -JobName w1<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp; &nbsp;Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; NotStarted&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost\nYou get the <strong>&ndash;AsJob<\/strong> parameter, which starts the workflow as a Job, and you also get the <strong>&ndash;JobName<\/strong> parameter, which allows you to give your own name to workflow&rsquo;s Job. Contrast that with other situations where you only get the ability to run a cmdlet as a Job, for example, with the WMI cmdlets you don&rsquo;t get the ability to give the Job a name.\nThe other important point to note is the <strong>PSJobTypeName<\/strong>&mdash;it&rsquo;s <strong>PSWorkflowJob<\/strong>. This is a new Job category introduced in Windows PowerShell 3.0, just for workflows.\nAfter you&rsquo;ve started your workflow as a Job, it is handled as any other Job:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; w1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Completed&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Receive-Job -Name w1<\/p>\n<p style=\"padding-left: 30px\">Hello World\n&nbsp;\nBy definition, workflows are long-running tasks for which you don&rsquo;t expect to provide interactive input. They are ideally suited for running as Windows PowerShell Jobs.\nThere&rsquo;s a bit more to workflows and the way they work with the Windows PowerShell Job engine, which is just as well as otherwise I wouldn&rsquo;t have enough to write about.<\/p>\n<h2>Stopping and starting workflows<\/h2>\n<p>Windows PowerShell workflows are built on top of the Windows PowerShell Job engine. One of the capabilities this gives you is the ability to suspend and resume the workflow. Let&rsquo;s simulate a long-running workflow:<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfsuspension {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Start-Sleep -seconds 10<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Suspend-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-ChildItem<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThe workflow will sleep for 10 seconds and then suspend itself through the call to <strong>Suspend-Workflow<\/strong>.\nOnce the workflow has suspended, you will see a display similar to this:<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp; &nbsp;&nbsp;&#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\n&nbsp;\nNotice that the State is set to Suspended. The data and state of the workflow have been saved to disk.\nYou can restart the Job by using the <strong>Resume-Job<\/strong> cmdlet:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Resume-Job -Id 7<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost\n&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Completed&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nThe data returned from the <strong>Resume-Job<\/strong> cmdlet may mislead you. It states that the job is <em>Suspended<\/em>! The cmdlet reports the state of the Job <em>before<\/em> it tells it to restart. After it is restarted, the Job completes and the data is available as usual.\nThis is useful but what is more useful is the ability to suspend a workflow Job, and then restart it in a different Windows PowerShell session! If you remove the old jobs and rerun the <strong>test-wfsuspension<\/strong> workflow, you&rsquo;ll get something like this:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; test-wfsuspension<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nI&rsquo;m running these demonstrations in ISE, but this still works if you are using the Windows PowerShell console.\n<em>Shut down your PowerShell session and you don&rsquo;t need to save any files<\/em>. That&rsquo;s right. Just click the little close button at the top-right corner of the Windows PowerShell or ISE window. This is safe to try at home&mdash;honest.\nNow open up a new session (you need to run it with elevated privileges). I&rsquo;m going to use ISE again, but feel free to use the console if you want to.\nNow try <strong>Get-Job<\/strong>, and you&rsquo;ll see &#8230; nothing!\nOK, don&rsquo;t panic. Import the Windows PowerShell workflow module again, and you&rsquo;ll see your job:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Import-Module PSWorkflow<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nYou can then resume the job and retrieve the data when appropriate:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Import-Module PSWorkflow<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nNotice that the job ID may change between sessions although the job name will remain constant.<\/p>\n<h2>Suspending a workflow job<\/h2>\n<p>There is another way to suspend workflow jobs&mdash;that&rsquo;s by using the <strong>Suspend-Job<\/strong> cmdlet. Start with a workflow:<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfsr {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Start-Sleep -seconds 30<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Checkpoint-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-ChildItem&nbsp;<\/p>\n<p style=\"padding-left: 30px\">}\nStart the workflow as a Job:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; test-wfsr -AsJob<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">11&nbsp;&nbsp;&nbsp;&nbsp; Job11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Suspend-Job -Id 11<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">11&nbsp;&nbsp;&nbsp;&nbsp; Job11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspending&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nYou can then use <strong>Suspend-Job<\/strong> to suspend the job. The job will show a State of Suspending until the Job reaches the <strong>CheckPoint-Workflow<\/strong> statement. At this point, the Job enters a Suspended state:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">11&nbsp;&nbsp;&nbsp;&nbsp; Job11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp; &nbsp;True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\nYou can then resume the job when appropriate:<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Resume-Job -id 11<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">11&nbsp;&nbsp;&nbsp;&nbsp; Job11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost\n&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">11&nbsp;&nbsp;&nbsp;&nbsp; Job11&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Completed&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost\n&nbsp;\nIn this case, I&rsquo;ve resumed the Job in the same session.\nA very important point to remember is that the <strong>Suspend-Job<\/strong> and <strong>Resume-Job<\/strong> cmdlets only work on workflow jobs. You will get an error if you try to suspend other job types!\nIn the last example, you need to use the <strong>Checkpoint-Workflow<\/strong> activity. This writes a copy of the workflows data and state to disk so that the job can be resumed. In other words, it takes a snapshot. If you don&rsquo;t take that snapshot, the workflow has nowhere to suspend and it will effectively ignore the suspension.\nHere is a summary of the two suspension techniques:<\/p>\n<ul>\n<li>If you want the suspension to be triggered from inside the workflow, use <strong>Suspend-Workflow<\/strong>.<\/li>\n<li>If the suspension is to be triggered from outside the workflow, use <strong>Checkpoint-Workflow<\/strong> and <strong>Suspend-Job<\/strong>.<\/li>\n<\/ul>\n<h2>Checkpoints<\/h2>\n<p>Checkpoints are a good technique for saving the current state of your workflow so that you can restart it if the machine or session is terminated. You can only restart from the last checkpoint taken&mdash;you can&rsquo;t choose which checkpoint to use.\nCheckpoint data is saved in your user profile on the system you are using to run the workflow. You could add a checkpoint after every activity but there is a balance between the time needed to write the data and state to disk compared to the time needed to rerun the workflow.\nYou have seen <strong>Checkpoint-Workflow<\/strong> being used&mdash;there are a number of other ways of adding checkpoints to your workflows:<\/p>\n<ul>\n<li><strong>Checkpoint-Workflow<\/strong> &ndash; Can be used after any activity, but not inside an InlineScript block. It takes an immediate checkpoint.<\/li>\n<li><strong>PSPersist <\/strong><em>workflow<\/em> parameter &ndash; Adds checkpoints at the beginning and at end of the workflow and after each activity. Does not modify any explicit checkpoints in the workflow.<\/li>\n<li><strong>PSPersist <\/strong><em>activity<\/em> parameter &ndash; Takes a checkpoint after the activity completes. This is not valid on expressions or commands in an InlineScript block.<\/li>\n<li><strong>$PSPersistPreference<\/strong> preference variable &ndash; When set to true, takes a checkpoint after every following activity until it&rsquo;s reset to false. Only effective within workflows.<\/li>\n<\/ul>\n<p>If your activity is in a pipeline and it&rsquo;s checkpointed, the checkpoint doesn&rsquo;t apply until the pipeline completes. Within parallel blocks, the checkpoint doesn&rsquo;t apply until the parallel processing has been applied to all items compared to a sequence block when checkpoints are applied after each activity.\nThese rules are best illustrated with some code. By using the <strong>Checkpoint-Workflow<\/strong> activity you&rsquo;ve seen before, you can checkpoint after every activity like this:<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfchkpnt {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_ComputerSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Checkpoint-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_OperatingSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Checkpoint-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_LogicalDisk<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Checkpoint-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nIn this example, the same result of checkpointing after every activity can be achieved by using the <strong>&ndash;PSPersist<\/strong> workflow parameter.<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfchkpnt {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_ComputerSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_OperatingSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_LogicalDisk<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;test-wfchkpnt -PSPersist\n&nbsp;This next example uses the <strong>&ndash;PSPersist<\/strong> activity parameter to perform a checkpoint after each individual activity:<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfchkpnt {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_ComputerSystem -PSPersist<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_OperatingSystem -PSPersist<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_LogicalDisk -PSPersist<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}\n&nbsp;The same result is achievable by using the preference variable:<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfchkpnt {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$pspersistpreference = $true<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_ComputerSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_OperatingSystem<\/p>\n<p style=\"padding-left: 30px\">&nbsp;Get-WmiObject -Class Win32_LogicalDisk<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$pspersistpreference = $false<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}\n&nbsp;My preference is to use <strong>Checkpoint-Workflow<\/strong> to perform explicit checkpoints at places of my choosing.\nLet&rsquo;s see how checkpointing works. This simple workflow checkpoints itself after every iteration of the loop.<\/p>\n<p style=\"padding-left: 30px\">workflow test-wfchkpnt {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;$i = 0<\/p>\n<p style=\"padding-left: 30px\">&nbsp;while ($true){<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; $i++<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; $i<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Checkpoint-Workflow<\/p>\n<p style=\"padding-left: 30px\">&nbsp;}&nbsp;<\/p>\n<p style=\"padding-left: 30px\">}&nbsp;\nYou need to start the workflow as a Job.<\/p>\n<p style=\"padding-left: 30px\">PS&gt; test-wfchkpnt -AsJob<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Running&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost&nbsp;\n&nbsp;\nAfter the Job has been running for a few seconds, shut down Windows PowerShell.\nOpen a new session (with elevated privileges), import the Windows PowerShell module, and then view the available jobs.<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Import-Module PSWorkflow<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Get-Job<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData&nbsp;&nbsp;&nbsp;&nbsp; Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Resume-Job -Id 8<\/p>\n<p style=\"padding-left: 30px\">Id&nbsp;&nbsp;&nbsp;&nbsp; Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSJobTypeName&nbsp;&nbsp; State&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HasMoreData &nbsp;&nbsp;&nbsp;&nbsp;Location&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&#8212;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8212;-&nbsp;&nbsp; &#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8212;&#8211;&nbsp;&nbsp;&nbsp;&nbsp; &#8212;&#8212;&#8211;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Job7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PSWorkflowJob&nbsp;&nbsp; Suspended&nbsp;&nbsp;&nbsp;&nbsp; True&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; localhost<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Stop-Job -Id 8<\/p>\n<p style=\"padding-left: 30px\">PS&gt; Receive-Job -Id 8 -Keep | select -f 3<\/p>\n<p style=\"padding-left: 30px\">WARNING: The workflow job &#8220;Job7&#8221; was stopped. Receive-Job is only displaying par<\/p>\n<p style=\"padding-left: 30px\">tial results.<\/p>\n<p style=\"padding-left: 30px\">1<\/p>\n<p style=\"padding-left: 30px\">2<\/p>\n<p style=\"padding-left: 30px\">3\n&nbsp;\nResume the Job, and let it run for a few seconds. The workflow is an infinite loop, so stop the Job.&nbsp; Use <strong>Receive-Job<\/strong> to pull the data back.<\/p>\n<h2>Summary<\/h2>\n<p>Windows PowerShell workflows work with the Windows PowerShell job engine. You can run workflows as jobs. Workflows provide the ability to suspend and restart their jobs. Checkpointing the workflow saves the state so your workflow can survive a session failure.\nNext time, you&rsquo;ll see how workflows can survive a computer restart!\n~Richard\nThank you, Richard! Your series on Windows PowerShell workflows is both important and timely. Great job, and I cannot wait for next Wednesday&rsquo;s article.\nJoin me tomorrow when <a href=\"http:\/\/www.powershellgroups.org\/charlotte.nc\">Charlotte Windows PowerShell User Group<\/a> member <a href=\"http:\/\/social.technet.microsoft.com\/Search\/en-US?query=brian%20wilhite&amp;beta=0&amp;rn=Hey%2c+Scripting+Guy!+Blog&amp;rq=site:blogs.technet.com\/b\/heyscriptingguy\/&amp;ac=8\">Brian Wilhite<\/a> talks using Windows PowerShell to work the mouse on his computer.\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Windows PowerShell MVP Richard Siddaway continues his workflow series by talking about the job engine. Microsoft Scripting Guy, Ed Wilson, is here. Today, we have the fourth in a series of guest blog posts written by Windows PowerShell MVP and Honorary Scripting Guy Richard Siddaway dealing with Windows PowerShell workflow. Note &nbsp;&nbsp;The first article, [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,189,3,45,382],"class_list":["post-4317","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-richard-siddaway","tag-scripting-guy","tag-windows-powershell","tag-workflow"],"acf":[],"blog_post_summary":"<p>Summary: Windows PowerShell MVP Richard Siddaway continues his workflow series by talking about the job engine. Microsoft Scripting Guy, Ed Wilson, is here. Today, we have the fourth in a series of guest blog posts written by Windows PowerShell MVP and Honorary Scripting Guy Richard Siddaway dealing with Windows PowerShell workflow. Note &nbsp;&nbsp;The first article, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4317","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\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=4317"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/4317\/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=4317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=4317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=4317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}