Wednesday, October 2, 2013

Get-DailyBackupAlerts

#requires -version 2
<#
.SYNOPSIS
Get-DailyBackupAlerts.ps1 - Exchange 2010 Database Backup Alert Script
.DESCRIPTION
Checks the backup timestamps for the servers
and alerts if a database hasn't been backed up
recently
.INPUTS
No inputs required, however you should modify the SMTP options to suit your environment.
.OUTPUTS
Sends an HTML email if databases are detected without
a backup for more than the specified number of hours.
.EXAMPLE
.\Get-DailyBackupAlerts.ps1
Tip: Run as a scheduled task to generate the alerts automatically
.LINK
http://exchangeserverpro.com/set-automated-exchange-2010-database-backup-alert-email
.NOTES
Written By: Paul Cunningham
Website: http://exchangeserverpro.com
Twitter: http://twitter.com/exchservpro
Additional Credits: Chris Brown
Website: http://www.flamingkeys.com
Twitter: http://twitter.com/chrisbrownie
Change Log
V1.00, 10/11/2011 - Initial version
V1.01, 23/10/2012 - Bug fixes and minor improvements
V1.02, 30/10/2012 - Bug fix with alertflag
V1.03, 11/01/2013 - Many code improvements, more comments, archive mailboxes now counted, and added
     option to always send the report regardless of number of alerts.
#>
[CmdletBinding()]
param (
 [Parameter( Mandatory=$false)]
 [switch]$AlwaysSend
 )
#...................................
# Modify these SMTP settings to suit
# your environment
#...................................
$smtpServer = "smtp.exchangeserverpro.net"
$smtpFrom = "exchangeserver@exchangeserverpro.net"
$smtpTo = "administrator@exchangeserverpro.net"

#...................................
# Modify these alert thresholds to
# suit your needs
#...................................
#You can set a different alert threshold for Mondays
#to account for weekend backup schedules
$day = (Get-Date).DayOfWeek
if ($day -eq "Monday")
{
 [int]$threshold = 48
}
else
{
 [int]$threshold = 24
}
Write-Verbose "Threshold for this check is $threshold hours"
#...................................
# If you wish to exclude databases
# from the report add them here
#...................................
#Example: $excludedbs = @("database1","database2")
$excludedbs = @()

#...................................
# Initialization
#...................................
#Add Exchange 2010 snapin if not already loaded
if (!(Get-PSSnapin | where {$_.Name -eq "Microsoft.Exchange.Management.PowerShell.E2010"}))
{
 Write-Verbose "Loading Exchange 2010 Snapin"
 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 -ErrorAction SilentlyContinue
}
Write-Verbose "Initializing variables"
$report = @()
$alertdbs = @()
$okdbs = @()
[bool]$alertflag = $false
#Current time is used in alert calculations
$now = [DateTime]::Now
Write-Verbose "Current date/time is $now"
Write-Verbose "Retrieving list of mailboxes for use in mailbox count later"
$mailboxes = $(Get-Mailbox -IgnoreDefaultScope -ResultSize Unlimited)

#...................................
# Script
#...................................
#Get all Mailbox and Public Folder databases
Write-Verbose "Retrieving database list"
$dbs = @(Get-MailboxDatabase -Status -IncludePreExchange2010 | Where {$_.Recovery -ne $true})
if ($dbs)
{
 Write-Verbose "$($dbs.count) mailbox databases found"
}
else
{
 Write-Verbose "No mailbox databases found"
}
$pfdbs = @(Get-PublicFolderDatabase -Status)
Write-Verbose "$($pfdbs.count) public folder databases found"
if ($pfdbs)
{
 $dbs += $pfdbs
 Write-Verbose "$($dbs.count) total databases found"
}
else
{
 Write-Verbose "No mailbox databases found"
}
#If a list of excluded databases exists, remove them from $dbs
if ($excludedbs)
{
 Write-Verbose "Removing excluded databases from the checks"
 $tempdbs = $dbs
 $dbs = @()
 foreach ($tempdb in $tempdbs)
 {
  if (!($excludedbs -icontains $tempdb))
  {
   Write-Verbose "$tempdb included"
   $dbs = $dbs += $tempdb
  }
  else
  {
   Write-Verbose "$tempdb excluded"
  }
 }
}
#Check each database for most recent backup timestamp
foreach ($db in $dbs)
{
 Write-Verbose "---- Checking $($db.name) ----"
 $lastbackup = @{}
 [int]$ago = $null
 if ( $db.LastFullBackup -eq $null -and $db.LastIncrementalBackup -eq $null)
 {
  #No backup timestamp was present. This means either the database has
  #never been backed up, or it was unreachable when this script ran
  $lastbackup.time = "Never/Unknown"
  $lastbackup.type = "Never/Unknown"
  [string]$ago = "Never/Unknown"
 }
 elseif ( $db.LastFullBackup -lt $db.LastIncrementalBackup )
 {
  #Most recent backup was Incremental
  $lastbackup.time = $db.LastIncrementalBackup
  $lastbackup.type = "Incremental"
  [int]$ago = ($now - $lastbackup.time).TotalHours
  [int]$ago = "{0:N0}" -f $ago
 }
 elseif ( $db.LastIncrementalBackup -lt $db.LastFullBackup )
 {
  #Most recent backup was Full
  $lastbackup.time = $db.LastFullBackup
  $lastbackup.type = "Full"
  [int]$ago = ($now - $lastbackup.time).TotalHours
  [int]$ago = "{0:N0}" -f $ago
 }
 Write-Verbose "Last backup of $($db.name) was $($lastbackup.type) on $($lastbackup.time), $ago hours ago"
 #Determines the database type (Mailbox or Public Folder)
 if ($db.IsMailboxDatabase -eq $true) {$dbtype = "Mailbox"}
 if ($db.IsPublicFolderDatabase -eq $true) {$dbtype = "Public Folder"}
 #Report data is collected into a custom object
 $dbObj = New-Object PSObject
 if ( $dbtype -eq "Public Folder")
 {
  #Exchange 2007/2010 Public Folder databases are only associated with a server
  $dbObj | Add-Member NoteProperty -Name "Server/DAG" -Value $db.Server
  [string]$mbcount = "n/a"
 }
 else
 {
  #Exchange 2007/2010 Mailbox databases can be associated with a server or DAG
  if ($db.MasterServerOrAvailabilityGroup)
  {
   $dbObj | Add-Member NoteProperty -Name "Server/DAG" -Value $db.MasterServerOrAvailabilityGroup
  }
  else
  {
   $dbObj | Add-Member NoteProperty -Name "Server/DAG" -Value $db.ServerName
  }
 
  #Mailbox count calculated for Mailbox Databases, including Exchange 2010 Archive mailboxes
  [int]$mbcount = 0
  [int]$mbcount = ($mailboxes | Where-Object {$_.Database -eq $($db.name)}).count
        [int]$archivecount = 0
        [int]$archivecount = ($mailboxes | Where-Object {$_.ArchiveDatabase -eq $($db.name)}).count
        [int]$mbcount = $mbcount + $archivecount
 }

 $dbObj | Add-Member NoteProperty -Name "Database" -Value $db.name
 $dbObj | Add-Member NoteProperty -Name "Database Type" -Value $dbtype

 #Check last backup time against alert threshold and set report status accordingly
 if ( $ago -gt $threshold -or $ago -eq "Never")
 {
  $dbObj | Add-Member NoteProperty -Name "Status" -Value "Alert"
  [bool]$alertflag = $true
  Write-Verbose "Alert flag is $alertflag"
 }
 else
 {
  $dbObj | Add-Member NoteProperty -Name "Status" -Value "OK"
 }

 $dbObj | Add-Member NoteProperty -Name "Mailboxes" -Value $mbcount
 $dbObj | Add-Member NoteProperty -Name "Last Backup Type" -Value $lastbackup.type
 $dbObj | Add-Member NoteProperty -Name "Hrs Ago" -Value $ago
 $dbObj | Add-Member NoteProperty -Name "Time Stamp" -Value $lastbackup.time
 $dbObj | Add-Member NoteProperty -Name "Currently Running" -Value $db.backupinprogress
 #Add the custom object to the report
 $report = $report += $dbObj
}
#All databases have now been checked
Write-Verbose "$($report.count) total databases checked"
$alertdbs = @($report | Where-Object {$_.Status -eq "Alert"})
$okdbs = @($report | Where-Object {$_.Status -eq "OK"})
#Send the email if there is at least one alert, or if -AlwaysSend is set
if (($alertflag -and $alertdbs) -or ($alwayssend))
{
 Write-Verbose "Alert email will be sent"

 #HTML styles for nice formatting of the email
 $style = "<style>BODY{font-family: Arial; font-size: 10pt;}"
 $style = $style + "TABLE{border: 1px solid black; border-collapse: collapse;}"
 $style = $style + "TH{border: 1px solid black; background: #333333; padding: 5px; color: #FFFFFF;}"
 $style = $style + "TD{border: 1px solid black; padding: 5px; }"
 $style = $style + "</style>"
 #Summarise databases with Alert status
 if ($alertdbs)
 {
  $totalalerts = $alertdbs.count
  $alertintro = "The following databases have not been backed up in the last $threshold hours.<BR><BR>"
  $alerthtml = $alertdbs | ConvertTo-Html -Fragment
 }
 else
 {
  $totalalerts = 0
 }
 #Summarise databases with OK status
 if ($okdbs)
 {
  Switch ($totalalerts) {
   0 { $okintro = "The following databases have been backed up in the last $threshold hours.<BR><BR>" }
   default { $okintro = "<BR><BR>The following databases have been backed up in the last $threshold hours.<BR><BR>" }
  }
  $okhtml = $okdbs | ConvertTo-Html -Fragment
 }
 else
 {
  $okintro = "<BR><BRThere are no databases that have been backed up in the last $threshold hours.<BR><BR>"
 }
 Write-Verbose "Report summary: Alerts $totalalerts, OK $($okdbs.count)"
 #Set some additional content for the email report
 $intro = "This is the Exchange database backup status for the last $threshold hours.<BR><BR>"
 Switch ($totalalerts)
 {
  1 {
   $messageSubject = "Daily Check - Exchange Database Backups ($totalalerts alert)"
   $summary = "There is <strong>$totalalerts</strong> database backup alert today.<BR><BR>"
  }
  default {
   $messageSubject = "Daily Check - Exchange Database Backups ($totalalerts alerts)"
   $summary = "There are <strong>$totalalerts</strong> database backup alerts today.<BR><BR>"
  }
 }

 $outro = "<BR><BR>You can place your own instructional text here or perhaps a link to your procedure for responding to backup alerts."
 #Get ready to send email message
 $message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
 $message.Subject = $messageSubject
 $message.IsBodyHTML = $true
 $message.Body = ConvertTo-Html -Body "$intro $summary $alertintro $alerthtml $okintro $okhtml $outro" -Head $style
 #Send email message
 Write-Verbose "Sending email report"
 $smtp = New-Object Net.Mail.SmtpClient($smtpServer)
 $smtp.Send($message)
}
Write-Verbose "Finished."