Prof. Powershell

Failure is Not an Option with JobStateInfo Property

The JobStateInfo property can expose the failure of background processes.

One of the great features in Windows PowerShell is the ability to create background jobs. These are PowerShell expressions that run in a separate runspace which means you can keep working in the console. Typically, you create jobs with the Start-Job cmdlet, although you can also use cmdlets like Get-WMIObject and Invoke-Command that support the -AsJob parameter.

One of the challenges when running a job is that you can't see when things fail. For example, I can run this command and PowerShell won't complain:

PS C:\> get-wmiobject win32_bios -computername Server01 asjob

Id Name State   HasMoreData Location  Command
-  ---- -----   ----------- --------  -------
1  Job1 Running False       localhost Get-WMIObject

It's not until I go to look at the job that I realize there was a problem:

PS C:\> get-job 1

Id Name State  HasMoreData Location  Command
-  ---- -----  ----------- --------  -------
1  Job1 Failed False       localhost Get-WMIObject

How can I figure out what happened?

When you create a job, the job you see here is the parent or executive job. It. in turn spawns one or more child jobs. It is these child jobs that carry out the actual work:

PS C:\> get-job | select childjobs

ChildJobs
---------
{Job2}

The job object contains a property called JobStateInfo. If you pipe the childjob to Get-Member, you'll see what I'm talking about. Because ChildJobs is always an array, often of a single item, you'll need an expression like this:

PS C:\> (get-job 1).childjobs[0] | select *

State         : Failed
StatusMessage : test
HasMoreData   : False
Location      : localhost
Command       :
JobStateInfo  : Failed
Finished      : System.Threading.ManualResetEvent
InstanceId    : 468ae4d2-1bba-4458-ade4-28e4673b1af6
Id            : 2
Name          : Job2
ChildJobs     : {}
Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}

The JobStateInfo property is itself an object:

PS C:\> (get-job 1).childjobs[0].jobstateinfo | get-member

TypeName: System.Management.Automation.JobStateInfo

Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
ToString    Method     string ToString()
Reason      Property   System.Exception Reason {get;}
State       Property   System.Management.Automation.JobState
                        State {get;}

Notice that Reason property? That's what we need. I'll take a shortcut approach to get this value with this one-line expression:

PS C:\> (get-job 1).childjobs[0].jobstateinfo.Reason
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

Because I saw that the child job was job 2, I also could have used this expression:

PS C:\> (get-job 2).jobstateinfo.Reason

In any event, now I can see the problem and take steps accordingly.

This isn't a 100 percent, foolproof method of troubleshooting failed background jobs. Sometimes the job will complete but you'll see the exception when you retrieve results. And unfortunately, sometimes a job will fail with no reason. But I have to say, that has been the exception rather than the rule in my experience. Just remember to always check the child jobs.

About the Author

Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.

comments powered by Disqus
Most   Popular