Showing posts with label monitoring. Show all posts
Showing posts with label monitoring. Show all posts

Thursday, 6 May 2021

Monitoring Hyper-V to Azure Site Replication with Powershell

Here is a simple script that I use to monitor the replication state of Hyper-V servers that I have replicated to Azure using Site Replication.

I have this script run every morning as a scheduled task which sends me an email with a nicely formatted table showing the replication status of all of my Hyper-V VM's to Azure

I have 4 Hyper-V nodes in a cluster - I have specified the 4 node names individually and used a ForEach loop to cycle through them all the generate the report. You can remove the loop if you're only using this for a single server/host.

(You will need to have the Hyper-V powershell module/cmdlets installed for this to work)

import-module Hyper-V -RequiredVersion 1.1
$repstate = @()
$date = get-date -f "dd-MM-yyyy"
$clusternodes = "node1", "node2", "node3", "node4"

foreach ($node in $clusternodes)
    {
    $repstate += get-vmreplication -computername $node | select Name, State, Health, Mode, FrequencySec, PrimaryServer, ReplicaServer, PrimaryServerName, LastReplicationTime
    }

You can then export the values from the $repstate variable to a CSV file and attach it, or as I have done, convert it to HTML and set it to the body of an email message

$mailmessage.Body = $repstate | ConvertTo-HTML -fragment



Friday, 14 October 2016

Windows Server Health Check Script - Powershell

Here is a script that I have developed to run a "health check" on a windows server. The script can be run on demand, but I tend to run them from Windows Task Scheduler with a trigger to run on windows startup to automatically check some of the key things within a windows server. At a high level, here is what is contained within the script;
  • A report of all services with a start mode of "Automatic" but are not in a "running" state
  • Any events in the windows Application log from the past 24 hours that are not categorized as "informational"
  • Any events in the windows System log from the past 24 hours that are not categorized as "informational"
  • A size report of all logical disks in the server showing size, and free space in GB and as a percentage
  • Average CPU load on server
  • Current memory usage (as a percentage)
All this information is retrieved by the script and stored in a .html file. At the end of the script the .html file is saved to disk and sent as an email attachment.

A few other points to note;
  • The start-sleep command is used at the start of the script to delay the script running for 300 seconds (5 minutes). If the script runs as soon as it is triggered on windows startup, not all windows components are ready/loaded and the results of the script are incomplete
  • The log file is saved by default into C:\Admin\ServerHealthChecks with a filename based on the target IP/hostname and date/time the script runs. This can be changed using the $logfile variable. Make sure the path specified in $logfile exists otherwise the .html file will not save correctly
  • If a disk has 10-20% free space, it's text will be orange in colour. Less than 10% will be red. Otherwise it will be green
  • Likewise, if CPU usage is 80-90% it will be orange. 90-100% will be red
  • If free memory is 10-20%, it will be orange. Less than 10% will be red
  • Be sure to update the variables at the end of the script (under #Send Log File via Email) to work with your email system/environment
Powershell script/code is below;

#Server Health Checker
#Author: Peter Morrissey

start-sleep 300

$target = "127.0.0.1"

#Get Last Bootup Time
$osprops = get-wmiobject -class win32_operatingsystem 
$lastboot = $osprops.ConvertToDateTime($osprops.LastBootUpTime)

#Log File
$error.clear()
$fulldate = Get-Date
$logfile = "c:\" + "Admin\" + "ServerHealthChecks\" + $FullDate.ToString("yyyyMMddHHmm") + "-" + "$Target" + ".html"
$global:strname = $env:username
$global:html = @()
$html += "<html>"
$html += '<font face="courier">'
$html += "<title>Server Health Check - $Target</title>"
$html += "<h3>$FullDate</h3>"
$html += "<h4>Server Health Check Script - Run by $global:strname</h4>"
$html += "<h4>Target Server: $Target</h4>"
$html += "<h4>Hostname: $env:computername</h4>"
$html += "<h4>Last Boot: $lastboot </h4>"
$html += "<p>"
$html += "******************************************************************************************* <p>"

#Services Check
write-host "Getting Service Information"
$html += '<u>Service Report - Services with StartMode "Auto" and State not currently "Running" </u><p>'

$servicelist = invoke-command -computername $target {
        get-wmiobject -class Win32_Service | Where {$_.StartMode -eq "Auto" -and $_.State -ne "Running"} | Select DisplayName, Name, StartMode, State
        }
if ($servicelist){$temphtml = $servicelist | Select DisplayName, Name, StartMode, State | ConvertTo-HTML -fragment
                    ForEach ($line in $temphtml){$html += "$Line"} }
ELSE {write-host "All automatic services are running";$html += "All automatic services are running <p>"}

$html += "<p> ******************************************************************************************* <p>"

#EventLog Checks
write-host "Querying Application Log on $Target"
$appeventlog = invoke-command -computername $target {
        $targetDate = Get-Date
        $targetdate = $targetdate.adddays(-1)
        get-eventlog -logname "Application" -after $targetdate | where-object {$_.entrytype -ne "Information" -and $_.Source -ne "Print" -and $_.Source -ne "TermServDevices"} 
        }

if ($appeventlog){#write-host $AppEventLog
                  $html += "<u>Application Log Non-Information Events (Last 24 Hours)</u><p>" 
                  $temphtml = $appeventlog |  Select TimeGenerated, EntryType, Source, Message | ConvertTo-HTML -fragment
                  foreach ($line in $temphtml){$html += "$line"}
                  }
                  
ELSE {$html += "No non-informational events found in application log in past 24 hours <p>"}

$html += "<p>*******************************************************************************************<p>"

write-host "Querying System Log on $Target"

$syseventlog = invoke-command -computername $Target {
        $targetdate = get-date
        $targetdate = $targetdate.adddays(-1)
        get-eventlog -logname "System" -after $targetdate | where-object {$_.entrytype -ne "Information" -and $_.source -ne "Print" -and $_.source -ne "TermServDevices"} 
        }

if ($syseventlog){#write-host $syseventlog
                  $html += "<u>System Log Non-Information Events (Last 24 Hours)</u><p>"
                  $temphtml = $syseventlog | Select TimeGenerated, EntryType, Source, Message | ConvertTo-HTML -fragment
                  ForEach ($line in $temphtml){$html += "$Line"}
                  }
                  
else {$html += "No non-informational events found in system log in past 24 hours<p>"}

$html += "<p>*******************************************************************************************<p>"

#Local Disk Health Check
write-host "Getting logical disk information"
$diskreport = invoke-command -computername $target {
    Get-WmiObject Win32_logicaldisk | Select DeviceID, MediaType, VolumeName, `
    @{Name="Size(GB)";Expression={[decimal]("{0:N0}" -f($_.size/1gb))}}, `
    @{Name="Free Space(GB)";Expression={[decimal]("{0:N0}" -f($_.freespace/1gb))}}, `
    @{Name="Free (%)";Expression={"{0,6:P0}" -f(($_.freespace/1gb) / ($_.size/1gb))}} `
    }

$html += "<u>Logical Disk Report</u><p>"
$temphtml = $DiskReport | Select DeviceID, VolumeName, "Size(GB)", "Free Space(GB)", "Free (%)" | ConvertTo-HTML -fragment
foreach ($Line in $temphtml)
    {
    if ($line -like "*%<*")
        {
        $lineindex = [array]::IndexOf($temphtml, $line)
        $templine = $line -split "%"
        $templine = $templine -replace(" ","")
        $templine = $templine[0] -split "<td>"
        $templine = $templine[5]
        $templine = $templine.trim()
        $templine = [decimal]$templine
               
        if ($templine -le 20 -and $templine -ge 10){$temphtml[$lineindex] = $temphtml[$lineindex].Replace("<td>",'<td><font color="orange">')}
        if ($templine -lt 10 ){$temphtml[$lineindex] = $temphtml[$lineindex].Replace("<td>",'<td><font color="red">');
                                $smtpclient = new-object system.net.mail.smtpClient 
                                $mailmessage = New-Object system.net.mail.mailmessage 
                                $smtpclient.Host = "192.168.0.5" 
                                $mailmessage.from = ("alerts@mitx.dynu.com") 
                                $mailmessage.To.add("peter@mitx.dynu.com")
                                $mailmessage.Subject = “Server Health Check Alert - Disk Space Low - $target"
                                $mailmessage.Body = "Low disk space found on $target -  $temphtml"
                                $mailmessage.IsBodyHtml = $true
                                $smtpclient.Send($mailmessage)
                                $mailmessage.dispose()
                              }
        }
    }

foreach ($line in $temphtml){$html += "$line"}

$html += "<p>*******************************************************************************************<p>"

#Check CPU Load
write-host "Getting CPU Stats"
$cpudata = get-wmiobject win32_processor -computername $target | measure-object -property LoadPercentage -average | select Average
$html += "<u>CPU Load (Average)</u><p>"
$cpuusage = $($cpudata.average)
    if ($cpuusage -ge 80 -and $cpuusage -le 90){$html += '<font color="orange">' + "Average CPU Load: $cpuusage%" + '</font>'}
    elseif ($cpuusage -gt 90){$html += '<font color="red">' + "Average CPU Load: $cpuusage%" + '</font>'}
    elseif ($cpuusage -lt 80){$html += '<font color = "green">' + "Average CPU Load: $cpuusage%" + '</font>'}
$html += "<p>*******************************************************************************************<p>"

#Check Memory Usage
write-host "Getting Memory Usage"
$html += "<u>Memory Usage Report</u><p>"
$memdata = get-wmiobject win32_operatingsystem -computername $target | select FreePhysicalMemory, FreeVirtualMemory, TotalVirtualMemorySize, TotalVisibleMemorySize
$freephysicalmem = $($memdata.freephysicalmemory)
$freevirtualmem = $($memdata.freevirtualmemory)
$totalvirtualmem = $($memdata.totalvirtualmemorysize)
$totalvisiblemem = $($memdata.totalvisiblememorysize)
$memusage = ($totalvisiblemem - $freephysicalmem) / $totalvisiblemem * 100
$freemem = $freephysicalmem / $totalvisiblemem * 100
[decimal]$freemem = "{0:N0}" -f $freemem

if ($freemem -lt 10){$html += '<font color="red">' + "Free Memory (%): $freemem" + '</font>'}
elseif ($freemem -gt 10 -and $freemem -le 20){$html += '<font color="orange">' + "Free Memory (%): $freemem" + '</font>'}
elseif ($freemem -gt 20){$html += '<font color="green">' + "Free Memory (%): $freemem" + '</font>'}
$html += "<p>*******************************************************************************************<p>"

#Export Log File
$html | out-file $LogFile

#Open Log File in Internet Explorer
#Invoke-Item $Logfile

#Send Log File via Email
$smtpclient = new-object system.net.mail.smtpClient 
$mailmessage = New-Object system.net.mail.mailmessage 
$smtpclient.Host = "mail.server.com" 
$mailmessage.from = ("user@domain.com") 
$mailmessage.To.add("user@domain.com")
$mailmessage.Subject = “Server Health Check Results - $target"
$mailmessage.Body = "Server Health Check Results for $target"
$mailmessage.Attachments.Add($LogFile)
$mailmessage.IsBodyHtml = $true
$smtpclient.Send($mailmessage)
$mailmessage.dispose()

Tuesday, 20 September 2016

IP Address/Ping Monitoring using Powershell

Here is a script I developed to monitor/check the online availability of an IP address using powershell. I've set this script to run every 10 minutes to check that a device on my network (WDTV Live) is online as it had a tendency to go offline occasionally.

$ip = the IP address you wish to monitor
$ping = utilises the test-connection cmdlet to attempt to contact the IP address (specified in $ip) using ICMP (ping) packets

If the ping test does not return a result (ie. the IP address does not respond), a "flag" is placed in the form of a file called "WDTV.Offline". This flag file is used/checked by the script to prevent repeat notifications being sent after the first notification that the device is offline (or is back online after previously being offline)

If the ping test does return a result (ie. the IP address responded to the ping request), the script checks to see if it was previously offline (by looking for the flag file mentioned previously). If the flag file is found, another notification is sent advising the device is now back online, and the flag file is deleted. This means the next time the script runs, if the device is still online and no flag file is present, no further notifications are sent


$ip = "192.168.0.150"
$ping = test-connection $ip -count 2

if (!$ping)
    {
    $flagtest = test-path "D:\Powershell\WDTV.Offline"
    if ($flagtest -eq $false)
        {
        new-item "D:\Powershell\WDTV.Offline" -type file
        $from = "user@gmail.com"
        $to = "user@gmail.com"
        $subject = "$ip Offline"
        $body = "$ip not responding to ping"
        $smtp = New-Object System.Net.Mail.SmtpClient("smtp.gmail.com", "587");
        # Uncomment the below row if your ISP´s outgoing mail server requires authentication.
        $smtp.EnableSSL = $true
        $smtp.Credentials = New-Object System.Net.NetworkCredential("username", "password");
        $smtp.Send($From, $To, $subject, $body);
        #write-host "Mail Sent"
        }
    }

elseif ($ping)
    {
    $flagtest = test-path "D:\Powershell\WDTV.Offline"
    if ($flagtest -eq $true)
        {
        remove-item "D:\Powershell\WDTV.Offline"
        $from = "user@gmail.com"
        $to = "user@gmail.com"
        $subject = "$ip Online"
        $body = "$ip now responding to ping"
        $smtp = New-Object System.Net.Mail.SmtpClient("smtp.gmail.com", "587");
        # Uncomment the below row if your ISP´s outgoing mail server requires authentication.
        $smtp.EnableSSL = $true
        $smtp.Credentials = New-Object System.Net.NetworkCredential("username", "password");
        $smtp.Send($From, $To, $subject, $body);
        #write-host "Mail Sent"
        }
    }

You will need to update the $from, $to and username/password fields within the $smtp.credentials variables in order for the email notifications to work, as well as the values for outgoing mail server and port within the System.Net.Mail.SmtpClient

You can refer to my previous blog post Here which contains some more information about sending SMTP emails using authentication with powershell

Friday, 28 March 2014

Check/Monitor Website URL with Powershell

Windows Powershell has a built in web request module that you can use to test/check the availability of website URL(s).

The full script to do this is below;

[string] $url = "http://www.google.com"
[net.httpWebRequest] $req = [net.webRequest]::create($url)
$req.method = "HEAD"
[net.httpWebResponse] $res = $req.getresponse()

The first line is self explanatory - simply put in the full URL for the website you wish to check. In this example we will be checking http://www.google.com

[string] $url = "http://www.google.com"

The second line is where we create the actual web request instance in Powershell - under the $req variable

[net.httpWebRequest] $req = [net.webRequest]::create($url)

On the third line, we change the request method to be "HEAD" which means that only header information for the requested URL is retrieved. This speeds up the web request process as it does not pull all the data from the web URL - only the header information. If you leave this line out, the default method is "GET".

$req.method = "HEAD"

On the last line we actually submit the web request, and store the response in the $res variable

[net.httpWebResponse] $res = $req.getresponse()

For a successful web request, the response should look something like this:

IsMutuallyAuthenticated : False
Cookies                 : {}
Headers                 : {Cache-Control, Content-Type, Date, Expires...}
ContentLength           : -1
ContentEncoding         : 
ContentType             : text/html; charset=ISO-8859-1
CharacterSet            : ISO-8859-1
Server                  : gws
LastModified            : 28/03/2014 10:24:19 AM
StatusCode              : OK
StatusDescription       : OK
ProtocolVersion         : 1.1
ResponseUri             : http://www.google.com.au/?gfe_rd=cr&ei=I7M0U5P_BenC8gfRvIHADg
Method                  : HEAD
IsFromCache             : False

Based on this, you could check the statuscode or statusdescription properties of $res to make sure the value matches "OK".

Alternatively, if you enter an invalid URL for a website that doesn't exist, you will get an error, and the value of $res will be $null. The error you get will be something along the lines of;

Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (502) Bad Gateway."
At line:4 char:46
+ [net.httpWebResponse] $res = $req.getresponse <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException