To monitor the state of Windows services, we can even turn to PowerShell. This post's script will send you email alerts whenever one of your crucial Windows services is not running.

Monitoring your infrastructure is an extremely important aspect of managing your systems. If services go down, IT needs to find this out as soon as possible so they can begin the process of bringing them back up. This is common practice for system administrators. To monitor systems there are a slew of tools available. Some of these include Nagios, Monitis, and WhatsUp Gold. However, with the PowerShell script in this post, you can quickly set up your own flexible monitoring solution.

PowerShell service cmdlets

PowerShell provides the ability to query, start, stop, restart, set, and even create Windows services. For instance, to get the current status of the remote registry service, we can simply run:

Getting the current status of the RemoteRegistry service

Getting the current status of the RemoteRegistry service

I can see the current status is "Stopped" as well as see other properties such as the dependent services. For the purposes of monitoring services, we will only be using Get-Service, but to start the remote registry service, I can run Start-Service:

Starting a service with Start Service

Starting a service with Start Service

Get-PSServiceStatus function overview

Using Get-Service allows us to get the service status both locally and remotely, but we also want the capability to receive alerts via email if the status of a service has changed. We can do this with Send-MailMessage as you will see in the function.

Normally, enterprise products use a database to keep track of the status on each polling and then send an alert if the status has changed. While this is still possible with PowerShell, I will keep it simple by only keeping track of the last status polled. To do this, I'll use the presence of a text file and send an alert if that has changed. The flow of this function goes:

  1. For each computer, check to see if a text file is present and if a service is running. An existing text file indicates the service was not running on the previous monitoring attempt.
  2. If the service is not running and the text file is present, do nothing since the status has not changed.
  3. If the service is not running and no text file is present, create the text file and send an email alert stating the service is not running.
  4. If the service is running and the text file is present, delete the text file and send an alert that the service is now running.
  5. If the service is running and the text file is not present, do nothing.

The latest version of my Get-PSServiceStatus function is on Github.

function Get-PSServiceStatus {

param (
    [string[]]$ComputerName,
    [string]$ServiceName,
    [string]$Path,
    [string]$FromAddress,
    [string]$ToAddress,
    [string]$SmtpServer
)
    # Test ping
    workflow Test-Ping 
    {
        param( 
            [Parameter(Mandatory=$true)] 
            [string[]]$Computers
        )
            foreach -parallel -throttlelimit 150 ($Computer in $Computers) 
            {
                if (Test-Connection -Count 1 $Computer -Quiet -ErrorAction SilentlyContinue) 
                {    
                    $Computer
                }
                else
                {
                    Write-Warning -Message "$Computer not online"
                }
            }
        }
    $ComputerName = Test-Ping -Computers $ComputerName 

    foreach ($Computer in $ComputerName)
    {
    $NewPath = Join-Path -Path $Path -ChildPath $Computer
        #Get previous status
        if (Test-Path -Path $NewPath)
        {
            $PreviousStatus = 'Not Running'
        }
        else 
        {
            $PreviousStatus = 'Running'    
        }

        #Get current status
        $CurrentStatus = Get-Service -Name $ServiceName -ComputerName $Computer | Where-Object {$_.Status -eq 'Running'}
        if ($CurrentStatus)
        {
            $CurrentStatus = 'Running'
        }
        else 
        {
            $CurrentStatus = 'Not Running'    
        }
        
        #Current status running and previous up
        if ($PreviousStatus -eq 'Running' -and $CurrentStatus -eq 'Running')
        {
            Write-Output "$Computer $ServiceName still running"
            Continue
        }

        #Current status running and previous down
        if ($PreviousStatus -eq 'Not Running' -and $CurrentStatus -eq 'Running')
        {
            Write-Warning -Message "$Computer $ServiceName now running"
            Remove-Item -Path $NewPath -Force | Out-Null
            Send-MailMessage -Body ' ' -From "$FromAddress" -SmtpServer "$SmtpServer" -Subject "$Computer $ServiceName is now running" -To $ToAddress 
            Continue
        }

        #Current status down and previous down 
        if ($PreviousStatus -eq 'Not Running' -and $CurrentStatus -eq 'Not Running')
        {
            Write-Warning -Message "$Computer $ServiceName still not running"
            New-Item -Path $NewPath -ItemType File -Force | Out-Null
            Continue
        }

        #Current status down and previous up 
        if ($PreviousStatus -eq 'Running' -and $CurrentStatus -eq 'Not Running')
        {
            Write-Warning -Message "$Computer $ServiceName is not running"
            New-Item -Path $NewPath -ItemType File -Force | Out-Null
            Send-MailMessage -Body ' ' -From "$FromAddress" -SmtpServer "$SmtpServer" -Subject "$Computer $ServiceName is not running" -To $ToAddress 
            Continue
        }
    }
}

Using Get-PSServiceStatus

To show how this function works, we will monitor the remote registry service against two computers, test-1 and test-2. Test-1 currently has its remote registry service stopped, while it's running on test-2. Note the -Path parameter is the location to store your text files indicating services are not running.

C:\> Get-PSServiceStatus -ComputerName test-1,test-2 -ServiceName 'RemoteRegistry' -Path 'C:\PSService\' -FromAddress 'alerts@domain.com' ‑ToAddress 'dfrancis@domain.com' -SmtpServer 'smtp.domain.com'
WARNING: test-1 RemoteRegistry is not running
test-2 RemoteRegistry still running

This alerts us via the console and by email that the remote registry service on test-1 is not running.

Starting the remote registry service on test-1 and running the same command again alerts us that the service is now running:

C:\> Get-PSServiceStatus -ComputerName test-1,test-2 -ServiceName 'RemoteRegistry' -Path 'C:\PSService\' -FromAddress 'alerts@domain.com’ -ToAddress 'dfrancis@domain.com' -SmtpServer 'smtp.domain.com'
WARNING: test-1 RemoteRegistry is now running
test-2 RemoteRegistry still running

Creating a scheduled task to run Get-PSServiceStatus

To automate this solution further, we can create a scheduled task to run Get-PSServiceStatus at a certain interval, for instance every 30 minutes. To create the task we will use schtask.exe to run a PowerShell script that includes the Get-PSServiceStatus function.

Here, I will create a scheduled task to run Get-PSSServiceStatus in a PowerShell script every 15 minutes under the logged-in account. The script will query all computers in the "Servers" organizational unit (OU) in Active Directory and check the status of the SNMP service.

Schtasks.exe /CREATE /TN Get-PSSService /TR "Powershell.exe C:\Scripts\Get-PSServiceStatus-Servers.ps1" /RL HIGHEST /F /SC DAILY /RI 15 /ST 00:00

Here is the "Get-PSServiceStatus-Servers.ps1" scheduled task script:

Subscribe to 4sysops newsletter!

Import-Module Get-PSServiceStatus
Get-PSServiceStatus -ComputerName (Get-ADComputer -SearchBase 'OU=Servers,DC=domain,DC=com' -Filter * | Select-Object -ExpandProperty name) ‑ServiceName 'SNMP' -Path 'C:\PSService\' -FromAddress 'alerts@domain.com’ ‑ToAddress 'dfrancis@domain.com' -SmtpServer 'smtp.domain.com'
avataravataravatar
7 Comments
  1. Rob Clarke 5 years ago

    What a great post. Exactly what I needed, an email alert when a service is not running.

    We had an instance where the Azure AD Connect service stopped. The Office 365 portal did not alert us and it hadn’t synced for 65 hours. Looked at creating custom alerts in the portal but you need an Azure Premium subscription to do this.
    Reporting on the failed service is the next best thing.

    Thanks!

  2. Python 4 years ago

    great post. Why am I getting the error? I am running the PowerShell script from the same folder where I have saved the .ps file and running PowerShell ISE as an admin.

     

    Get-PSServiceStatus : The term 'Get-PSServiceStatus' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was 
    included, verify that the path is correct and try again.
    At line:1 char:1
    + Get-PSServiceStatus -ComputerName wafstcapd02 -ServiceName 'Tomcat8.0 …
    + ~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (Get-PSServiceStatus:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

    avatar
    • PS C:\Users\demouser\Desktop> . .\script.ps1
      
      PS C:\Users\demouser\Desktop> Get-PSServiceStatus

      Please dot source the stored script and then try to run the function.

       

      avatar
  3. Vaneet 4 years ago

    Thanks for the wonderful script Dan :).

    I have query for email function, because I didn't get the email with the steps provided by you.

    -How email function will work, do we need to supply the credential in the script by using (System.Net.NetworkCredential) ?

  4. Andrew Kaplan 1 month ago

    Hello.

    I am trying to get the script to operate, but without success. The command syntax that I am using is the following:

    powershell -executionpolicy bypass -File C:\Scripts\Get-PSServiceStatus.ps1 -ComputerName mghromatlab1 -ServiceName ‘MATLAB License Server’ -Path C:\PSService\matlab.txt -FromAddress -ToAddress -SmtpServer

    However, I am not receiving emails after running the script. What am I doing wrong?

  5. Did you supply the email addresses for the -FromAddress and -ToAddress, along with the SMTPServer address (name or IP)?

    Lastly, your need to make sure your sending the email to and from the correct domain for the mail server. You cannot normally send mail through a mail server for a different domain than it can receive, unless it is configured to allow your machine’s email to be relayed. If you’re trying to use a mail server external to the domain, then you need to understand what the requirements of that mail server are. Most of the time, the Send-MailMessage is not sufficient for those, and you end up needing something like MailKit or Mailosaurus to send email to those.

    David F.

  6. Andrew Kaplan 1 month ago

    Hello.

    Thank-you for your response. The actual command syntax that I used is the following:

    powershell.exe -executionpolicy bypass C:\Scripts\GetDiskReport.ps1 -ComputerName mghromatlab1 -ServerName ‘MATLAB License Server’ -Path C:\PSService\matlab.txt -FromAddress rowatchman@partners.org -ToAddress ahkaplan@partners.org -SmtpServer smtp.partners.org

    When I ran the above command again, the following error messages appeared on-screen:

    At C:\Scripts\GetDiskReport.ps1:1 char:599
    + … ´TËnÂ0¼Wê?D¾V‰¡‡ªú8¶H¥`ì
    + ~
    Missing closing ‘)’ in expression.
    At C:\Scripts\GetDiskReport.ps1:2 char:17
    + XõKöòúûnDUA*å)YïÌììăÑÚšl 1iïJÖ/z,’½ÒnV²ÉK~ϲ„Â)a¼ƒ’m ±Ñðúj0Ù …
    + ~
    Unexpected token ‘)’ in expression or statement.
    At C:\Scripts\GetDiskReport.ps1:4 char:32
    + ¤×8ãAÈO1~ÛëÝqé‚Ãk6<A%³ç5}Þ*‰`Ë·k®’‰Œ–I)_:õƒ%ß1ÔÙœIsÒ
    + ~
    Unexpected token '}' in expression or statement.
    At C:\Scripts\GetDiskReport.ps1:7 char:65
    + "ÒÙÞH¡w"ë„™ìw̤ھ½£ ºPÛ^æôçËOÖ›ƒ›Ô;§ž»vøéˆÀoñÀˆP//=+¡G*~\§¨§ì’ƒ¼BYL|¡˜î¤¬&¼#‘+çXB#)׊ZÑÊ*Ö …
    + ~
    Unexpected token ‘)’ in expression or statement.
    At C:\Scripts\GetDiskReport.ps1:15 char:404
    + … à+qp5µQÙ1Žk«›dµ¿²è³Ð²g;øµéâi¸P(mgël/yÂjŸ¸Zª{y,™«|“&awSôslI\ …
    + ~
    The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double quotation marks (“&”) to pass it as part of a string.
    Not all parse errors were reported. Correct the reported errors and try again.
    + CategoryInfo : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : MissingEndParenthesisInExpression

Leave a reply

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

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account