Monitor SharePoint Timer Job Execution using PowerShell

This came up a few days after one of our clients asked us to deploy a custom solution that included a custom timer job to our production environment. This custom Timer Job was suppose to synchronize list items from an external farm down into our internal SharePoint 2010 environment, and send out an email notification to our client whenever a new item was found and synchronized. The client stormed in our office one morning complaining that the email service was down because people were no longer getting emails when new items were found. After investigating this further, we realized that the custom timer job was failing. This custom timer job was set to run every 10 minutes, and it had been failing for the last 5 days, without anybody noticing it.

The client was a bit mad that we didn’t spot this earlier, but understood that it was not my team’s job to manually monitor these jobs, However, what came out of this situation was that there should be an easy was, to automate a task that will go and regularly check for the status of any given timer job, and notify the SharePoint administration team in the case where it is failing. I decided to go and write a PowerShell script to do just that.

The Script

My script accepts two parameters, one being the name of the job to monitor, and the second being a timezone adjustment in the case where the time on the SharePoint server and on the machine executing the code are different (I’ve seen cases where bringing everything back to UTC just didn’t cut it). As for the first parameter, I want to make sure I allowed the users to either specify an exact name for the timer job they wished to monitor, or to let them use wild card naming. For example, if a custom timer job’s name is “My Super Fancy Timer Job”, users could simply execute the script and pass it “*Fancy*” to retrieve the proper timer job instance. Now my script isn’t that smart. If there are more than one instance of a given timer job found, it’ll simply monitor the first one in the collection. If you need to be able to monitor several instances of a timer job with PowerShell, you should be able to adjust my script relatively fast.

param([string]$Job, [Int32]$HourSpan=-4) # Receive parameter from script’s call

$timerJob = $null

if($Job.Contains(“*”))

{

    # Get the timer job based on received pattern

    $timerJob = Get-SPTimerJob | Where{$_.Title -like $Job} | Select-Object -First 1

}

else

{

# Get the timer job based on exact name

$timerJob = Get-SPTimerJob | Where{$_.Title -eq $Job} | Select-Object -First 1

}

The second part of my script looks at the reference timer job and extracts information about its schedule. SharePoint Timer jobs are set to run at a specific interval (Hourly, every so many minutes, daily, etc). My script determines what the frequency is for the given timer job and substract this time span (hours, days, or minutes) to the current time. This gives me the the absolute maximum last time the timer job could have run. For example, if a timer job is set to run every 15 minutes, and assume that we are currently June 25th 9PM, then for sure the timer job ran between June 25th 8h45 PM and June 25th 9PM.

Then the script gets a reference to the very last execution summary of the timer job, called the Timer Job’s run history. The events in the history of a timer job are sorted descending by date, meaning that the very first item returned in the collection is the latest one that happened. I then get a reference to that last event, check for the time at which it was executed and compare it with the time span obtained previously. If it is greater than the time obtained from substracting the interval from the current time, then we know for sure it is the latest possible event to happen. This is to force a double check. If ever a timer job was stopped after it last failed, we don’t want out script to always report an error. I could also have simply checked for the timer job’s status to ensure it was still marked as “Online”, but I wanted to show how you can interact with dates using PowerShell. For example, assume that the latest history entry for my custom timer job is at 8h55PM on June 25h, then comparing it with the value calculated earlier (8h45PM June 25th), because the last execution time is greater, we know for a fact that it is the latest event.

If ever the script realizes that the event retrieved from the history is the latest entry and that its status is set to “Failed”, it will print an error on screen. Otherwise, it will print a green message displaying what the status of the last timer job’s execution was.

Putting it All Together

param([string]$Job, [Int32]$HourSpan=-4) # Receive parameter from script’s call

$timerJob = $null

if($Job.Contains(“*”))

{

    # Get the timer job based on received pattern

    $timerJob = Get-SPTimerJob | Where{$_.Title -like $Job} | Select-Object -First 1

}

else

{

    # Get the timer job based on exact name

    $timerJob = Get-SPTimerJob | Where{$_.Title -eq $Job} | Select-Object -First 1

}

# If we couldn’t find the timer job, notify the user using text on yellow background

if($timerJob -eq $null)

{

Write-Host “Timer Job not Found” -BackgroundColor “Yellow” -ForegroundColor “Black”

}

else

{

# Get the latest entry in the timer job’s run history;

$history = $timerJob.HistoryEntries | Select-Object -First 1

# Get the timer job’s schedule

$schedule = $timerJob.Schedule

# Based on the schedule obtain the interval;

$interval = $schedule.Interval

 

# Get the current time and subtract the interval time span from it. This is to ensure we are looking at the most recent entry;

if($schedule.GetType().ToString() -eq “Microsoft.SharePoint.SPMinuteSchedule”)

{

    $thresholdCheck = [System.DateTime]::Now.AddMinutes($interval * -1)

}

elseif($schedule.GetType().ToString() -eq “Microsoft.SharePoint.SPHourlySchedule”)

{

    $thresholdCheck = [System.DateTime]::Now.AddHours(-1)

}

elseif($schedule.GetType().ToString() -eq “Microsoft.SharePoint.SPDailySchedule”)

{

    $thresholdCheck = [System.DateTime]::Now.AddDays($interval * -1)

}

# Get the time difference between the last entry in the timer job’s run history, and the threshold time acquire from the interval. The AddHours call is to adjust between local time and server time;

$diff = New-TimeSpan $history.EndTime.AddHours($HourSpan) $thresholdCheck

# If we are looking at the latest entry and its status is failed, then print an error message, otherwise print whatever its last execution’s status was;

if($diff.minutes -lt 0 -and $history.Status -eq “Failed”)

{

    $message = “Last Execution of Timer Job {“ + $timerJob.Name + “} Failed at [“ + $history.EndTime + “]”

    Write-Host $message -Backgroundcolor “Red”

}

else

{

    $message = “Last Execution of Timer Job {“ + $timerJob.Name + “} was [“ + $history.Status + “]”

    Write-Host $message -Backgroundcolor “Green” -ForegroundColor “Black”

}

}

 

20140625-1.png

 

Microsoft Premier Field Engineer – SharePoint

Leave a Comment

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

*
*