Being notified when a child background job is done


PowerShell is so cool that you can be notified when a background job is done. I will not show how to do that, because you can find an excellent explanation about that in this blog post from my good friend Boe Prox .

PowerShell and Events: Object Events

My point is , have you ever tried to use –asjob on Invoke-command ? It will run the scriptblock in a background job. cool !!! But, if you pass more than one server to run this process, lets say 4 servers,  it will create to you one job “Father” and 3 others child jobs. That father  job will  control and report the success of failure of the entire process (all the child jobs)

In PowerShell 2.0  you need to expand the property childjobs to know them. To know what jobs are child  I will create a simples process to sleep the job in a random count. I am using more than one server (in my case the same server more than one time)

If we use the PowerShell 2.0 and get the object $job we will see only one job. The father one :

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader'

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' Get-Job $job.id

The result will be :

image 

As we create the job for 3 servers, to check the other ones we need to expand the property childjobs :

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' Get-Job $job.id | select -ExpandProperty childjobs

image

 

Now imagine that you are dealing with a lot of long-running process in background jobs in different servers. How can I  check the child jobs status ?(to check how it is going the process in each server)  ?

PowerShell 3.0,  2 new parameters was added. They are -IncludeChildJob –ChildJobState. Lets take a look in the –IncludeChildJob

If I run the same command and now get the job but with –IncludeChildJob  parameter :

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' Get-Job $job.id -IncludeChildJob

image

Cool !! Backing to our example, I have a lot of long-running process in background jobs in different servers. Now I want to know when they finished. I can use the example that I mentioned in the beginning of this post, but it will notify you only when all the child jobs finished because the event is subscribe to the father job. I want to be notifed for each child job in the process. Let´s say that I want to know when he process in each server finished.  The parameter –ChildJobState only will show for me the jobs with the specified state. I will not be notified.

Well, the idea is the same to the father job, I just need to go in each child job and register an event for each one :)

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' Get-Job $job.id -IncludeChildJob | % { Register-ObjectEvent -InputObject $_ -EventName StateChanged -MessageData $_.Id -SourceIdentifier "Job.Monitor$($_.id)" -Action { write-host "Job $($event.sender.id) has changed from $($event.SourceEventArgs.PreviousJobStateInfo.State) to $($event.SourceEventArgs.JobStateInfo.state)" -ForegroundColor Yellow if ($event.SourceEventArgs.JobStateInfo.state -eq 'Completed') { $eventSubscriber | Unregister-Event $eventSubscriber.Action | Remove-Job } } }

image

if you are using PowerShell 4.0, you can use the new foreach method :)

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' (Get-Job $job.id -IncludeChildJob).foreach({ Register-ObjectEvent -InputObject $_ -EventName StateChanged -MessageData $_.Id -SourceIdentifier "Job.Monitor$($_.id)" -Action { write-host "Job $($event.sender.id) has changed from $($event.SourceEventArgs.PreviousJobStateInfo.State) to $($event.SourceEventArgs.JobStateInfo.state)" -ForegroundColor Yellow if ($event.SourceEventArgs.JobStateInfo.state -eq 'Completed') { $eventSubscriber | Unregister-Event $eventSubscriber.Action | Remove-Job } } })

Or you can do the same in the PowerShell 2.0, but expanding the property childjobs :

$job = Invoke-Command {start-sleep (get-random (1..500))} -AsJob -computername 'Vader','Vader','Vader' Get-Job $job.id | select -ExpandProperty childjobs | % { Register-ObjectEvent -InputObject $_ -EventName StateChanged -MessageData $_.Id -SourceIdentifier "Job.Monitor$($_.id)" -Action { write-host "Job $($event.sender.id) has changed from $($event.SourceEventArgs.PreviousJobStateInfo.State) to $($event.SourceEventArgs.JobStateInfo.state)" -ForegroundColor Yellow if ($event.SourceEventArgs.JobStateInfo.state -eq 'Completed') { $eventSubscriber | Unregister-Event $eventSubscriber.Action | Remove-Job } } }

Yes..I am using write-host. In this case as I just wanto to show in the screen, I dont need any output piped .

You can change the notification to send email, speak..whatever. It is up to you :)

About Laerte Junior

Laerte Junior Laerte Junior is a SQL Server specialist and an active member of WW SQL Server and the Windows PowerShell community. He also is a huge Star Wars fan (yes, he has the Darth Vader´s Helmet with the voice changer). He has a passion for DC comics and living the simple life. "May The Force be with all of us"
This entry was posted in Algo que Esqueci de Categorizar. Bookmark the permalink.

2 Responses to Being notified when a child background job is done

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s