Friday, December 20, 2013

Lightweight monitoring solution with PowerShell

For one of my projects, I needed a simple and lightweight monitoring and testing framework for the public website. As we tweaked SharePoint with some custom redirect functionality to improve SEO, and moved the Google Analytics code from one solution to one other, I wanted to make sure these functionalities still work after each release.
As UI bugs will be noticed directly, those more technical features are hardly noticed directly on failure. So there was the need to script and monitor those features.

As I wanted a simple, free and easy to expand solution (I worked with HostMonitor before, it's easy to write small tests there, but it isn't free), I decided to create my own PowerShell framework. Here it is. It is written for PowerShell 2.0, so webrequests are not executed with Invoke-WebRequest but with the C# System.Net variant.
The script is basically executing tests found in one folder, format the results as HTML and sending it by email with a SMTP server. Things to improve are more test outcomes (now it is Success or Failure, but there might be a yellow inbetween warning or so), or possibly using a tool like Selenium to run tests inside a browser session as well.

Import-Module .\Tests\TestModule.psm1
Start-Tests -testContainer C:\Example\Tests
$result = Get-EmptyResult
$result.TestName = "301 Redirect on root site"
$result.Group = "SEO"
$url = "http://www.nielsvrolijk.local"
$req = [system.Net.WebRequest]::Create($url)
$req.AllowAutoRedirect = $false
$res = $req.GetResponse()
$result.Data = $res.GetResponseHeader("Location")
$result.Value = $res.StatusCode
$result.Success = $res.StatusCode -eq [system.net.httpstatuscode]::MovedPermanently
return $result
$result = Get-EmptyResult
$result.TestName = "NV UA-Id present"
$result.Group = "Google Analytics"
$url = "http://www.nielsvrolijk.local/"
$body = (New-Object System.Net.WebClient).DownloadString($url)
$testString = "_gaq.push(['_setAccount', 'UA-42-1'])"
$result.Success = $body.Contains($testString)
return $result
$testProperty = @{
TestName = "Empty result"
Success = $false
Data = ""
Value = ""
Group = ""
}
function Get-EmptyResult
{
$result = New-Object PSObject -Property $testProperty
return $result
}
function Start-Tests([switch]$displayOnly, $testContainer) {
#Run tests
$tests = Get-ChildItem $testContainer -Filter Test-*.ps1
$fs = "<font face=""Calibri, Arial, sans-serif"">"
$fe = "</font>"
$body =
"<body><style>
*, body, table, tr, th, td { font-family: Calibri, Arial, sans-serif;}
th { text-align: left; }
.OK .status { background-color:Green; }
.Fail .status { background-color:Red; }
</style>
<table>
<thead><tr><th>$fs Test$fe</th><th>$fs Status$fe</th><th>$fs Waarde$fe</th><th>$fs Aanvullende data$fe</th></tr></thead>
"
$jobs = @()
$results = @()
$r = Remove-Job * -Force
foreach ($test in $tests)
{
$jobs += Start-Job -Init ([ScriptBlock]::Create("Set-Location $testContainer;Import-Module .\TestModule.psm1")) -FilePath $test.FullName
}
$r = Wait-Job $jobs
foreach ($job in $jobs)
{
$result = Receive-Job $job
$results += $result
}
$results = $results | Group-Object Group
if ($displayOnly) {
foreach($group in $results) {
Write-Output $group.Group
}
}
foreach ($groupResult in $results)
{
$group=$groupResult.Name
$body += "<tr><td colspan=""4""><strong>$fs$Group$fe</strong></td></tr>"
foreach ($result in $groupResult.Group)
{
$testName = $result.TestName
$success = "OK"
if (!$result.Success) {
$success = "Fail"
}
$colorStyle =
$value = $result.Value
$data = $result.Data
#Write-Host $result.Group
$body = "$body <tr class=""$success""><td>$fs$testName$fe</td><td class=""status"">$fs$success$fe</td><td>$fs$value$fe</td><td>$fs$data$fe</td></tr>"
}
}
$body = "$body </table></body>"
#Send mail
$smtp = "smtp.local"
$to = "Niels <niels@nielsvrolijk.local>"
$from = "Daily Test <test@nielsvrolijk.local>"
$subject = "Daily test"
if ($displayOnly) {
Write-Host $body
} else {
Send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -BodyAsHtml
}
}
export-modulemember -function Get-EmptyResult, Start-Tests