Foursquare, PowerShell and IIS Monitoring Fun

Posted on Posted in PowerShell

As part of our daily Ops lives, monitoring is massive part of making sure our systems are performing in a stable and performant fashion. Now lets face it, SCOM and Nagios or [Insert Favourite Monitoring Solution Here], aren’t anything to get excited about.

I’ve been a foursquare (4sq) user for some time now and love the service. Now wouldn’t it be cool, if one of my servers gets and error it would notify me somehow over 4sq?

Well, fear not people, it’s possible.

We have a particularly annoying problem on IIS that happens once in a blue moon that affects our XSLTs on our SharePoint farms, very annoying. WAS Error 5011 (scream) happens every couple of days on a random web front-end and we need to recycle the App Pool to alleviate this corruption. Thanks XLV Web Parts and heap corruptions! *sigh*

Now, when we receive this error my little app called PingBot will checkin to one of the world’s more notorious jails and I know there’s an issue. When the problem is fixed, after an hour he’ll/she’ll/it will check back in our Development Corner venue automatically.

Example Checkin

So without further ado here’s what you’ll need;

  1. A foursquare login – http://www.foursquare.com
  2. A foursquare OAuth2 App Client Id – https://developer.foursquare.com
  3. An OAuth2 AccessToken

Firstly want to say a massive thank you to Trevor Sullivan for initially writing the PowerShell function to get our OAuth2 access Token. If you have no idea what OAuth2 is, jump across to his site for a nice intro;

http://trevorsullivan.net/2012/05/18/powershell-getting-an-access-token-from-instagram-oauth-2-0

Firstly we’ll need to get our Access Token. For this I’ve altered Trevor’s function to suite foursquare’s OAuth2 service provider. This will give you the Access Token that will allows you to query the foursquare API.

Get your Access Token


function Get-oAuth2AccessToken {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)] [string] $AuthUrl
, [Parameter(Mandatory = $true)] [string] $ClientId
, [Parameter(Mandatory = $true)] [string] $RedirectUri
, [int] $SleepInterval = 2

)

# Build the scope of permissions
if (!$Scope) {
$ScopeString = 'basic'
}
foreach ($item in $Scope) {
$ScopeString += $item + '+'
}
$ScopeString = $ScopeString.TrimEnd('+')

# Build the request URL from a template
$RequestUrl = '{0}?client_id={1}&redirect_uri={2}&response_type=token' `
-f $AuthUrl, $ClientId, $RedirectUri, $ScopeString
Write-Debug -Message ('Request URL is: {0}' -f $RequestUrl)

# Create the Internet Explorer object and navigate to the constructed authorization URL
$IE = New-Object -ComObject InternetExplorer.Application
$IE.Navigate($RequestUrl)
$IE.Visible = $true

# Sleep the script for $X seconds until callback URL has been reached
# NOTE: If user cancels authorization, this condition will not be satisifed
while ($IE.LocationUrl -notmatch 'access_token=') {
Write-Debug -Message ('Sleeping {0} seconds for access URL' -f $SleepInterval)
Start-Sleep -Seconds $SleepInterval
}

# Parse the access token from the callback URL and exit Internet Explorer
Write-Debug -Message ('Callback URL is: {0}' -f $IE.LocationUrl)
[Void]($IE.LocationUrl -match '=([\w\.]+)')
$AccessToken = $Matches[1]
$IE.Quit()

# Write the access token to the pipeline inside of a HashTable (in case we want to return other properties later)
Write-Debug -Message ('Access token is: {0}' -f $AccessToken)
Write-Output -InputObject @{ AccessToken = $AccessToken }
}

#Enable debug messages to be sent to console
$DebugPreference = 'continue'

# The base authorization URL from the service provider
$AuthUrl = 'https://foursquare.com/oauth2/authorize'
# Your registered foursquare application Client ID
$ClientId = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
# The callback URL configured on your application. Must be valid!
$RedirectUri = 'http://www.yourdomain.com'

# Get Access Token
Get-oAuth2AccessToken -AuthUrl $AuthUrl -ClientId $ClientId -RedirectUri $RedirectUri

Since PowerShell v3 is right around the corner I really wanted to get to know the new cmdlets so I’ve written PingBot in a way that it will leverage v3s new Invoke-RestMethod, Invoke-WebRequest and the new JSON serializer. Notice how many lines the v2 code is to the v3? Egads!

Foursquare PingBot


#
# Ping Bot Checker
#

# Your application's foursquare UserId
$userId = "XXXXXXXX"
# Your application's foursquare Access Token
$accessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Your aplication's API version, basedon current date
$version = (Get-Date -Format yyyyMMdd).ToString()

# Venues
# Your Home Venue, etc 5500 - Dev Corner
$ourHome = "4fa3f133e4b0e7038a278824"

# Error Venues
# Alcatraz Island
$alcatraz = "4451c80ef964a520a5321fe3"
# San Quintin State Prison
$sanQuintin = "4a348baef964a520639c1fe3"
# Hỏa Lò (Hoa Lo Prison or "Hanoi Hilton")
$hanoiHilton = "4bb022def964a5202c393ce3"
# Robben Island
$robbenIs = "4bb70b6046d4a593ce0ec7c0"
# Long Bay Jail
$longBay = "4bf4bfd594af2d7fcd023b72"

# Get random error jail venue - Jails of the world
$random = @( "$alcatraz", "$sanQuintin", "$hanoiHilton", "$robbenIs", "$longBay" ) | Get-Random

# Servers to check error on
$computers = @("SERVER01", "SERVER02", "SERVER03", "SERVER04")

foreach ($computer in $computers)
{
# Get-EventLog didn't return the Message correctly
$event = Get-EventLog -LogName System -Newest 1 -Source "WAS" -ComputerName $computer -After (Get-Date).AddMinutes(-15) | Select -First 1
if ($event.Id -eq 5011) {
# Get error message as Shout
[string]$id = $event.Id.ToString()
[string]$message = $event.Message.Substring(0,167)
$shout = "Crikey! $computer has $id! $message ..."

if ((Get-Host).Version.Major -eq 3) {
# Get venue name
$urlVenues = "https://api.foursquare.com/v2/venues/" + $random + "?" + "oauth_token=" + $accessToken + "&v=" +$version
$venue = Invoke-RestMethod $urlVenues -Method Get
[string]$venueName = $venue.response.venue.name

# Checkin to venue
Write-Host "Error detected! Checking into $venueName!"
$urlCheckins = "https://api.foursquare.com/v2/checkins/add?venueId=" + $random + "&shout=" + $shout + "&oauth_token=" + $accessToken + "&v=" + $version
Invoke-WebRequest -Uri $urlCheckins -Method Post
}
else {
# Get venue name
$urlVenues = "https://api.foursquare.com/v2/venues/" + $random + "?" + "oauth_token=" + $accessToken + "&v=" +$version
$wc = new-object net.WebClient
$obj = $wc.downloadData($urlVenues)
$s = [text.encoding]::ascii.getString($obj)

# Convert JSON to PowerShell
[Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions") | Out-Null
$c = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$cVenue = $c.DeserializeObject( $s )
[string]$venueName = $cVenue.response.venue.name

# Checkin to venue
Write-Host "Error detected! Checking into $venueName!";
$urlCheckins = "https://api.foursquare.com/v2/checkins/add?venueId=" + $random + "&shout=" + $shout + "&oauth_token=" + $accessToken + "&v=" + $version
$command = '{"jsonrpc": "2.0", "method": "JSONRPC.Version", "id": 1}'
$bytes = [System.Text.Encoding]::ASCII.GetBytes($command)
$web = [System.Net.WebRequest]::Create($urlCheckins)
$web.Method = "POST"
$web.ContentLength = $bytes.Length
$web.ContentType = "application/x-www-form-urlencoded"
$stream = $web.GetRequestStream()
$stream.Write($bytes,0,$bytes.Length)
$stream.close()

# If you want to see checkin output
#$reader = New-Object System.IO.Streamreader -ArgumentList $web.GetResponse().GetResponseStream()
#$reader.ReadToEnd()
#$reader.Close()
}
}

else {
if ((Get-Host).Version.Major -eq 3) {
# No error found, but check to see if home checkin is needed. Checkin to home if error appeared more then 1 hour ago.
# Get checkin createdAt time

$urlUserId = "https://api.foursquare.com/v2/users/" + $userId + "?" + "oauth_token=" + $accessToken + "&v=" + $version
$cUserId = Invoke-RestMethod $urlUserId -Method Get
[string]$items = $cUserId.response.user.checkins.items

$items = $cUserId.response.user.checkins.items
foreach ($item in $items) {
$epochDate = $item.createdAt
}
$createdAt = [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($epochDate))

# Checkin to home again
if ( $createdAt -le (Get-Date).AddHours(-1) ) {
#
Write-Host "Already Home"
}
else {
# Checkin to Home
Write-Host "Checking to home!"
$shoutHome = "Home! Sweet Home!"
$urlCheckins = "https://api.foursquare.com/v2/checkins/add?venueId=" + $ourHome + "&shout=" + $shoutHome + "&oauth_token=" + $accessToken + "&v=" + $version
Invoke-WebRequest -Uri $urlCheckins -Method Post | Out-Null
}

}
else {
# No error found, but check to see if home checkin is needed. Checkin to home if error appeared more then 1 hour ago.
# Get checkin createdAt time
$urlUserId = "https://api.foursquare.com/v2/users/" + $userId + "?" + "oauth_token=" + $accessToken + "&v=" + $version
$wc = new-object net.WebClient
$obj = $wc.downloadData($urlUserId)
$s = [text.encoding]::ascii.getString($obj)

# Convert JSON to PowerShell
[Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions") | Out-Null
$c = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$cUserId = $c.DeserializeObject( $s )

$items = $cUserId.response.user.checkins.items
foreach ($item in $items) {
$epochDate = $item.createdAt
}
$createdAt = [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($epochDate))

# Checkin to home again
if ( $createdAt -le (Get-Date).AddHours(-1) ) {
#
Write-Host "Already Home"
}
else {
# Checkin to Home
Write-Host "Checking to home!"
$shoutHome = "Home! Sweet Home!"
$urlCheckins = "https://api.foursquare.com/v2/checkins/add?venueId=" + $ourHome + "&shout=" + $shoutHome + "&oauth_token=" + $accessToken + "&v=" + $version
$command = '{"jsonrpc": "2.0", "method": "JSONRPC.Version", "id": 1}'
$bytes = [System.Text.Encoding]::ASCII.GetBytes($command)
$web = [System.Net.WebRequest]::Create($urlCheckins)
$web.Method = "POST"
$web.ContentLength = $bytes.Length
$web.ContentType = "application/x-www-form-urlencoded"
#$stream = $web.GetRequestStream()
#$stream.Write($bytes,0,$bytes.Length)
#$stream.close()
}
}
}
}

I have this task running every 15 minutes on an admin box, but a word of warning, you need to be wary that you dont abuse the API rate limits set by fousquare as they’ll block your id. Check their documentation at;

Rate Limits
https://developer.foursquare.com/overview/ratelimits

Who says you can’t have a bit of fun with your monitoring regime aye?

Enjoy!

Kristof

2 thoughts on “Foursquare, PowerShell and IIS Monitoring Fun

Leave a Reply

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