-
-
Save virgilwashere/964c81b9bb49792dceb8 to your computer and use it in GitHub Desktop.
Veeam Backup Report
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 | |
.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