If you execute a long-running command or script as a PowerShell background job, the command prompt will return instantly while the command continues to run in its own thread.

Timothy Warner

Timothy Warner is a Microsoft Cloud and Datacenter Management Most Valuable Professional (MVP) who is based in Nashville, TN. Check out his Azure and Windows Server video training at Pluralsight, and feel free to reach out to Tim via Twitter.

Latest posts by Timothy Warner (see all)

PowerShell logoLet’s say you sat down at your administrative workstation with a cup of coffee, and you planned to run a series of administration tasks by using PowerShell. The first few tasks complete quickly—no big deal. However, when you run a particularly long-running task (perhaps a remoting job that queries a dozen servers in parallel), your PowerShell session hangs and you find yourself waiting... and waiting... and waiting.

Sure, you can spawn another PowerShell console session, but then you lose all the variables and other goodies that you loaded into your original PowerShell runspace. What to do?

The short answer is to take advantage of the PowerShell background jobs architecture. From now on, we can send long-running tasks to the background and continue our current PowerShell session uninterrupted. Let’s see how to do this.

Starting a new background job ^

Fire up an elevated PowerShell console session and run the following command to retrieve a list of job-related cmdlets:

To create a new background job, we use Start-Job (frankly, I originally looked for a New-Job command, but the PowerShell team must have thought that Start-Job was more action-oriented).

As usual, we’ll first run Update-Help to make sure we have the latest and greatest PowerShell documentation, and then we’ll examine the help for Start-Job, paying particular attention to the examples.

Let’s say that we needed to scour our computer’s D: drive for PowerShell .ps1 script files. Depending on how clogged your file system is, a recursive search such as the following could take a while:

Let’s instead define a new job named “ScriptSearch” that puts the work into the background of our console session:

Let me break down the job details table for you:

  • Id: Jobs receive a unique identification number. The PowerShell job engine often breaks a “parent” job into one or more “child” jobs. You see this a lot when you use remoting.
  • Name: This is the optional “friendly” name that you provide with the -Name parameter.
  • PSJobTypeName: Besides BackgroundJob, PowerShell also has RemoteJob, PSWorkflowJob, and PSScheduledJob job types.
  • State: This tells you whether the job is running, finished, or in an error state.
  • HasMoreData: If this is True, then the job results are still in memory.
  • Location: This tells you on which computer(s) the job task is executing or has executed.

Retrieving job results ^

We use Receive-Job to check on job status. The big “gotcha” here is that, if you forget to include the -Keep parameter, PowerShell dumps the job results from memory! Watch this:

Now, I’ll try to receive the job results a second time:

Ouch! Let’s run Get-Job to see if the job object still persists in memory:

Yeah, the ScriptSearch job shows as completed, and the HasMoreData value of False says that the job results data flew out the proverbial window. We can either close our PowerShell console session to dump the job object from memory, or we can use the PowerShell pipeline and the Remove-Job cmdlet:

Adding scripts to the mix ^

The Start-Job cmdlet has a -FilePath parameter that accepts .ps1 script files. Let’s imagine we wrote a script called EventScrape.ps1 that pulled warning and error messages from the System event log from two computers. Here’s the code:

To run the script as a background job, we simply use the appropriate parameter:

My job ID 2 shows as “localhost” for its location because the script (job) itself is managed on the local computer, which, in my case, is a Windows Server 2012 R2 domain controller named dc1. Within the script code, of course, we’re touching three boxes simultaneously, thanks to the magic of PowerShell remoting.

When fetching results, we’ll be sure to include -Keep:

The PSComputerName property is particularly handy when you use PowerShell remoting because you’ll learn on which remote computer the message arose. Let’s verify that the job’s data still exists in our runspace:

Remember, the True value for the HasMoreData property means that we are free to receive the job results again, provided we include the -Keep parameter.

Understanding parent and child jobs ^

To demonstrate how PowerShell implements parent and child jobs, let’s start a new remoting job that again targets my three lab computers:

If you’ve been watching my code output closely, you’ll observe that my wmi-os job has job ID 4, while the previous job, EventScrape, has job ID 2. What’s the deal with job ID 3? Look here:

PowerShell always defines a parent, or top-level, job, and then does the actual work in one or more child jobs. As you can see, the -IncludeChildJob parameter allows you to see the full family relationship, as it were, between parent and child.

We can also selectively retrieve job results data from a child job directly. Practically speaking, I always query the parent job when I’m fetching results; examining the child jobs is useful when you’re troubleshooting errors.

One bummer about the above example is that using -ComputerName with Get-WMIObject doesn’t return a PSComputerName value like “true” PowerShell remoting does. A better approach would be to run the Get-WMIObject call from the context of an Invoke-Command statement. Live and learn!

Using the -AsJob parameter ^

Some Windows PowerShell commands can be shoehorned into a PowerShell job by using the -AsJob parameter. Let’s investigate which commands can do this in PowerShell v5 preview:

Cool—Invoke-Command is on the list. Let’s capture a list of stopped services on our three computers as a job:

The -AsJob parameter is great for when you decide in the middle of your PowerShell statement that, “Hey, this task would make for a great background job!”

For further study ^

I hope you found this article useful. Please don’t forget about PowerShell’s built-in conceptual help library. Run the following command to get the filenames; I’ve hyperlinked each one to its online version for your studying convenience:

Join the 4sysops PowerShell group!

1+
Share
0 Comments

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2019

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account