Skip to content

Instantly share code, notes, and snippets.

@virgilwashere
Forked from smasterson/MyVeeamReport.ps1
Last active April 1, 2020 10:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save virgilwashere/964c81b9bb49792dceb8 to your computer and use it in GitHub Desktop.
Save virgilwashere/964c81b9bb49792dceb8 to your computer and use it in GitHub Desktop.
Veeam Backup Report
#Requires -Version 4
<#
.Notes
NAME: MyVeeamReport.ps1
AUTHOR: Virgil@endless.net.au
CREATED: 22/02/2014
LASTEDIT: 13/01/2016
.Synopsis
Veeam Backup Report
.Description
Detailed backup report of Veeam Backup sessions
.Inputs
None
.Outputs
Email
.Example
MyVeeamReport.ps1 -reportMode Daily -sendEmail -onlyLastBk
Report on the last 24 hours of backups. This is suitable for scheduled tasks.
.Example
MyVeeamReport.ps1 -reportMode Weekly -sendEmail -saveFile -To virgil@endless.net.au
Report on the last week of backups. Send email and save file locally.
.LINK
Online Version: https://gist.github.com/virgilwashere/964c81b9bb49792dceb8
Original Version: https://gist.github.com/smasterson/9136468#file-myveeamreport-ps1
#>
#region paramblock
[CmdletBinding(HelpUri = 'http://blog.smasterson.com/2015/11/16/veeam-v8-my-veeam-report-turns-2-0/')]
Param
(
#A number of hours (24, 48, 66, 72) or Daily, WeekEnd, Weekly, Monthly
[Parameter(Mandatory=$True,Position=0)]
[ValidateSet(24, 48, 66, 72, 'Daily', 'WeekEnd', 'Weekly', 'Monthly')]
# [ValidateNotNullOrEmpty()]
[String]
$reportMode = 24,
#Send HTML report as body of an email message.
[Parameter(Position=1,HelpMessage='Send report via email')]
[Alias('Email')]
[Switch]
$sendEmail = $False,
# Save output to a file - $True or $False
[Parameter(Position=2)]
[Switch]
$saveFile = $False,
# Only show last session for each Backup Job
[Parameter(Position=3)]
[Switch]
$onlyLastBk = $False,
#The email address who will receive the report.
[Parameter(Position=4,HelpMessage='Please specify the email address of the recipient')]
[ValidateScript({ If ($_ -match "\w+@\w+\.\w+") { $True } else { Throw "$_ is not a valid e-mail address " }})]
[ValidateNotNullOrEmpty()]
[Alias('To')]
[String]
$EmailTo = 'you@youremail.com',
# Report Title
[Parameter()]
[ValidateNotNullOrEmpty()]
[String]
$rptTitle = "My Veeam Backup Report",
# Show Backup Session Summary
[Parameter()]
[Switch]
$showSummaryBk = $True,
# Show Backup Job Status
[Parameter()]
[Switch]
$showStatusJobsBk = $True,
# Show Running Backup jobs
[Parameter()]
[Switch]
$showRunningBk = $True,
# Show detailed information for Backup Jobs/Sessions (Avg Speed, Total(GB), Processed(GB), Read(GB), Transferred(GB))
[Parameter()]
[Switch]
$showDetailedBk = $True,
# Show Backup Sessions w/Warnings or Failures within time frame ($reportMode)
[Parameter()]
[Switch]
$showWarnFailBk = $True,
# Show Successful Backup Sessions within time frame ($reportMode)
[Parameter()]
[Switch]
$showSuccessBk = $True,
# Show VMs with no successful backups within time frame ($reportMode)
[Parameter()]
[Switch]
$showUnprotectedVMs = $True,
# Show VMs with successful backups within time frame ($reportMode)
[Parameter()]
[Switch]
$showProtectedVMs = $False,
# Show Running Restore VM Sessions within time frame ($reportMode)
[Parameter()]
[Switch]
$showRestoreRunVM = $True,
# Show Completed Restore VM Sessions within time frame ($reportMode)
[Parameter()]
[Switch]
$showRestoreVM = $True,
# Show Repository Info
[Parameter()]
[Switch]
$showRepo = $True,
# Show Proxy Info
[Parameter()]
[Switch]
$showProxy = $True,
# Show Replica Target Info
[Parameter()]
[Switch]
$showReplica = $False,
# Show Veeam Services Info (Windows Services)
[Parameter()]
[Switch]
$showServices = $False,
# Show License expiry info
[Parameter()]
[Switch]
$showLicExp = $True,
#The email address of the sender.
[Parameter(HelpMessage='Please specify the email address of the sender')]
[ValidateScript({ If ($_ -match "\w+@\w+\.\w+") { $True } else { Throw "$_ is not a valid e-mail address " }})]
[ValidateNotNullOrEmpty()]
[Alias('From')]
[String]
$EmailFrom = 'MyVeeamReport@yourdoamin.com',
#SMTP relay host.
[Parameter(HelpMessage='Please specify the SMTP server address')]
[ValidateNotNullOrEmpty()]
[Alias('Relay','SMTP')]
[String]
$EmailHost = 'smtp.yourserver.com',
# Launch file after creation - $True or $False
[Parameter()]
[Switch]
$launchFile = $saveFile,
# vCenter server(s) - Must be the same as seen in VBR server
[Parameter()]
[ValidateNotNullOrEmpty()]
[String[]]
$vcenters = 'yourvcenter.name',
# To Exclude VMs from Missing and Successful Backups section add VM names to be excluded
[Parameter()]
[ValidateNotNull()]
[String[]]
$ExcludeVMs = @(''),
# Exclude VMs from Missing and Successful Backups section in the following (vCenter) folder(s)
[Parameter()]
[ValidateNotNull()]
[String[]]
$ExcludeFolder = @('')
)
#endregion
Add-PSSnapin "VeeamPSSnapIn" -ErrorAction SilentlyContinue
#region User-Variables
# Append Report Mode to Report Title E.g. My Veeam Report (Last 24 Hours)
$fullTitle = $True
# Include HourstoCheck in report table headings
$reportTableHours = $True
# Report Width in Pixels
$rptWidth = 1024
# Location of Veeam executable (Veeam.Backup.Shell.exe)
$veeamExePath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Shell.exe"
# Location of common dll - Needed for repository function - Get-vPCRepoInfo (Veeam.Backup.Core.dll)
$veeamDllPath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Core.dll"
# To Exclude VMs from Missing and Successful Backups section add VM names to be excluded
# $excludevms = @("vm1","vm2","*_replica")
$excludeVMsdefault = @("Archived*","TMPL*","TEST*")
$ExcludeVMs = $excludeVMsdefault + $ExcludeVMs
# Exclude VMs from Missing and Successful Backups section in the following (vCenter) folder(s)
# $excludeFolder = = @("folder1","folder2","*_testonly")
$excludeFolderdefault = @("Templates","Archived Servers","Pending Deletion")
$excludeFolder = $excludeVMsdefault + $excludeFolder
# Exclude VMs from Missing and Successful Backups section in the following (vCenter) datacenter(s)
# $excludeDC = = @("dc1","dc2","dc*")
$excludeDC = @("")
# Use Veeam SQL Database for Repository info
$repoSQL = $False
# Show only Services that are NOT running
$hideRunningSvc = $False
# File output path and filename
$outFile = "$env:TEMP\MyVeeamReport_$(Get-Date -format MMddyyyy_hhmmss).htm"
# Email configuration
$emailUser = ""
$emailPass = ""
# Send report as attachment - $True or $False
$emailAttach = $False
# Email Subject
$emailSubject = $rptTitle
# Append Report Mode to Email Subject E.g. My Veeam Report (Last 24 Hours)
$fullSubject = $fullTitle
# Highlighting Thresholds
# Repository Free Space Remaining %
$repoCritical = 10
$repoWarn = 20
# Replica Target Free Space Remaining %
$replicaCritical = 10
$replicaWarn = 20
# License Days Remaining
$licenseCritical = 30
$licenseWarn = 90
# Show Endpoint Backup Session Summary
$showSummaryEp = $False
# Show Endpoint Backup Job Status
$showJobsEp = $False
# Show Running Endpoint Backup jobs
$showRunningEp = $False
# Only show last session for each Endpoint Backup Job
$onlyLastEp = $False
# Show Endpoint Backup Sessions w/Warnings or Failures within time frame ($reportMode)
$showWarnFailEp = $False
# Show Successful Endpoint Backup Sessions within time frame ($reportMode)
$showSuccessEp = $False
#endregion
#region VersionInfo
$MVRversion = "2.1"
#
# Version 2.1 - V
# Added $HourstoCheck to table names for clarity
# Moved most of the switches into proper parameters
# Some ForEach optimisations
# Added -WarningAction SilentlyContinue to all Veeam commands to remove the silly PowerShell 2.0 warning
# Script-Analyzer said to remove the plural nouns and aliases
#
# Version 2.0 - SM
# Misc minor tweaks/cleanup
# Proxy host IP info now always returns IPv4 address
# Added ability to query Veeam database for Repository size info
# Big thanks to tsightler - http://forums.veeam.com/powershell-f26/get-vbrbackuprepository-why-no-size-info-t27296.html
# Added report section - Backup Job Status
# Added option to show detailed Backup Job/Session information (Avg Speed, Total(GB), Processed(GB), Read(GB), Transferred(GB))
# Added report section - Running VM Restore Sessions
# Added report section - Completed VM Restore Sessions
# Added report section - Endpoint Backup Results Summary
# Added report section - Endpoint Backup Job Status
# Added report section - Running Endpoint Backup Jobs
# Added report section - Endpoint Backup Jobs/Sessions with Warnings or Failures
# Added report section - Successful Endpoint Backup Jobs/Sessions
#
# Version 1.4.1 - SM
# Fixed issue with summary counts
# Version 1.4 - SM
# Misc minor tweaks/cleanup
# Added variable for report width
# Added variable for email subject
# Added ability to show/hide all report sections
# Added Protected/Unprotected VM Count to Summary
# Added per object details for sessions w/no details
# Added proxy host name to Proxy Details
# Added repository host name to Repository Details
# Added section showing successful sessions
# Added ability to view only last session per job
# Added Cluster field for protected/unprotected VMs
# Added catch for cifs repositories greater than 4TB as erroneous data is returned
# Added % Complete for Running Jobs
# Added ability to exclude multiple (vCenter) folders from Missing and Successful Backups section
# Added ability to exclude multiple (vCenter) datacenters from Missing and Successful Backups section
# Tweaked license info for better reporting across different date formats
#
# Version 1.3 - SM
# Now supports VBR v8
# For VBR v7, use report version 1.2
# Added more flexible options to save and launch file
#
# Version 1.2 - SM
# Added option to show VMs Successfully backed up
#
# Version 1.1.4 - SM
# Misc tweaks/bug fixes
# Reconfigured HTML a bit to help with certain email clients
# Added cell coloring to highlight status
# Added $rptTitle variable to hold report title
# Added ability to send report via email as attachment
#
# Version 1.1.3 - SM
# Added Details to Sessions with Warnings or Failures
#
# Version 1.1.2 - SM
# Minor tweaks/updates
# Added Veeam version info to header
#
# Version 1.1.1 - Shawn Masterson
# Based on vPowerCLI v6 Army Report (v1.1) by Thomas McConnell
# http://www.vpowercli.co.uk/2012/01/23/vpowercli-v6-army-report/
# http://pastebin.com/6p3LrWt7
#
# Tweaked HTML header (color, title)
#
# Changed report width to 1024px
#
# Moved hard-coded path to exe/dll files to user declared variables ($veeamExePath/$veeamDllPath)
#
# Adjusted sorting on all objects
#
# Modified info group/counts
# Modified - Total Jobs = Job Runs
# Added - Read (GB)
# Added - Transferred (GB)
# Modified - Warning = Warnings
# Modified - Failed = Failures
# Added - Failed (last session)
# Added - Running (currently running sessions)
#
# Modified job lines
# Renamed Header - Sessions with Warnings or Failures
# Fixed Write (GB) - Broke with v7
#
# Added support license renewal
# Credit - Gavin Townsend http://www.theagreeablecow.com/2012/09/sysadmin-modular-reporting-samreports.html
# Original Credit - Arne Fokkema http://ict-freak.nl/2011/12/29/powershell-veeam-br-get-total-days-before-the-license-expires/
#
# Modified Proxy section
# Removed Read/Write/Util - Broke in v7 - Workaround unknown
#
# Modified Services section
# Added - $runningSvc variable to toggle displaying services that are running
# Added - Ability to hide section if no results returned (all services are running)
# Added - Scans proxies and repositories as well as the VBR server for services
#
# Added VMs Not Backed Up section
# Credit - Tom Sightler - http://sightunseen.org/blog/?p=1
# http://www.sightunseen.org/files/vm_backup_status_dev.ps1
#
# Modified $reportMode
# Added ability to run with any number of hours (8,12,72 etc)
# Added bits to allow for zero sessions (semi-gracefully)
#
# Added Running Jobs section
# Added ability to toggle displaying running jobs
#
# Added catch to ensure running v7 or greater
#
#
# Version 1.1
# Added job lines as per a request on the website
#
# Version 1.0
# Clean up for release
#
# Version 0.9
# More cmdlet rewrite to improve perfomace, credit to @SethBartlett
# for practically writing the Get-vPCRepoInfo
#
# Version 0.8
# Added Read/Write stats for proxies at requests of @bsousapt
# Performance improvement of proxy tear down due to rewrite of cmdlet
# Replaced 2 other functions
# Added Warning counter, .00 to all storage returns and fetch credentials for
# remote WinLocal repos
#
# Version 0.7
# Added Utilisation(Get-vPCDailyProxyUsage) and Modes 24, 48, Weekly, and Monthly
# Minor performance tweaks
#endregion
#region NonUser-Variables
# Get the VBR Server
$vbrServer = Get-VBRLocalHost -WarningAction SilentlyContinue
# Get all Proxies
$viProxyList = Get-VBRViProxy -WarningAction SilentlyContinue
# Get all Repositories
$repoList = Get-VBRBackupRepository -WarningAction SilentlyContinue
# Get all Sessions (Backup/BackupCopy/Replica)
$allSesh = Get-VBRBackupSession -WarningAction SilentlyContinue
# Get all Restore Sessions
$allResto = Get-VBRRestoreSession -WarningAction SilentlyContinue
# Convert mode (timeframe) to hours
Switch ($reportMode) {
'Monthly' {
[int]$HourstoCheck = 720
Break
}
'Weekly' {
[int]$HourstoCheck = 168
Break
}
'WeekEnd' {
[int]$HourstoCheck = 66
Break
}
'Daily' {
[int]$HourstoCheck = 24
Break
}
default {
[int]$HourstoCheck = $reportMode
Break
}
}
If ($showStatusJobsBk -or $showSummaryBk) {
# Gather Backup jobs
$allJobsBk = Get-VBRJob -WarningAction SilentlyContinue | ? {$_.JobType -eq "Backup"}
# Gather all Backup sessions within timeframe
$seshListBk = $allSesh | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)) -and $_.JobType -eq "Backup"}
# Get Backup session information
$totalxferBk = 0
$totalReadBk = 0
$seshListBk | %{
$totalxferBk += $([Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2))
$totalReadBk += $([Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2))
}
If ($onlyLastBk) {
$tempSeshListBk = $seshListBk
$seshListBk = @()
Foreach($job in $allJobsBk) {
$seshListBk += $TempSeshListBk | ?{$_.Jobname -eq $job.name} | Sort-Object CreationTime -Descending | Select-Object -First 1
}
}
$successSessionsBk = @($seshListBk | ?{$_.Result -eq "Success"})
$warningSessionsBk = @($seshListBk | ?{$_.Result -eq "Warning"})
$failsSessionsBk = @($seshListBk | ?{$_.Result -eq "Failed"})
$runningSessionsBk = @($allSesh | ?{$_.State -eq "Working" -and $_.JobType -eq "Backup"})
$failedSessionsBk = @($seshListBk | ?{($_.Result -eq "Failed") -and ($_.WillBeRetried -ne "True")})
If ($showRestoreVM) {
# Gather VM Restore sessions within timeframe
$seshListResto = $allResto | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck))}
# Get VM Restore session information
$completeResto = $seshListResto | ?{$_.IsCompleted}
$runningResto = $seshListResto | ?{!($_.IsCompleted)}
}
}
# Gather Endpoint Backup jobs
If ($showSummaryEp) {
$allJobsEp = Get-VBREPJob -WarningAction SilentlyContinue
# Gather all Endpoint Backup sessions within timeframe
$allSeshEp = Get-VBREPSession -WarningAction SilentlyContinue
$seshListEp = $allSeshEp | ?{$_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)}
If ($onlyLastEp) {
$tempSeshListEp = $seshListEp
$seshListEp = @()
Foreach($job in $allJobsEp) {
$seshListEp += $TempSeshListEp | ?{$_.JobId -eq $job.Id} | Sort-Object CreationTime -Descending | Select-Object -First 1
}
}
$successSessionsEp = @($seshListEp | ?{$_.Result -eq "Success"})
$warningSessionsEp = @($seshListEp | ?{$_.Result -eq "Warning"})
$failsSessionsEp = @($seshListEp | ?{$_.Result -eq "Failed"})
$runningSessionsEp = @($allSeshEp | ?{$_.State -eq "Working"})
}
#Get Replica jobs
If ($showReplica) {
$repList = Get-VBRJob -WarningAction SilentlyContinue | ?{$_.IsReplica}
}
$reportName = ('Daily', 'WeekEnd', 'Weekly','Monthly')
# Append Report Mode to Report Title
If ($fullTitle) {
If (-Not $reportMode -In $reportName) {
$rptTitle = "$rptTitle (Last $reportMode Hrs)"
} Else {
$rptTitle = "$rptTitle ($reportMode)"
}
}
# Append Report Mode to Email subject
If ($fullSubject) {
If (-Not $reportMode -In $reportName) {
$emailSubject = "$emailSubject (Last $reportMode Hrs)"
} Else {
$emailSubject = "$emailSubject ($reportMode)"
}
}
#endregion
#region Functions
Function Get-vPCProxyInfo {
$vPCObjAry = @()
Function Build-vPCObj {param ([PsObject]$inputObj)
$ping = new-object system.net.networkinformation.ping
$DNS = [Net.DNS]::GetHostEntry("$($inputObj.Host.RealName)")
$IPv4 = ($DNS.get_AddressList() | Where {$_.AddressFamily -eq "InterNetwork"} | Select-Object -First 1).IPAddressToString
$pinginfo = $ping.send("$($IPv4)")
If ($pinginfo.Status -eq "Success") {
$hostAlive = "Alive"
} Else {
$hostAlive = "Dead"
}
$vPCFuncObject = New-Object PSObject -Property @{
ProxyName = $inputObj.Name
RealName = $inputObj.Host.RealName.ToLower()
Disabled = $inputObj.IsDisabled
Status = $hostAlive
IP = $IPv4
Responce = $pinginfo.RoundtripTime
}
Return $vPCFuncObject
}
Get-VBRViProxy -WarningAction SilentlyContinue | %{$vPCObjAry = $vPCObjAry + $(Build-vPCObj $_)}
$vPCObjAry
}
Function Get-vPCRepoInfo {
[CmdletBinding()]
param (
[Parameter(Position=0, ValueFromPipeline=$True)]
[PSObject[]]$Repository
)
Begin {
$outputAry = @()
[Reflection.Assembly]::LoadFile($veeamDllPath) | Out-Null
Function Build-Object {param($name, $repohost, $path, $free, $total)
$repoObj = New-Object -TypeName PSObject -Property @{
Target = $name
RepoHost = $repohost.ToLower()
Storepath = $path
StorageFree = [Math]::Round([Decimal]$free/1GB,2)
StorageTotal = [Math]::Round([Decimal]$total/1GB,2)
FreePercentage = [Math]::Round(($free/$total)*100)
}
Return $repoObj | Select-Object Target, RepoHost, Storepath, StorageFree, StorageTotal, FreePercentage
}
}
Process {
Foreach ($r in $Repository) {
If ($r.GetType().Name -eq [String]) {
$r = Get-VBRBackupRepository -Name $r -WarningAction SilentlyContinue
}
If ($r.Type -eq "WinLocal") {
$Server = $r.GetHost()
$FileCommander = [Veeam.Backup.Core.CWinFileCommander]::Create($Server.Info)
$storage = $FileCommander.GetDrives([ref]$null) | ?{$_.Name -eq $r.Path.Substring(0,3)}
$outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.FreeSpace $storage.TotalSpace
}
Elseif ($r.Type -eq "LinuxLocal") {
$Server = $r.GetHost()
$FileCommander = new-object Veeam.Backup.Core.CSshFileCommander $server.info
$storage = $FileCommander.FindDirInfo($r.Path)
$outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.FreeSpace $storage.TotalSize
}
Elseif ($r.Type -eq "CifsShare") {
$Server = $r.GetHost()
$fso = New-Object -Com Scripting.FileSystemObject
$folder = $fso.GetFolder($r.Path)
$storage = $folder.Drive
# Catch shares with > 4TB space (not calculated correctly)
If (!($storage.TotalSize) -or $storage.TotalSize -ge 4398046510080){
$outputObj = New-Object -TypeName PSObject -Property @{
Target = $r.Name
RepoHost = $server.RealName.ToLower()
Storepath = $r.Path
StorageFree = "Unknown"
StorageTotal = "Unknown"
FreePercentage = "Unknown"
}
} Else {
$outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.AvailableSpace $storage.TotalSize
}
}
$outputAry += $outputObj
}
}
End {
$outputAry
}
}
Function Get-vPCRepoInfoSQL {
[CmdletBinding()]
param (
[Parameter(Position=0, ValueFromPipeline=$True)]
[PSObject[]]$Repository
)
Begin {
$dbServer = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication').SqlServerName
$dbInstance = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication').SqlInstanceName
$dbName = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication').SqlDatabaseName
$outputAry = @()
Function Build-Object {param($name, $repohost, $path, $free, $total)
$repoObj = New-Object -TypeName PSObject -Property @{
Target = $name
RepoHost = $repohost.ToLower()
Storepath = $path
StorageFree = [Math]::Round([Decimal]$free/1GB,2)
StorageTotal = [Math]::Round([Decimal]$total/1GB,2)
FreePercentage = [Math]::Round(($free/$total)*100)
}
Return $repoObj | Select-Object Target, RepoHost, Storepath, StorageFree, StorageTotal, FreePercentage
}
}
Process {
Foreach ($r in $Repository) {
If ($r.GetType().Name -eq [String]) {
$r = Get-VBRBackupRepository -Name $r -WarningAction SilentlyContinue
}
$Server = $r.GetHost()
# Force refresh of free space in repo
[Veeam.Backup.Core.CBackupRepositoryEx]::SyncSpaceInfoToDb($r, $True)
# Setup SQL server connection
# Grab Total/Free space from DB
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = "Server=$dbServer\$dbInstance;Database=$dbName;Integrated Security=True"
$sqlConn.Open()
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.Connection = $sqlConn
$sqlCmd.CommandText = "SELECT total_space, free_space from [dbo].[BackupRepositories] where id = '$($r.id.ToString())'"
$sqlReader = $sqlCmd.ExecuteReader()
While ($sqlReader.Read()) {
$totalspace = $sqlReader["total_space"]
$freespace = $sqlReader["free_space"]
}
$sqlConn.Close()
$outputObj = Build-Object $r.Name $Server.RealName $r.Path $freespace $totalspace
$outputAry += $outputObj
}
}
End {
$outputAry
}
}
Function Get-vPCReplicaTarget {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[PSObject[]]$InputObj
)
BEGIN {
$outputAry = @()
$dsAry = @()
If (($Name -ne $null) -and ($InputObj -eq $null)) {
$InputObj = Get-VBRJob -Name $Name -WarningAction SilentlyContinue
}
}
PROCESS {
Foreach ($obj in $InputObj) {
If (($dsAry -contains $obj.ViReplicaTargetOptions.DatastoreName) -eq $False) {
$esxi = $obj.GetTargetHost()
$dtstr = $esxi | Find-VBRViDatastore -Name $obj.ViReplicaTargetOptions.DatastoreName
$objoutput = New-Object -TypeName PSObject -Property @{
Target = $esxi.Name
Datastore = $obj.ViReplicaTargetOptions.DatastoreName
StorageFree = [Math]::Round([Decimal]$dtstr.FreeSpace/1GB,2)
StorageTotal = [Math]::Round([Decimal]$dtstr.Capacity/1GB,2)
FreePercentage = [Math]::Round(($dtstr.FreeSpace/$dtstr.Capacity)*100)
}
$dsAry = $dsAry + $obj.ViReplicaTargetOptions.DatastoreName
$outputAry = $outputAry + $objoutput
}
Else {
return
}
}
}
END {
$outputAry | Select-Object Target, Datastore, StorageFree, StorageTotal, FreePercentage
}
}
Function Get-VeeamVersion {
$veeamExe = Get-Item $veeamExePath
$VeeamVersion = $veeamExe.VersionInfo.ProductVersion
Return $VeeamVersion
}
Function Get-VeeamSupportDate {
#Get version and license info
$regBinary = (Get-Item 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\license').GetValue('Lic1')
$veeamLicInfo = [string]::Join($null, ($regBinary | % { [char][int]$_; }))
$pattern = "expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}"
# Convert Binary key
If($script:VeeamVersion -like "8*"){
$expirationDate = [regex]::matches($VeeamLicInfo, $pattern)[0].Value.Split("=")[1]
$datearray = $expirationDate -split '/'
$expirationDate = Get-Date -Day $datearray[0] -Month $datearray[1] -Year $datearray[2]
$totalDaysLeft = ($expirationDate - (get-date)).Totaldays.toString().split(",")[0]
$totalDaysLeft = [int]$totalDaysLeft
$objoutput = New-Object -TypeName PSObject -Property @{
ExpDate = $expirationDate.ToShortDateString()
DaysRemain = $totalDaysLeft
}
$objoutput
}
Else{
$objoutput = New-Object -TypeName PSObject -Property @{
ExpDate = "Failed"
DaysRemain = "Failed"
}
$objoutput
}
}
Function Get-VeeamServer {
$vservers=@{}
$outputAry = @()
$vservers.add($($script:vbrserver.realname),"VBRServer")
Foreach ($srv in $script:viProxyList) {
If (!$vservers.ContainsKey($srv.Host.Realname)) {
$vservers.Add($srv.Host.Realname,"ProxyServer")
}
}
Foreach ($srv in $script:repoList) {
If (!$vservers.ContainsKey($srv.gethost().Realname)) {
$vservers.Add($srv.gethost().Realname,"RepoServer")
}
}
$vservers = $vservers.GetEnumerator() | Sort-Object Name
Foreach ($vserver in $vservers) {
$outputAry += $vserver.Name
}
return $outputAry
}
Function Get-VeeamService {
param (
[PSObject]$inputObj)
$outputAry = @()
Foreach ($obj in $InputObj) {
$output = Get-Service -computername $obj -Name "*Veeam*" -exclude "SQLAgent*" |
Select @{Name="Server Name"; Expression = {$obj.ToLower()}}, @{Name="Service Name"; Expression = {$_.DisplayName}}, Status
# $outputAry = $outputAry + $output
$outputAry += $output
}
$outputAry
}
Function Get-VMBackupStatus {
param ([String]$vcenter)
# Convert exclusion list to simple regular expression
$excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
$excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
$excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
$outputary = @()
$vcenterobj = Get-VBRServer -Name $vcenter -WarningAction SilentlyContinue
$vmobjs = Find-VBRObject -Server $vcenterobj -WarningAction SilentlyContinue |
Where-Object {$_.Type -eq "VirtualMachine" -and $_.VMFolderName -notmatch $excludefolder_regex} |
Where-Object {$_.Name -notmatch $excludevms_regex} |
Where-Object {$_.GetParent("Datacenter") -notmatch $excludedc_regex}
$jobobjids = [Veeam.Backup.Core.CHierarchyObj]::GetObjectsOnHost($vcenterobj.id) | Where-Object {$_.Type -eq "Vm"}
Foreach ($vm in $vmobjs) {
$jobobjid = ($jobobjids | Where-Object {$_.ObjectId -eq $vm.Id}).Id
If (!$jobobjid) {
$jobobjid = $vm.FindParent("Datacenter").Id + "\" + $vm.Id
}
$vm | Add-Member -MemberType NoteProperty "JobObjId" -Value $jobobjid
}
# Get a list of all VMs from vCenter and add to hash table, assume Unprotected
$vms=@{}
Foreach ($vm in $vmobjs) {
If(!$vms.ContainsKey($vm.JobObjId)) {
$vmdc = [string]$vm.GetParent("Datacenter")
Try {$vmclus = [string]$vm.GetParent("ClusterComputeResource")} Catch {$vmclus = ""}
$vms.Add($vm.JobObjId, @("!", $vmdc, $vmclus, $vm.Name))
}
}
# Find all backup job sessions that have ended in the last x hours
$vbrsessions = Get-VBRBackupSession -WarningAction SilentlyContinue | Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}
# Find all Successfuly backed up VMs in selected sessions (i.e. VMs not ending in failure) and update status to "Protected"
If ($vbrsessions) {
Foreach ($session in $vbrsessions) {
Foreach ($vm in ($session.gettasksessions() | Where-Object {$_.Status -ne "Failed"} | ForEach-Object { $_ })) {
If($vms.ContainsKey($vm.Info.ObjectId)) {
$vms[$vm.Info.ObjectId][0]=$session.JobName
}
}
}
}
$vms.GetEnumerator() | Sort-Object Value
}
Function Get-VMMissingBackup {
param (
$vms)
$outputary = @()
Foreach ($vm in $vms) {
If ($vm.Value[0] -eq "!") {
$objoutput = New-Object -TypeName PSObject -Property @{
Datacenter = $vm.Value[1]
Cluster = $vm.Value[2]
Name = $vm.Value[3]
}
$outputAry += $objoutput
}
}
$outputAry | Select-Object Datacenter, Cluster, Name
}
Function Get-VMSuccessBackup {
param (
$vms)
$outputary = @()
Foreach ($vm in $vms) {
If ($vm.Value[0] -ne "!") {
$objoutput = New-Object -TypeName PSObject -Property @{
Datacenter = $vm.Value[1]
Cluster = $vm.Value[2]
Name = $vm.Value[3]
}
$outputAry += $objoutput
}
}
$outputAry | Select-Object Datacenter, Cluster, Name
}
#endregion
#region Report
# Get Veeam Version
$VeeamVersion = Get-VeeamVersion
If ($VeeamVersion -lt 8) {
Throw "Script requires VBR v8 or greater"
# Write-Error "Script requires VBR v8 or greater"
}
#region HTML Stuff
$headerObj = @"
<html>
<head>
<title>$rptTitle</title>
<style>
body {font-family: Tahoma; background-color:#fff;}
table {font-family: Tahoma;width: $($rptWidth)px;font-size: 12px;border-collapse:collapse;}
<!-- table tr:nth-child(odd) td {background: #e2e2e2;} -->
th {background-color: #cccc99;border: 1px solid #a7a9ac;border-bottom: none;}
td {background-color: #ffffff;border: 1px solid #a7a9ac;padding: 2px 3px 2px 3px;vertical-align: top;}
</style>
</head>
"@
$bodyTop = @"
<body>
<center>
<table cellspacing="0" cellpadding="0">
<tr>
<td style="width: 80%;height: 45px;border: none;background-color: #003366;color: White;font-size: 24px;vertical-align: bottom;padding: 0px 0px 0px 15px;">$rptTitle</td>
<td style="width: 20%;height: 45px;border: none;background-color: #003366;color: White;font-size: 12px;vertical-align:text-top;text-align:right;padding: 2px 3px 2px 3px;">v$MVRversion</td>
</tr>
<tr>
<td style="width: 80%;height: 35px;border: none;background-color: #003366;color: White;font-size: 10px;vertical-align: bottom;padding: 0px 0px 2px 3px;">Report generated: $(Get-Date -format g)</td>
<td style="width: 20%;height: 35px;border: none;background-color: #003366;color: White;font-size: 10px;vertical-align:bottom;text-align:right;padding: 2px 3px 2px 3px;">Veeam v$VeeamVersion</td>
</tr>
</table>
"@
$subHead01 = @"
<table>
<tr>
<td style="height: 35px;background-color: #eeeeee;color: #003366;font-size: 16px;font-weight: bold;vertical-align: middle;padding: 5px 0 0 15px;border-top: 1px solid #cccc99;border-bottom: none;">
"@
$subHead01err = @"
<table>
<tr>
<td style="height: 35px;background-color: #FF0000;color: #003366;font-size: 16px;font-weight: bold;vertical-align: middle;padding: 5px 0 0 15px;border-top: 1px solid #cccc99;border-bottom: none;">
"@
$subHead02 = @"
</td>
</tr>
</table>
"@
$footerObj = @"
</center>
</body>
</html>
"@
#endregion
#region bodysummarybk
#Get VM Backup Status
$vmstatus = @()
Foreach ($vcenter in $vcenters) {
$status = Get-VMBackupStatus $vcenter
$vmstatus += $status
}
# VMs Missing Backups
$missingVMs = @(Get-VMMissingBackup $vmstatus)
# VMs Successfuly Backed Up
$successVMs = @(Get-VMSuccessBackup $vmstatus)
# Get Backup Summary Info
$bodySummaryBk = $null
If ($showSummaryBk) {
$vbrMasterHash = @{
"Coordinator" = "$((gc env:computername).ToLower())"
"Failed" = @($failedSessionsBk).Count
"Sessions" = If ($seshListBk) {@($seshListBk).Count} Else {0}
"Read" = $totalReadBk
"Transferred" = $totalXferBk
"Successful" = @($successSessionsBk).Count
"Warning" = @($warningSessionsBk).Count
"Fails" = @($failsSessionsBk).Count
"Running" = @($runningSessionsBk).Count
"SuccessVM" = @($successVMs).Count
"FailedVM" = @($missingVMs).Count
}
$vbrMasterObj = New-Object -TypeName PSObject -Property $vbrMasterHash
If ($onlyLastBk) {
$bodySummaryBk = $vbrMasterObj | Select-Object @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
@{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Jobs Run"; Expression = {$_.Sessions}},
@{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
@{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
@{Name="Warnings"; Expression = {$_.Warning}},
@{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
} Else {
$bodySummaryBk = $vbrMasterObj | Select-Object @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
@{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Total Sessions"; Expression = {$_.Sessions}},
@{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
@{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
@{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}},
@{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
}
$bodySummaryBk = $subHead01 + "Backup Results Summary" + $subHead02 + $bodySummaryBk
}
#endregion
# Get Proxy Info
$bodyProxy = $null
If ($showProxy) {
If ($viProxyList -ne $null) {
$bodyProxy = Get-vPCProxyInfo | Select-Object @{Name="Proxy Name"; Expression = {$_.ProxyName}},
@{Name="Proxy Host"; Expression = {$_.RealName}}, Disabled, @{Name="IP Address"; Expression = {$_.IP}},
@{Name="RT (ms)"; Expression = {$_.Responce}}, Status | Sort-Object "Proxy Host" | ConvertTo-HTML -Fragment
$bodyProxy = $subHead01 + "Proxy Details" + $subHead02 + $bodyProxy
}
}
# Get Repository Info
$bodyRepo = $null
If ($showRepo) {
If ($repoList -ne $null) {
If ($repoSQL) {
$RepoInfo = $repoList | Get-vPCRepoInfoSQL
} Else {
$RepoInfo = $repoList | Get-vPCRepoInfo
}
$bodyRepo = $RepoInfo | Select-Object @{Name="Repository Name"; Expression = {$_.Target}},
@{Name="Repository Host"; Expression = {$_.RepoHost}},
@{Name="Path"; Expression = {$_.Storepath}}, @{Name="Free (GB)"; Expression = {$_.StorageFree}},
@{Name="Total (GB)"; Expression = {$_.StorageTotal}}, @{Name="Free (%)"; Expression = {$_.FreePercentage}},
@{Name="Status"; Expression = {
If ($_.FreePercentage -lt $repoCritical) {"Critical"}
ElseIf ($_.FreePercentage -lt $repoWarn) {"Warning"}
ElseIf ($_.FreePercentage -eq "Unknown") {"Unknown"}
Else {"OK"}}} | `
Sort-Object "Repository Name" | ConvertTo-HTML -Fragment
$bodyRepo = $subHead01 + "Repository Details" + $subHead02 + $bodyRepo
}
}
# Get Replica Target Info
$bodyReplica = $null
If ($showReplica) {
If ($repList -ne $null) {
$bodyReplica = $repList | Get-vPCReplicaTarget | Select-Object @{Name="Replica Target"; Expression = {$_.Target}}, Datastore,
@{Name="Free (GB)"; Expression = {$_.StorageFree}}, @{Name="Total (GB)"; Expression = {$_.StorageTotal}},
@{Name="Free (%)"; Expression = {$_.FreePercentage}},
@{Name="Status"; Expression = {If ($_.FreePercentage -lt $replicaCritical) {"Critical"} ElseIf ($_.FreePercentage -lt $replicaWarn) {"Warning"} Else {"OK"}}} | `
Sort "Replica Target" | ConvertTo-HTML -Fragment
$bodyReplica = $subHead01 + "Replica Details" + $subHead02 + $bodyReplica
}
}
# Get Veeam Services Info
$bodyServices = $null
If ($showServices) {
$bodyServices = Get-VeeamServer
$bodyServices = Get-VeeamService $bodyServices
If ($hideRunningSvc) {$bodyServices = $bodyServices | ?{$_.Status -ne "Running"}}
If ($bodyServices -ne $null) {
$bodyServices = $bodyServices | Select-Object "Server Name", "Service Name",
@{Name="Status"; Expression = {If ($_.Status -eq "Stopped"){"Not Running"} Else {$_.Status}}} | Sort-Object "Server Name", "Service Name" | ConvertTo-HTML -Fragment
bodyServices = $subHead01 + "Veeam Services" + $subHead02 + $bodyServices
}
}
# Get License Info
$bodyLicense = $null
If ($showLicExp) {
$bodyLicense = Get-VeeamSupportDate | Select-Object @{Name="Expiry Date"; Expression = {$_.ExpDate}}, @{Name="Days Remaining"; Expression = {$_.DaysRemain}}, `
@{Name="Status"; Expression = {If ($_.DaysRemain -lt $licenseCritical) {"Critical"} ElseIf ($_.DaysRemain -lt $licenseWarn) {"Warning"} ElseIf ($_.DaysRemain -eq "Failed") {"Failed"} Else {"OK"}}} | `
ConvertTo-HTML -Fragment
$bodyLicense = $subHead01 + "License/Support Renewal Date" + $subHead02 + $bodyLicense
}
# Get Backup Job Status
$bodyJobsBk = @()
If ($showStatusJobsBk) {
If ($allJobsBk.count -gt 0) {
Foreach($bkJob in $allJobsBk) {
$bodyJobsBk += $bkJob | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Enabled"; Expression = {$_.Info.IsScheduleEnabled}},
@{Name="Status"; Expression = {If ($_.IsRunning) {"Working"} Else {"Stopped"}}},
@{Name="Target Repo"; Expression = {$(Get-VBRBackupRepository -WarningAction SilentlyContinue | Where {$_.Id -eq $bkJob.Info.TargetRepositoryId}).Name}},
@{Name="Next Run"; Expression = {If ($_.ScheduleOptions.IsContinious) {"<Continious>"}
ElseIf ($_.ScheduleOptions.NextRun) {$_.ScheduleOptions.NextRun}
ElseIf ($_.IsScheduleEnabled -eq $False) {"<Disabled>"}
ElseIf ($_.ScheduleOptions.OptionsScheduleAfterJob.IsEnabled) {"After [" + $(Get-VBRJob -WarningAction SilentlyContinue | Where {$_.Id -eq $bkJob.Info.ParentScheduleId}).Name + "]"}
Else {"<not scheduled>"}}},
@{Name="Last Result"; Expression = {If ($_.Info.LatestStatus -eq "None"){""}Else{$_.Info.LatestStatus}}}
}
$bodyJobsBk = $bodyJobsBk | Sort-Object "Next Run" | ConvertTo-HTML -Fragment
If ($reportTableHours) {
$bodyJobsBk = $subHead01 + "Backup Job Status (within last $($HourstoCheck) hours)" + $subHead02 + $bodyJobsBk
} Else {
$bodyJobsBk = $subHead01 + "Backup Job Status" + $subHead02 + $bodyJobsBk
}
}
}
# Get Running Backup Jobs
$bodyRunningBk = $null
If ($showRunningBk) {
If ($runningSessionsBk.count -gt 0) {
$bodyRunningBk = $runningSessionsBk | Sort-Object Creationtime | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.Progress.StartTime) $(Get-Date)).TotalMinutes,2)}},
@{Name="% Complete"; Expression = {$_.Progress.Percents}},
@{Name="Read (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2)}},
@{Name="Write (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2)}} | ConvertTo-HTML -Fragment
If ($reportTableHours) {
$bodyRunningBk = $subHead01 + "Running Backup Jobs (within last $($HourstoCheck) hours)" + $subHead02 + $bodyRunningBk
} Else {
$bodyRunningBk = $subHead01 + "Running Backup Jobs" + $subHead02 + $bodyRunningBk
}
}
}
# Get Backup Sessions with Failures or Warnings
$bodySessWFBk = $null
If ($showWarnFailBk) {
$sessWF = @($warningSessionsBk + $failsSessionsBk)
If ($sessWF.count -gt 0) {
If ($onlyLastBk) {
$headerWF = "Backup Jobs with Warnings or Failures"
} Else {
$headerWF = "Backup Sessions with Warnings or Failures"
}
If ($reportTableHours) {
$headerWF += " (within last $($HourstoCheck) hours)"
}
If ($showDetailedBk) {
$bodySessWFBk = $sessWF | Sort-Object Creationtime | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
@{Name="Avg Speed (MB/s)"; Expression = {[Math]::Round($_.Info.Progress.AvgSpeed/1MB,2)}},
@{Name="Total (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedSize/1GB,2)}},
@{Name="Processed (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedUsedSize/1GB,2)}},
@{Name="Data Read (GB)"; Expression = {[Math]::Round($_.Info.Progress.ReadSize/1GB,2)}},
@{Name="Transferred (GB)"; Expression = {[Math]::Round($_.Info.Progress.TransferedSize/1GB,2)}},
@{Name="Details"; Expression = {
If ($_.GetDetails() -eq ""){($_.GetDetails()).Replace("<br />"," - ") + ($_ | Get-VBRTaskSession -WarningAction SilentlyContinue | %{If ($_.GetDetails()){$_.Name + ": " + $_.GetDetails()}})}
Else {($_.GetDetails()).Replace("<br />"," - ")}}},
Result | ConvertTo-HTML -Fragment
$bodySessWFBk = $subHead01 + $headerWF + $subHead02 + $bodySessWFBk
} Else {
$bodySessWFBk = $sessWF | Sort-Object Creationtime | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
@{Name="Details"; Expression = {
If ($_.GetDetails() -eq ""){($_.GetDetails()).Replace("<br />"," - ") + ($_ | Get-VBRTaskSession -WarningAction SilentlyContinue | %{If ($_.GetDetails()){$_.Name + ": " + $_.GetDetails()}})}
Else {($_.GetDetails()).Replace("<br />"," - ")}}},
Result | ConvertTo-HTML -Fragment
$bodySessWFBk = $subHead01 + $headerWF + $subHead02 + $bodySessWFBk
}
}
}
# Get Successful Backup Sessions
$bodySessSuccBk = $null
If ($showSuccessBk) {
If ($successSessionsBk.count -gt 0) {
If ($onlyLastBk) {
$headerSucc = "Successful Backup Jobs"
} Else {
$headerSucc = "Successful Backup Sessions"
}
If ($reportTableHours) {
$headerSucc += " (within last $($HourstoCheck) hours)"
}
If ($showDetailedBk) {
$bodySessSuccBk = $successSessionsBk | Sort-Object Creationtime | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
@{Name="Avg Speed (MB/s)"; Expression = {[Math]::Round($_.Info.Progress.AvgSpeed/1MB,2)}},
@{Name="Total (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedSize/1GB,2)}},
@{Name="Processed (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedUsedSize/1GB,2)}},
@{Name="Data Read (GB)"; Expression = {[Math]::Round($_.Info.Progress.ReadSize/1GB,2)}},
@{Name="Transferred (GB)"; Expression = {[Math]::Round($_.Info.Progress.TransferedSize/1GB,2)}},
Result | ConvertTo-HTML -Fragment
$bodySessSuccBk = $subHead01 + $headerSucc + $subHead02 + $bodySessSuccBk
} Else {
$bodySessSuccBk = $successSessionsBk | Sort-Object Creationtime | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
Result | ConvertTo-HTML -Fragment
$bodySessSuccBk = $subHead01 + $headerSucc + $subHead02 + $bodySessSuccBk
}
}
}
# Get Running VM Restore Sessions
$bodyRestoRunVM = $null
If ($showRestoreRunVM) {
If ($($runningResto).count -gt 0) {
$bodyRestoRunVM = $runningResto | Sort-Object CreationTime | Select-Object @{Name="VM Name"; Expression = {$_.Info.VmDisplayName}},
@{Name="Restore Type"; Expression = {$_.JobTypeString}}, @{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Initiator"; Expression = {$_.Info.Initiator.Name}},
@{Name="Reason"; Expression = {$_.Info.Reason}} | ConvertTo-HTML -Fragment
$bodyRestoRunVM = $subHead01 + "Running VM Restore Sessions" + $subHead02 + $bodyRestoRunVM
}
}
# Get Completed VM Restore Sessions
$bodyRestoreVM = $null
If ($showRestoreVM) {
If ($($completeResto).count -gt 0) {
$bodyRestoreVM = $completeResto | Sort-Object CreationTime | Select-Object @{Name="VM Name"; Expression = {$_.Info.VmDisplayName}},
@{Name="Restore Type"; Expression = {$_.JobTypeString}},
@{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $_.CreationTime $_.EndTime).TotalMinutes,2)}},
@{Name="Initiator"; Expression = {$_.Info.Initiator.Name}}, @{Name="Reason"; Expression = {$_.Info.Reason}},
@{Name="Result"; Expression = {$_.Info.Result}} | ConvertTo-HTML -Fragment
$bodyRestoreVM = $subHead01 + "Completed VM Restore Sessions" + $subHead02 + $bodyRestoreVM
}
}
# Get VMs Missing Backups
$bodyMissing = $null
If ($showUnprotectedVMs) {
If ($missingVMs -ne $null) {
$missingVMs = $missingVMs | Sort-Object Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
If ($reportTableHours) {
$bodyMissing = $subHead01err + "VMs with No Successful Backups (within last $($HourstoCheck) hours)" + $subHead02 + $missingVMs
} Else {
$bodyMissing = $subHead01err + "VMs with No Successful Backups" + $subHead02 + $missingVMs
}
}
}
# Get VMs Successfuly Backed Up
$bodySuccess = $null
If ($showProtectedVMs) {
If ($successVMs -ne $null) {
$successVMs = $successVMs | Sort-Object Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
If ($reportTableHours) {
$bodySuccess = $subHead01 + "VMs with Successful Backups (within last $($HourstoCheck) hours)" + $subHead02 + $successVMs
} Else {
$bodySuccess = $subHead01 + "VMs with Successful Backups" + $subHead02 + $successVMs
}
}
}
#region endpointoutput
# Get Endpoint Backup Summary Info
$bodySummaryEp = $null
If ($showSummaryEp) {
$vbrEpHash = @{
"Coordinator" = "$((gc env:computername).ToLower())"
"Sessions" = If ($seshListEp) {@($seshListEp).Count} Else {0}
"Successful" = @($successSessionsEp).Count
"Warning" = @($warningSessionsEp).Count
"Fails" = @($failsSessionsEp).Count
"Running" = @($runningSessionsEp).Count
}
$vbrEPObj = New-Object -TypeName PSObject -Property $vbrEpHash
If ($onlyLastEp) {
$bodySummaryEp = $vbrEPObj | Select-Object @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Total Jobs"; Expression = {$_.Sessions}},
@{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
@{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}} | ConvertTo-HTML -Fragment
} Else {
$bodySummaryEp = $vbrEPObj | Select-Object @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Total Sessions"; Expression = {$_.Sessions}},
@{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
@{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}} | ConvertTo-HTML -Fragment
}
$bodySummaryEp = $subHead01 + "Endpoint Backup Results Summary" + $subHead02 + $bodySummaryEp
}
# Get Endpoint Backup Job Status
$bodyJobsEp = $null
If ($showJobsEp) {
If ($allJobsEp.count -gt 0) {
$bodyJobsEp = $allJobsEp | Select-Object @{Name="Job Name"; Expression = {$_.Name}},
@{Name="Enabled"; Expression = {$_.IsEnabled}},@{Name="Status"; Expression = {$_.LastState}},
@{Name="Target Repo"; Expression = {$_.Target}},@{Name="Next Run"; Expression = {$_.NextRun}},
@{Name="Last Result"; Expression = {If ($_.LastResult -eq "None"){""}Else{$_.LastResult}}} | Sort-Object "Next Run" | ConvertTo-HTML -Fragment
$bodyJobsEp = $subHead01 + "Endpoint Backup Job Status" + $subHead02 + $bodyJobsEp
}
}
# Get Running Endpoint Backup Jobs
$bodyRunningEp = @()
If ($showRunningEp) {
If ($runningSessionsEp.count -gt 0) {
Foreach($job in $allJobsEp) {
$bodyRunningEp += $runningSessionsEp | ?{$_.JobId -eq $job.Id} | Select-Object @{Name="Job Name"; Expression = {$job.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date)).TotalMinutes,2)}}
}
$bodyRunningEp = $bodyRunningEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment
$bodyRunningEp = $subHead01 + "Running Endpoint Backup Jobs" + $subHead02 + $bodyRunningEp
}
}
# Get Endpoint Backup Sessions with Failures or Warnings
$bodySessWFEp = @()
If ($showWarnFailEp) {
$sessWFEp = @($warningSessionsEp + $failsSessionsEp)
If ($sessWFEp.count -gt 0) {
If ($onlyLastEp) {
$headerWFEp = "Endpoint Backup Jobs with Warnings or Failures"
} Else {
$headerWFEp = "Endpoint Backup Sessions with Warnings or Failures"
}
Foreach($job in $allJobsEp) {
$bodySessWFEp += $sessWFEp | ?{$_.JobId -eq $job.Id} | Select-Object @{Name="Job Name"; Expression = {$job.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date $_.EndTime)).TotalMinutes,2)}},
Result
}
$bodySessWFEp = $bodySessWFEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment
$bodySessWFEp = $subHead01 + $headerWFEp + $subHead02 + $bodySessWFEp
}
}
# Get Successful Endpoint Backup Sessions
$bodySessSuccEp = @()
If ($showSuccessEp) {
If ($successSessionsEp.count -gt 0) {
If ($onlyLastEp) {
$headerSuccEp = "Successful Endpoint Backup Jobs"
} Else {
$headerSuccEp = "Successful Endpoint Backup Sessions"
}
Foreach($job in $allJobsEp) {
$bodySessSuccEp += $successSessionsEp | ?{$_.JobId -eq $job.Id} | Select-Object @{Name="Job Name"; Expression = {$job.Name}},
@{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},
@{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date $_.EndTime)).TotalMinutes,2)}},
Result
}
$bodySessSuccEp = $bodySessSuccEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment
$bodySessSuccEp = $subHead01 + $headerSuccEp + $subHead02 + $bodySessSuccEp
}
}
#endregion
# Combine HTML Output
$htmlOutput = $headerObj + $bodyTop + $bodySummaryBK + $bodySummaryEp + $bodyProxy + $bodyRepo + $bodyReplica + $bodyLicense + $bodyJobsBk +
$bodyRunningBk + $bodySessWFBk + $bodySessSuccBk + $bodyRestoRunVM + $bodyRestoreVM + $bodyMissing + $bodySuccess + $bodyJobsEp + $bodyRunningEp +
$bodySessWFEp + $bodySessSuccEp + $bodyServices + $footerObj
# $htmlOutput = $headerObj + $bodyTop + $bodySummaryBK + $bodySummaryEp + $bodyMissing + $bodySuccess + $bodyJobsBk + $bodyRunningBk + $bodySessWFBk +
# $bodySessSuccBk + $bodyRestoRunVM + $bodyRestoreVM + $bodyJobsEp + $bodyRunningEp + $bodySessWFEp + $bodySessSuccEp + $bodyRepo + $bodyProxy +
# $bodyReplica + $bodyServices + $bodyLicense + $footerObj
# Add color to output depending on results
#Green
$htmlOutput = $htmlOutput.Replace("<td>Running<","<td style=""background-color: Green;color: White;"">Running<")
$htmlOutput = $htmlOutput.Replace("<td>OK<","<td style=""background-color: Green;color: White;"">OK<")
$htmlOutput = $htmlOutput.Replace("<td>Alive<","<td style=""background-color: Green;color: White;"">Alive<")
$htmlOutput = $htmlOutput.Replace("<td>Success<","<td style=""background-color: Green;color: White;"">Success<")
#Yellow
$htmlOutput = $htmlOutput.Replace("<td>Warning<","<td style=""background-color: Yellow;"">Warning<")
#Red
$htmlOutput = $htmlOutput.Replace("<td>Not Running<","<td style=""background-color: Red;color: White;"">Not Running<")
$htmlOutput = $htmlOutput.Replace("<td>Failed<","<td style=""background-color: Red;color: White;"">Failed<")
$htmlOutput = $htmlOutput.Replace("<td>Critical<","<td style=""background-color: Red;color: White;"">Critical<")
$htmlOutput = $htmlOutput.Replace("<td>Dead<","<td style=""background-color: Red;color: White;"">Dead<")
#endregion
#region Output
If (-not $sendEmail -and -not $saveFile) {
Write-Warning 'No output selected, enabling saveFile'
$saveFile = $True
}
If ($sendEmail) {
$smtp = New-Object System.Net.Mail.SmtpClient $emailHost
$smtp.Credentials = New-Object System.Net.NetworkCredential($emailUser, $emailPass);
$msg = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
$msg.Subject = $emailSubject
If ($emailAttach) {
$body = "Veeam Report Attached"
$msg.Body = $body
$tempfile = "$env:TEMP\$rptTitle.htm"
$htmlOutput | Out-File $tempfile
$attachment = new-object System.Net.Mail.Attachment $tempfile
$msg.Attachments.Add($attachment)
} Else {
$body = $htmlOutput
$msg.Body = $body
$msg.isBodyhtml = $True
}
$smtp.send($msg)
If ($emailAttach) {
$attachment.dispose()
Remove-Item $tempfile
}
}
If ($saveFile) {
$htmlOutput | Out-File $outFile
If ($launchFile) {
Invoke-Item $outFile
} Else {
Write-Output $outFile
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment