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

 

Determine SharePoint Farm Version Remotely using PowerShell

Earlier this week, I wrote a post about how you can use PowerShell to determine what version of a SharePoint Farm and Updateable Components you are running in a given environment. I also wrote, a few months ago, a short article on using SharePoint’s service.cnf endpoint to remotely return the farm’s version of an environment. Today I decided to combine both articles and to demo how it is possible for one to write a short PowerShell script that will remotely ping the service.cnf service to remotely retrieve the farm’s version. The PowerShell script below will prompt the user for the URL of any site on the given Farm, and will automatically build the service’s url:

http://<url of given site>/_vti_bin/service.cnf

Then it will use the Invoke-WebRequest cmdlet to retrieve the content back from the web service using a Screen Scrapping methodology. It will then manipulate the content retrieved to extract only the Farm’s build number and print it on screen. Please note that this script will use the default user’s credentials to connect remotely to the environment. Here is the full script:

​$url = Read-Host “URL of Web Application”
if($url.EndsWith(“/”) -eq $false)
{
$url = $url + “/”;
}
$serviceUrl = $url + “_vti_pvt/service.cnf”
$response = Invoke-WebRequest $serviceUrl -UseDefaultCredentials
$content = $response.Content
$content.Split(‘|’)[2].Trim()​

remoteIconLrg.gif

Identifying All Artefacts that Uses Active Directory Groups in SharePoint using PowerShell

Background Information:

We wish to identify all webs and lists in a given SharePoint Web Application, where Active Directory (AD) groups are used to grant permissions. We want to develop a script in PowerShell that will loop through all site collections in a given Web Application and then through all webs​ in each of these site collection. The script will look at the permissions of each web and determine if permissions are granted to a an AD group. If they are, the script will add them to a string variable, list the URL of the site and the AD group being used to grant permissions. The script will also take a look at every list in those SharePoint webs. If a list has its permission inheritance broken (not inheriting from the web), then the script will look at all of its permission and determine if any AD groups are used to grant permissions. If the script finds any, it will add the URL of the web on which the list exists, the name of the list, as well as the name of the AD group that is being used to grant permissions.

Once all sites, webs and lists have been analyzed, the script will output its result in a text file. The script will also be prompting the user to input the URL of the Web Application to scan.

Script:

function Dig($url)
{
try{
$rootWeb = Get-SPWeb $url
Write-Host $url -backgroundcolor “green”
foreach($web in $rootWeb.Webs)
{
foreach($list in $web.Lists)
{
if($list.HasUniqueRoleAssignments)
{
$assignments = $list.RoleAssignments
foreach($assign in $assignments)
{
if($assign.Member.IsDomainGroup)
{
$script:report += $web.Url + “`t” + $list.Title + “`t” + $assign.Member.DisplayName + “`r`n”
}
}
}
}


$assignments = $web.RoleAssignments
foreach($assign in $assignments)
{
if($assign.Member.IsDomainGroup)
{
$script:report += $web.Url + “`t`t” + $assign.Member.DisplayName + “`r`n”
}
}

         Dig($web.Url)
$web.Dispose()

      }
}
catch{Write-Host $web -Backgroundcolor “Yellow”}
}
$script:report
$url = Read-Host “Web Application URL”
$WebApp = Get-SPWebApplication $url

foreach($site in $webApp.Sites)
{
Dig($site.RootWeb.Url)
$site.Dispose()
}

$script:report | Out-File “C:\temp\ADGroupsReport.txt”

Listing All Properties of a SharePoint Artefact using PowerShell

This is a very useful tip I use daily in my work. A while ago, I tried to figure out a way to list all properties of an object and to expose all of their values without having to call one at a time. Since PowerShell object really are .NET entities in the background, each item exposes its own set of properties and methods. We are all familiar with the Get-Member PowerShell cmdlet that gives the name of all methods and properties of an object. However what we are looking for here is​ a way to get a full list of all properties, with their names and values. Using the PowerShell piping functionality, we can simply get a reference to an object, and then pipe it through a Select statement that will query the object for all of its properties. The piping function will then take care of iterating through all of them, and will display its value on screen. The example shown in Figure 1 below shows the execution of this concept on a web object, representing the root web of my site collection:

Get-SPWeb http://localhost | Select -Property *

20140609-1.png

Using the Select -Property * statement tells PowerShell to grab the object received by the pipe command, to query it for its properties that have a name of * (wildcard) and to list all of there values. Note that this method doesn’T have to be used with SPWeb object, and can be used with any level of SharePoint artefacts. Enjoy!