Prof. Powershell
Failure is Not an Option with JobStateInfo Property
The JobStateInfo property can expose the failure of background processes.
- By Jeffery Hicks
- 12/07/2010
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.