Powershell – Monitaur, a simple monitoring script

just add your devices to Monitaur-List.csv and run!

Monitaur-List.csv format:

Local 1,127.0.0.1
Local 2,127.0.0.2
Local 3,::1
cloudflare,1.1.1.1
Google,google.com
8.8.8.8,8.8.8.8
down,kdjfkdjfk

<#====================================================================
Author(s):		Mathew Bray 
File: 			Monitaur.ps1
Date:			2015-09-09
Revision: 		2
References:
 Start-Monitor - https://gallery.technet.microsoft.com/scriptcenter/2d537e5c-b5d4-42ca-a23e-2cbce636f58d/
 Set-TopMost - poshcode.org/4070
 Window Sizing - http://blogs.technet.com/b/heyscriptingguy/archive/2006/12/04/how-can-i-expand-the-width-of-the-windows-powershell-console.aspx
 Blink-Message - https://social.technet.microsoft.com/forums/windowsserver/en-US/3e9585cf-bbed-4e8e-80ba-65fd0a3a71d5/flashing-text-in-powershell
 Start-Countdown - http://poshcode.org/3378
 SetForegroundWindow - http://stackoverflow.com/questions/2556872/how-to-set-foreground-window-from-powershell-event-subscriber-action


Release Notes:
 v1 - Resized Start-Monitor and set to clear screen each ping session. 
      Added note to disable QuickEdit (accidental highliting will pause the script).
      Added beep when down and options for speech, sound file, or built in sounds.
      Added blinking text when there is no response, and an "email sent" notification.

      
 v2 - Added an IP translation ability to display any name or description instead of the IP.

====================================================================
#>

#############################################################
#This section sets the properties of the window, size and buffer size, as well as title.
$pshost = get-host
$pswindow = $pshost.ui.rawui

$pswindow.WindowTitle = "Monitaur"

$newsize = $pswindow.windowsize
$newsize.height = 50
$newsize.width = 30
$pswindow.windowsize = $newsize

$newsize = $pswindow.buffersize
$newsize.height = 50
$newsize.width = 30
$pswindow.buffersize = $newsize

$pswindow.BackgroundColor = "black"
Clear-Host
 
#############################################################
# Main Monitor function - https://gallery.technet.microsoft.com/scriptcenter/2d537e5c-b5d4-42ca-a23e-2cbce636f58d/
function Start-Monitaur {
     
     
      #Requires -Version 2.0            
      [CmdletBinding()]            
      Param             
      (                       
            [Parameter(Position=0,                         
                       ValueFromPipeline=$true,            
                       ValueFromPipelineByPropertyName=$true)]

            # String for Computer Names
            [String[]]$ComputerName = $env:COMPUTERNAME,        
            
            # Switch to Enable Email Notifications on First Down
            [Switch]$notifyonServerDown,
            
            # Switch to Enable Email Notifications on Server Online
            [Switch]$notifyonServerBackOnline,
            
            # Switch to Enable Email Notifications on MaxOutageCount
            [Switch]$notifyonMaxOutageCount,
            
            # Switch to Enable all notifications
            [Switch]$notifyAll,

            # Switch to Keep on Top
            [Switch]$StayOnTop,

            # Switch to Keep on Top
            [Switch]$notifySpeech,
            
            # specify the time you want email notifications resent for hosts that are down
            $EmailTimeOut = 30,
            # specify the time you want to cycle through your host lists.
            $SleepTimeOut = 10,
            # specify the maximum hosts that can be down before the script is aborted
            $MaxOutageCount = 100,

            # speech when unavailable
            $speechdown = "Something is down!",

            # sound when unavailable
            $soundfileDown = 'C:\windows\media\notify.wav',
            
            # specify who gets notified 
            $tonotification = "mathew.bray.1@us.af.mil", 

            # specify where the notifications come from 
            $fromnotification = "Monitor-Script@us.af.mil", 
            # specify the SMTP server 
            $smtpserver = "131.9.25.144",
            
            # reset the lists of hosts prior to looping
            $OutageHosts = @()            
      )#End Param


 # Use end block, to ensure all computers are read in at once, even by pipeline     
 end {





$userDesktop = [Environment]::GetFolderPath("Desktop")
$listFilePath = "$userDesktop\LaunchPad\Scripts\Monitaur-List.csv"





    # If notifyAll option is used, then set all messaging to true
    if ($notifyAll)
    {
        $notifyonMaxOutageCount,$notifyonServerBackOnline,$notifyonServerDown =  $True,$True,$True
    }

    Write-Verbose -Message "computername: $computername"
    Write-Verbose -Message "notifyonMaxOutageCount: $notifyonMaxOutageCount"
    Write-Verbose -Message "notifyonServerBackOnline: $notifyonServerBackOnline"
    Write-Verbose -Message "notifyonServerDown: $notifyonServerDown"

    # Allow
    if ( $Input )
    {
        Write-Verbose -Message "Input: $Input"
        $ComputerName = $Input
    }
    
    #############################################################
    # Stay on Top Script - poshcode.org/4070
    if ($StayOnTop)
    {
        $signature = @"
       
        [DllImport("user32.dll")]  
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);  
 
        public static IntPtr FindWindow(string windowName){
                return FindWindow(null,windowName);
        }
 
        [DllImport("user32.dll")]
        public static extern bool SetWindowPos(IntPtr hWnd,
        IntPtr hWndInsertAfter, int X,int Y, int cx, int cy, uint uFlags);
 
        [DllImport("user32.dll")]  
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
 
        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
 
        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
 
        const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
 
        public static void MakeTopMost (IntPtr fHandle)
        {
                SetWindowPos(fHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }
 
        public static void MakeNormal (IntPtr fHandle)
        {
                SetWindowPos(fHandle, HWND_NOTOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }
"@
 
 
        $app = Add-Type -MemberDefinition $signature -Name Win32Window -Namespace ScriptFanatic.WinAPI -ReferencedAssemblies System.Windows.Forms -Using System.Windows.Forms -PassThru
 
        function Set-TopMost
        {
            param(         
                [Parameter(
                    Position=0,ValueFromPipelineByPropertyName=$true
                )][Alias('MainWindowHandle')]$hWnd=0,
                [Parameter()][switch]$Disable
            )
            if($hWnd -ne 0)
            {
                if($Disable)
            {
                Write-Verbose "Set process handle :$hWnd to NORMAL state"
                $null = $app::MakeNormal($hWnd)
                return
            }
                Write-Verbose "Set process handle :$hWnd to TOPMOST state"
                $null = $app::MakeTopMost($hWnd)
            }
            else
            {
                Write-Verbose "$hWnd is 0"
            }
            }
        function Get-WindowByTitle($WindowTitle="*")
        {
            Write-Verbose "WindowTitle is: $WindowTitle"
       
            if($WindowTitle -eq "*")
            {
                    Write-Verbose "WindowTitle is *, print all windows title"
                    Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object Id,Name,MainWindowHandle,MainWindowTitle
            }
            else
            {
                    Write-Verbose "WindowTitle is $WindowTitle"
                    Get-Process | Where-Object {$_.MainWindowTitle -like "*$WindowTitle*"} | Select-Object Id,Name,MainWindowHandle,MainWindowTitle
            }
        }
        gps -Id $pid | Set-TopMost
    }

    #######################################################
    # Bring to foreground Script - http://stackoverflow.com/questions/2556872/how-to-set-foreground-window-from-powershell-event-subscriber-action
    Add-Type @"
        using System;
        using System.Runtime.InteropServices;
        public class Tricks {
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetForegroundWindow(IntPtr hWnd);
        }
"@
        $httf = (Get-Process -id $pid).MainWindowHandle

    #############################################################
    # Blinking Message function - https://social.technet.microsoft.com/forums/windowsserver/en-US/3e9585cf-bbed-4e8e-80ba-65fd0a3a71d5/flashing-text-in-powershell
    function Blink-Message {
     param([String]$Message,[int]$Delay,[int]$Count,[ConsoleColor[]]$Colors,$ForeGroundColor) 
        $startColor = [Console]::BackgroundColor
        $startFGColor = [Console]::ForegroundColor
        $startLeft  = [Console]::CursorLeft
        $startTop   = [Console]::CursorTop
        $colorCount = $Colors.Length
        for($i = 0; $i -lt $Count; $i++) {
            [Console]::CursorLeft = $startLeft
            [Console]::CursorTop  = $startTop
            [Console]::BackgroundColor = $Colors[$($i % $colorCount)]
            [Console]::ForegroundColor = $ForeGroundColor
            [Console]::WriteLine($Message)
            Start-Sleep -Milliseconds $Delay
        }
        [Console]::BackgroundColor = $startColor
        [Console]::ForegroundColor = $startFGColor
    }

    #############################################################
    # Ticking Timer - http://poshcode.org/3378
    function Start-Countdown {
        Param(
        [INT]$Seconds = (Read-Host "Enter seconds to countdown from"),
        [Switch]$ProgressBar,
        [String]$Message = "Blast Off!"
        )
        #Clear-Host
        while ($seconds -ge 1){
            If($ProgressBar){
	            Write-Progress -Activity "Countdown" -SecondsRemaining $Seconds -Status "Time Remaining"
	            Start-Sleep -Seconds 1
            }ELSE{
                Write-Host -NoNewline  "."
	            Start-Sleep -Seconds 1
	            #Clear-Host
            }
            $Seconds --
        }
    #Write-Output $Message
    }


      
#############################################################
# start looping here
    Do{
        $available = @()
        $notavailable = @()
        Write-Host ""
        Write-Host "    "(Get-Date)
        Write-Host ""
            
        # Read the File with the Hosts every cycle, this way to can add/remove hosts
        # from the list without touching the script/scheduled task, 
        # also hash/comment (#) out any hosts that are going for maintenance or are down.
        #$ComputerName | Where-Object {!($_ -match "#")} | 
        Import-Csv $listFilePath -Header DisplayName,IP | 
        ForEach-Object {
                if(Test-Connection -ComputerName $_.IP -Count 1 -ea silentlycontinue)
            {
                # if the Host is AVAILABLE then write it to the screen
                Write-Host " " $_.DisplayName  -BackgroundColor Black -ForegroundColor Green
                            
                [String[]]$available += $_.DisplayName
                        
                # if the Host was out and is now BACK-ONLINE, remove it from the OutageHosts list
                if ($OutageHosts -ne $Null)
                {
                    if ($OutageHosts.ContainsKey($_.IP))
                    {
                        # Back online tone
                        [console]::beep(3000,200)
                        [console]::beep(4000,200)
                        [console]::beep(5000,200)
                        Blink-Message "<<<<<< Back Online! :) >>>>>>>" 150 7 black,white,green green


                        $OutageHosts.Remove($_.IP)
                        $Now = Get-date
                        if ($notifyonServerBackOnline)
                        {
                        $Body = $_.DisplayName + "is back online at $Now"
                        Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification `
                        -Subject "Host " + $_.DisplayName + " is up" -SmtpServer $smtpserver
                        }
                                    
                    }
                }  
            }
            else
            {
                # If the host is unavailable, give a warning to screen
                write-host " " $_.DisplayName "..." -BackgroundColor Black -ForegroundColor Red

                if(!(Test-Connection -ComputerName $_.IP -Count 2 -ea silentlycontinue))
                {
                    # If the host is still unavailable for 4 full pings, play sound, write error, and send email
                    $sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
                    Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
                    $hwnd = @(Get-Process -id $pid)[0].MainWindowHandle
                    #[Void] [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)



                    #[Void] [Tricks]::SetForegroundWindow($httf)
                             
                    ############################################################
                    # Optional Sound Types
                    #
                    #System Sound
                    #[System.Media.SystemSounds]::Asterisk.play()
                    #[System.Media.SystemSounds]::Beep.play()
                    #[System.Media.SystemSounds]::Exclamation.play()
                    #[System.Media.SystemSounds]::Hand.play()
                    #
                    #Speech
                    #(New-Object -com SAPI.SpVoice).speak($speechdown) | Out-Null
                    #
                    #Play Sound File
                    #$sound = new-Object System.Media.SoundPlayer;
                    #$sound.SoundLocation=$soundfileDown;
                    #$sound.Play();

                    #Console Beep

                    [console]::beep(5000,200)
                    [console]::beep(4000,200)
                    [console]::beep(3000,200)

                    Blink-Message "<<<<<<< No Response!! >>>>>>>>" 150 7 black,white,red red


                              
                                           
                    #write-host "        "$_ -BackgroundColor Magenta -ForegroundColor White
                    [Array]$notavailable += $_.DisplayName
                              
                    if ($OutageHosts -ne $Null)
                    {
                        if (!$OutageHosts.ContainsKey($_.DisplayName))
                        {
                            # Announce Host is down
                            if ($notifySpeech) {
                                (New-Object -com SAPI.SpVoice).speak($_.DisplayName + " is down!") | Out-Null
                            }
                                          
                            # First time down add to the list and send email
                            #Maximize window and bring to foreground
                            [Void] [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
                            [Void] [Tricks]::SetForegroundWindow($httf)

                            #Write-Host "----- Down! Email Sent!! -----"

                            if ($notifyonServerDown)
                            {
                                Blink-Message "///////////////\\\\\\\\\\\\\\\`r<<<<< Down! Email Sent!! >>>>>`r\\\\\\\\\\\\\\\///////////////" 150 7 black,white,red red
                            }

                            $OutageHosts.Add($_.DisplayName,(get-date))
                            $Now = Get-date
                            if ($notifyonServerDown)
                            {
                                $Body = $_.DisplayName + " has not responded for 5 pings at $Now"
                                Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification `
                                -Subject $_.DisplayName + " is down" -SmtpServer $smtpserver
                            }
                        }
                        else
                        {
                            # If the host is in the list do nothing for 1 hour and then remove from the list.
                            #Write-Host "$_ Is in the OutageHosts list"
                            if (((Get-Date) - $OutageHosts.Item($_.DisplayName)).TotalMinutes -gt $EmailTimeOut)
                            {$OutageHosts.Remove($_.DisplayName)}
                        }
                    }
                    else
                    {
                        # Announce Host is down
                        if ($notifySpeech) {
                            (New-Object -com SAPI.SpVoice).speak($_.DisplayName + " is down!") | Out-Null
                        }
                        # First time down create the list and send email
                        # Maximize and bring to foreground
                        [Void] [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4)
                        [Void] [Tricks]::SetForegroundWindow($httf)
                        #Write-Host "----- Down! Email Sent!! -----"
                                    
                        if ($notifyonServerDown)
                        {
                            Blink-Message "///////////////\\\\\\\\\\\\\\\`r<<<<< Down! Email Sent!! >>>>>`r\\\\\\\\\\\\\\\///////////////" 150 7 black,white,red red
                        }  
                        $OutageHosts = @{$_.DisplayName=(get-date)}
                        #$OutageHosts += "$_ @ "(get-date)
                        $Now = Get-Date
                        #$OutageHosts += "$_@$Now"
                        $Now = Get-date
                        if ($notifyonServerDown)
                        {
                            $Body = $_.DisplayName + " has not responded for 5 pings at $Now"
                            Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification `
                            -Subject "Host " + $_.DisplayName + " is down" -SmtpServer $smtpserver
                        }
                    } 
                }
            }
        }
        # Report to screen the details
        Write-Host ""
        Write-Host "Up:"$available.count " Down:"$notavailable.count
        if ($OutageHosts)
        {
            Write-Host "Not available hosts:"
            $OutageHosts 
        }
        Write-Host ""

        #Write-Host "Recycle in $SleepTimeOut seconds..."
        #Start-Sleep -Seconds $SleepTimeOut
        Start-Countdown -Seconds $SleepTimeOut

        # Clear the Screen cuz Mr. Bray likes it neat.
        cls
        if ($OutageHosts.Count -gt $MaxOutageCount)
        {
            # If there are more than a certain number of host down in an hour abort the script.
            $Exit = $True
            $body = $OutageHosts | Out-String
                  
            if ($notifyonMaxOutageCount)
            {
            Send-MailMessage -Body "$body" -to $tonotification -from $fromnotification `
            -Subject "More than $MaxOutageCount Hosts down, monitoring aborted" -SmtpServer $smtpServer
            }
        }
    }
    while ($Exit -ne $True)
}#End     
}#Start-Monitaur


# load the list of computers
$computers = Get-Content Monitaur-List.csv

# QuickEdit mode can cause someone to accidentally pause the script
echo ""
echo "       Please disable"
echo "       QuickEdit Mode"
echo "     before continuing..."
echo ""
echo ""
pause
cls

# call the function
Start-Monitaur -notifySpeech

 

Leave a Reply

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