Monitor Windows Services with UptimeRobot
I recently learnt that UptimeRobot can monitor cron jobs using a heartbeat ping. On the documentation they provide a short example of how to monitor a Windows Server using a scheduled task and an Invoke-Request
HTTP request to pulse the heartbeat. This got me thinking... and I ended up extending this to monitor Services running on a Windows Server too.
Overview
The requirements are:
- Monitor the overall server availability - a heartbeat that is sent unconditionally each time the script is run
- Have a dynamic and extendable list of Services to check each run
Why use UptimeMonitor?
I wanted something that is hosted and external to all of our infrastructure. The cost of the UptimeRobot Team plan is more than reasonable to avoid the management overhead with running a self-hosted monitor, plus I wanted something setup fairly quickly, this was just the ticket.
It also has the additional security benefit of not requiring any holes to be punched through firewalls, as the heartbeat is sent with a simple HTTP GET request.
Monitors
I setup a monitor in UptimeRobot for the server as a whole, and then individual monitors for each of the Services I wanted to watch.
I setup the monitors in a fairly standard way:
- Create a new monitor, choose Cron, and give it a name
- Set the response time (I typically used update every 1 minute, down after 30 seconds)
- Save and get the UID from the provided heartbeat URI
The script
The PowerShell below is simple but effective. You could add further checks for things that are not Windows Services. For example, a ping test to an internal website (avoiding opening up firewalls to monitor it externally), pulsing the heartbeat URI on a successful ping.
Set as many services as you need in the array. ServiceName is the name you see under 'Service name:' on when opening the properties of the Service. UID is the part of the heartbeat URI after https://heartbeat.uptimerobot.com/
that is generated after creating the monitor.
Save the script somewhere accessible to the account that will run the scheduled task.
# Send the server status first
# Enter the UID of the server monitor here
$serverUID = 'UID'
Invoke-WebRequest -Uri "https://heartbeat.uptimerobot.com/$serverUID" -UseBasicParsing
# Define services to monitor
# Enter the service names and corresponding monitor UIDs here
$servicesToMonitor = @(
@{ ServiceName = 'example'; UID = 'UID' },
@{ ServiceName = 'example'; UID = 'UID' },
@{ ServiceName = 'example'; UID = 'UID' }
)
foreach ($serviceInfo in $servicesToMonitor) {
$serviceName = $serviceInfo.ServiceName
$serviceUID = $serviceInfo.UID
try {
$service = Get-Service -Name $serviceName
if ($service.Status -eq 'Running') {
$url = "https://heartbeat.uptimerobot.com/$serviceUID"
Invoke-WebRequest -Uri $url -UseBasicParsing
Write-Host "$serviceName is running. Web request sent to $url."
} else {
Write-Host "$serviceName is not running. No action taken."
}
}
catch {
Write-Host "Error checking service '$serviceName': $_"
}
}
Scheduled task
The last step was creating the scheduled task:
- Create a new task in Task Scheduler
- Set the task to run even when not logged in (best practice is to use a dedicated service account for this)
- Set the trigger to be:
- Run daily
- Set the start time a few minutes in the future
- Repeat every 1 minute
- For duration of 1 day
- Enabled
- Set the action:
- Start a program:
C:\Windows\System32\conhost.exe
- Arguments:
--headless powershell.exe -WindowStyle Hidden -NoProfile -NonInteractive -File "C:\scripts\uptimerobot.ps1"
- This prevents a PowerShell window from appearing on each run
- Start a program:
- On the settings tab, enable 'Run task as soon as possible after a scheduled start is missed'
Gotchas
I found some issues with running the script as a scheduled task under a service account, in that the service account didn't have access to read the status of some protected Services. I followed this guidance by Peter Hahndorf on how to update SDDL to grant permissions to a Service (warning: pay attention and don't forget to always update the permissions by also including the original string of permissions).
If using a service account to run the task, don't forget it'll need 'Run as batch' permissions in the Local Security Policy editor, and access to read the folder storing the script.
And that's it, get the scheduled task running and you should see the monitors glow green after a few minutes.