Find the latest version here.
My Veeam Report v1.2 has arrived! With it comes a couple of minor tweaks and the addition of successfully backed up VMs. For those backing up hundreds (or thousands) of VMs, this option can be disabled within the User Variables section. In smaller scenarios it’s nice to see at a glance which VMs got backed up last night.
This will most likely be the last update prior to Veeam rolling out v8. We’ll have to wait and see if any changes are needed for it to continue to work properly.
Original post with screenshot and credits can be found here.
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
asnp "VeeamPSSnapIn" -ErrorAction SilentlyContinue | |
#region User-Variables | |
# Report Title | |
$rptTitle = "My Veeam Report" | |
# Report mode – valid modes: any number of hours, Weekly or Monthly | |
# 24, 48, "Weekly", "Monthly" | |
$reportMode = 24 | |
# File Output path and filename | |
# When $sendEmail is set to $false, file is created and opened in default browser | |
$file = "E:\MyVeeamReport.htm" | |
# Show VMs with no successful backups within time frame ($reportMode) | |
$showMissing = $true | |
# Show VMs with successful backups within time frame ($reportMode) | |
$showSuccess = $true | |
# vCenter server(s) – As seen in VBR server | |
#$vcenters = "vcenter1","vcenter2" | |
$vcenters = "vcenter.yourdomain.local" | |
# To Exclude VMs from Missing and Successful Backups report add VM names to be excluded as follows | |
# $excludevms = @("vm1","vm2","*_replica") | |
$excludeVMs = @("") | |
# Exclude VMs from Missing and Successful Backups report in the following (vcenter) folder | |
$folderExclude = "" | |
# Show jobs w/warnings or errors | |
$jlines = $true | |
# Show running jobs | |
$rlines = $true | |
# Show running Services | |
$runningSvc = $true | |
# 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.Common.dll) | |
$veeamDllPath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Common.dll" | |
# Email configuration | |
# Send Email – $true or $false – if true, email is sent, if false, output is saved to $file | |
$sendEmail = $false | |
$emailHost = "smtprelay.yourdomain.local" | |
$emailUser = "" | |
$emailPass = "" | |
$emailFrom = "MyVeeamReport@yourdomain.local" | |
$emailTo = "you@yourdomain.local" | |
# Send report as attachment – $true or $false | |
$emailAttach = $false | |
# 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 | |
#endregion | |
#region VersionInfo | |
$vPCARversion = "1.2" | |
# | |
# 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 B&R Server | |
$vbrServer = Get-VBRLocalHost | |
# Get all the VI proxies in your army | |
$viProxyList = Get-VBRViProxy | |
# Get all the backup repositories | |
$repoList = Get-VBRBackupRepository | |
# Get unique repo servers | |
$uniqueRepos = @() | |
$repolist | %{$uniqueRepos += $($_.gethost().realname)} | |
$uniqueRepos = $uniqueRepos | Sort -unique | |
# Get all sessions to determine working jobs | |
$allSesh = Get-VBRBackupSession | |
# Get all the backup sessions for mode | |
if ($reportMode -eq "Monthly") { | |
$HourstoCheck = 720 | |
} elseif ($reportMode -eq "Weekly") { | |
$HourstoCheck = 168 | |
} else { | |
$HourstoCheck = $reportMode | |
} | |
$seshList = $allSesh | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)) -and ($_.State -ne "Working")} | |
if (($reportMode -ne "Weekly") -And ($reportMode -ne "Monthly")) { | |
$rptTitle = "$rptTitle (Last $reportMode Hrs)" | |
} else { | |
$rptTitle = "$rptTitle ($reportMode)" | |
} | |
#Get replica jobs | |
$repList = Get-VBRJob | ?{$_.IsReplica} | |
# Get the job counts | |
$totalxfer = $null | |
$totalRead = $null | |
$seshList | %{$totalxfer += $([Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2))} | |
$seshList | %{$totalRead += $([Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2))} | |
$succesSessions = @($seshList | ?{$_.Result -eq "Success"}) | |
$warningSessions = @($seshList | ?{$_.Result -eq "Warning"}) | |
$failsSessions = @($seshList | ?{$_.Result -eq "Failed"}) | |
$totalSessions = @($seshList | ?{$_.Result -eq "Failed" -Or $_.Result -eq "Success" -Or $_.Result -eq "Warning"}) | |
$runningSessions = @($allSesh | ?{$_.State -eq "Working"}) | |
$failedSessions = @($seshList | ?{($_.Result -eq "Failed") -and ($_.WillBeRetried -ne "True")}) | |
#endregion | |
#region Functions | |
function Get-vPCProxyInfo { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Position=0)] | |
[String[]]$Name, | |
[Parameter(Mandatory=$true, Position=1)] | |
[PSObject[]]$Sessions | |
) | |
Begin { | |
$vPCObjAry = @() | |
[Int]$script:jobcount = 0 | |
[Int]$totalObjInJob = 0 | |
$cleanSessions = $Sessions | ?{($_.Result -ne "Failed") -and | |
($_.State -eq "Stopped") -and ($_.JobType -ne "Copy")} | |
$cleanSessions | %{$totalObjInJob = $totalObjInJob + $_.Progress.TotalObjects} | | |
Out-Null | |
function Build-vPCObj {param ([PsObject]$inputObj) | |
$ping = new-object system.net.networkinformation.ping | |
$pinginfo = $ping.send("$($inputObj.Host.RealName)") | |
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 = $pinginfo.Address | |
Responce = $pinginfo.RoundtripTime | |
ReadData = [Decimal]0.00 | |
WriteData = [Decimal]0.00 | |
UsageCount = 0 | |
ProxiesInSessions = $($totalObjInJob*2) | |
} | |
return $vPCFuncObject | |
} | |
function Get-vPCProxyName {param ([String]$title) | |
$titleAry = @($title.Split()) | |
$ai = 3 | |
$bi = $titleAry.count – 1 | |
while ($ai -ne $bi) { | |
$proxyString = $proxyString + " " + $titleAry[$ai] | |
$ai ++ | |
} | |
return $proxyString.Trim() | |
} | |
function Get-vPCProxyRole {param ([String]$title) | |
$titleAry = @($title.Split()) | |
$roleString = $titleAry[1] | |
return $roleString | |
} | |
Get-VBRViProxy | %{$vPCObjAry = $vPCObjAry + $(Build-vPCObj $_)} | |
} | |
Process { | |
if ($Name -ne $null) { | |
$output = $vPCObjAry | ?{$Name -like $_.ProxyName} | |
} | |
else { | |
$output = $vPCObjAry | |
} | |
} | |
End { | |
$output | |
} | |
} | |
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, $path, $free, $total) | |
$repoObj = New-Object -TypeName PSObject -Property @{ | |
Target = $name | |
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 Target, Storepath, StorageFree, StorageTotal, FreePercentage | |
} | |
} | |
Process { | |
foreach ($r in $Repository) { | |
if ($r.GetType().Name -eq [String]) { | |
$r = Get-VBRBackupRepository -Name $r | |
} | |
if ($r.Type -eq "WinLocal") { | |
$Server = $r.GetHost() | |
$FileCommander = [Veeam.Backup.Core.CRemoteWinFileCommander]::Create($Server.Info) | |
$storage = $FileCommander.GetDrives([ref]$null) | ?{$_.Name -eq $r.Path.Substring(0,3)} | |
$outputObj = Build-Object $r.Name $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 $r.Path $storage.FreeSpace $storage.TotalSize | |
} | |
elseif ($r.Type -eq "CifsShare") { | |
$fso = New-Object -Com Scripting.FileSystemObject | |
$storage = $fso.GetDrive($r.Path) | |
$outputObj = Build-Object $r.Name $r.Path $storage.AvailableSpace $storage.TotalSize | |
} | |
$outputAry = $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 | |
} | |
} | |
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 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]$_; })) | |
if($script:VeeamVersion -like "6*"){ | |
$pattern = "Expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}" | |
} | |
elseif($script:VeeamVersion -like "5*"){ | |
$pattern = "EXPIRATION DATE\=\d{1,2}\/\d{1,2}\/\d{1,4}" | |
} | |
elseif($script:VeeamVersion -like "7*"){ | |
$pattern = "expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}" | |
} | |
# Convert Binary key | |
if($script:VeeamVersion -like "5*" -OR $script:VeeamVersion -like "6*" -OR $script:VeeamVersion -like "7*"){ | |
$expirationDate = [regex]::matches($VeeamLicInfo, $pattern)[0].Value.Split("=")[1] | |
$datearray = $expirationDate -split '/' | |
$datearray = $datearray[1],$datearray[0],$datearray[2] | |
$expirationDate = $datearray -join '/' | |
$totalDaysLeft = ((Get-Date $expirationDate) – (get-date)).Totaldays.toString().split(",")[0] | |
$totalDaysLeft = [int]$totalDaysLeft | |
$objoutput = New-Object -TypeName PSObject -Property @{ | |
ExpDate = $expirationDate | |
DaysRemain = $totalDaysLeft | |
} | |
$objoutput | |
} | |
else{ | |
$objoutput = New-Object -TypeName PSObject -Property @{ | |
ExpDate = "Failed" | |
DaysRemain = "Failed" | |
} | |
$objoutput | |
} | |
} | |
function Get-VeeamServers { | |
$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-VeeamServices { | |
param ( | |
[PSObject]$inputObj) | |
$outputAry = @() | |
foreach ($obj in $InputObj) { | |
$output = Get-Service -computername $obj -Name "*Veeam*" -exclude "SQLAgent*" | | |
Select @{Name="Server Name"; Expression = {$obj}}, @{Name="Service Name"; Expression = {$_.DisplayName}}, Status | |
$outputAry = $outputAry + $output | |
} | |
$outputAry | |
} | |
function Get-VMsBackupStatus { | |
param ( | |
[String]$vcenter) | |
$outputary = @() | |
$vcenterobj = Get-VBRServer -Name $vcenter | |
$vmobjs = Find-VBRObject -Server $vcenterobj | Where-Object {$_.Type -eq "VirtualMachine" -and $_.VMFolderName -notlike $script:folderExclude} | |
$jobobjids = [Veeam.Backup.Core.CHierarchyObj]::GetObjectsOnHost($vcenterobj.id) | Where-Object {$_.GetItem().Type -eq "Vm"} | |
# Convert exclusion list to simple regular expression | |
$excludevms_regex = (‘(?i)^(‘ + (($script:excludevms | ForEach {[regex]::escape($_)}) –join “|”) + ‘)$’) -replace "\\\*", ".*" | |
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 | where {$_.Name -notmatch $excludevms_regex})) { | |
if(!$vms.ContainsKey($vm.JobObjId)) { | |
$vms.Add($vm.JobObjId, @("!", [string]$vm.GetParent("Datacenter"), $vm.Name)) | |
} | |
} | |
# Find all backup job sessions that have ended in the last x hours | |
$vbrjobs = Get-VBRJob | Where-Object {$_.JobType -eq "Backup"} | |
$vbrsessions = Get-VBRBackupSession | Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)} | |
# Find all successfully 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-VMsMissingBackup { | |
param ( | |
$vms) | |
$outputary = @() | |
foreach ($vm in $vms) { | |
if ($vm.Value[0] -eq "!") { | |
$objoutput = New-Object -TypeName PSObject -Property @{ | |
Datacenter = $vm.Value[1] | |
Name = $vm.Value[2] | |
} | |
$outputAry += $objoutput | |
} | |
} | |
$outputAry | Select Datacenter, Name | Sort Name | |
} | |
function Get-VMsSuccessBackup { | |
param ( | |
$vms) | |
$outputary = @() | |
foreach ($vm in $vms) { | |
if ($vm.Value[0] -ne "!") { | |
$objoutput = New-Object -TypeName PSObject -Property @{ | |
Datacenter = $vm.Value[1] | |
Name = $vm.Value[2] | |
} | |
$outputAry += $objoutput | |
} | |
} | |
$outputAry | Select Datacenter, Name | Sort Name | |
} | |
#endregion | |
#region Report | |
# Get Veeam Version | |
$VeeamVersion = Get-VeeamVersion | |
# HTML Stuff | |
$headerObj = @" | |
<html> | |
<head> | |
<title>$rptTitle</title> | |
<style> | |
body {font-family: Tahoma; background-color:#fff;width: 1024px;} | |
table {font-family: Tahoma;width: 1024px;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> | |
<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: 30%;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$vPCARversion<br>Veeam v$VeeamVersion</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)</font></td> | |
<td style="width: 30%;height: 35px;border: none;background-color: #003366;color: White;font-size: 10px;vertical-align:bottom;text-align:right;padding: 2px 3px 2px 3px;">Based on:<br><a href="http://www.vpowercli.co.uk/2012/01/23/vpowercli-v6-army-report/" target="_blank"><font color="White">vPowerCLI v6 Army Report</font></a></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: none;border-bottom: none;"> | |
"@ | |
$subHead01err = @" | |
<table> | |
<tr> | |
<td style="height: 35px;background-color: #eeeeee;color: #FF0000;font-size: 16px;font-weight: bold;vertical-align: middle;padding: 5px 0 0 15px;border-top: none;border-bottom: none;"> | |
"@ | |
$subHead02 = @" | |
</td> | |
</tr> | |
</table> | |
"@ | |
$footerObj = @" | |
</body> | |
</html> | |
"@ | |
If ($VeeamVersion -lt 7) { | |
Write-Host "Script requires VBR v7 or greater" | |
exit | |
} | |
# Get Summary Info | |
$vbrMasterHash = @{"Coordinator" = "$((gc env:computername).ToLower())"; "Failed" = $failedSessions.Count; "Sessions" = $totalSessions.Count; | |
"Read" = $totalRead; "Transferred" = $totalXfer; "Successfull" = $succesSessions.Count; "Warning" = $warningSessions.Count; | |
"Fails" = $failsSessions.Count; "Running" = $runningSessions.Count;} | |
$vbrMasterObj = New-Object -TypeName PSObject -Property $vbrMasterHash | |
$bodyMasterT = $vbrMasterObj | Select Coordinator, @{Name="Job Runs"; Expression = {$_.Sessions}}, | |
@{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}}, | |
@{Name="Running"; Expression = {$_.Running}}, @{Name="Successfull"; Expression = {$_.Successfull}}, | |
@{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}}, | |
@{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment | |
#Get VM Backup Status | |
If ($showMissing -or $showSuccess) { | |
$vmstatus = @() | |
foreach ($vcenter in $vcenters) { | |
$status = Get-VMsBackupStatus $vcenter | |
$vmstatus += $status | |
} | |
} | |
# Get VMs Missing Backups | |
$bodyMissing = $null | |
$missingVMs = @() | |
If ($showMissing) { | |
$missingVMs = Get-VMsMissingBackup $vmstatus | |
If ($missingVMs -ne $null) { | |
$missingVMs = $missingVMs | Sort Datacenter, Name | ConvertTo-HTML -Fragment | |
$bodyMissing = $subHead01err + "VMs with no successful backups" + $subHead02 + $missingVMs | |
} | |
} | |
# Get VMs Successfully Backed Up | |
$bodySuccess = $null | |
$successVMs = @() | |
If ($showSuccess) { | |
$successVMs = Get-VMsSuccessBackup $vmstatus | |
If ($successVMs -ne $null) { | |
$successVMs = $successVMs | Sort Datacenter, Name | ConvertTo-HTML -Fragment | |
$bodySuccess = $subHead01 + "VMs with successful backups" + $subHead02 + $successVMs | |
} | |
} | |
# Get Running Jobs | |
$bodyrLines = $null | |
if ($rlines -eq $true) { | |
if ($runningSessions.count -gt 0) { | |
$bodyrLines = $runningSessions | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}}, | |
@{Name="Start Date"; Expression = {$_.Progress.StartTime.ToShortDateString()}}, | |
@{Name="Start Time"; Expression = {$_.Progress.StartTime.ToShortTimeString()}}, | |
@{Name="Duration"; Expression = {Get-Date "$($_.Progress.Duration)" -f HH:mm:ss}}, | |
@{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 | |
$bodyrLines = $subHead01 + "Running Jobs" + $subHead02 + $bodyrLines | |
} | |
} | |
# Get Jobs with Failures or Warnings | |
$bodyjLines = $null | |
if ($jlines -eq $true) { | |
$counter = @($seshList | ?{($_.Result -eq "Warning") -or ($_.Result -eq "Failed")}) | |
if ($counter.count -gt 0) { | |
$bodyjLines = $seshList | ?{($_.Result -eq "Warning") -or ($_.Result -eq "Failed")} | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}}, | |
@{Name="Start Date"; Expression = {$_.Progress.StartTime.ToShortDateString()}}, | |
@{Name="Start Time"; Expression = {$_.Progress.StartTime.ToShortTimeString()}}, | |
@{Name="Stop Time"; Expression = {$_.Progress.StopTime.ToShortTimeString()}}, | |
@{Name="Details"; Expression = {($_.GetDetails()).Replace("<br />"," – ")}},Result | ConvertTo-HTML -Fragment | |
$bodyjLines = $subHead01 + "Sessions with Warnings or Failures" + $subHead02 + $bodyjLines | |
} | |
} | |
# Get Proxy Info | |
$bodyProxy = $null | |
if ($viProxyList -ne $null -And $seshList -ne $null) { | |
$bodyProxy = Get-vPCProxyInfo -Sessions $seshList | Select @{Name="Proxy Host"; Expression = {$_.RealName}}, | |
Disabled, @{Name="IP Address"; Expression = {$_.IP}}, @{Name="RT (ms)"; Expression = {$_.Responce}}, Status | | |
Sort "Proxy Host" | ConvertTo-HTML -Fragment | |
$bodyProxy = $subHead01 + "Proxy Details" + $subHead02 + $bodyProxy | |
} | |
# Get Repository Info | |
$bodyRepo = $null | |
if ($repoList -ne $null) { | |
$bodyRepo = $repoList | Get-vPCRepoInfo | Select @{Name="Repository Name"; Expression = {$_.Target}}, | |
@{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"} Else {"OK"}}} | ` | |
Sort "Repository Name" | ConvertTo-HTML -Fragment | |
$bodyRepo = $subHead01 + "Repository Details" + $subHead02 + $bodyRepo | |
} | |
# Get Replica Target Info | |
$bodyReplica = $null | |
if ($repList -ne $null) { | |
$bodyReplica = $repList | Get-vPCReplicaTarget | Select @{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 = Get-VeeamServers | |
$bodyServices = Get-VeeamServices $bodyServices | |
If ($runningSvc -ne $true) {$bodyServices = $bodyServices | ?{$_.Status -ne "Running"}} | |
If ($bodyServices -ne $null) { | |
$bodyServices = $bodyServices | Select "Server Name", "Service Name", Status | Sort "Server Name", "Service Name" | ConvertTo-HTML -Fragment | |
$bodyServices = $subHead01 + "Services" + $subHead02 + $bodyServices | |
} | |
# Get License Info | |
$bodyLicense = Get-VeeamSupportDate | Select @{Name="Expiry Date"; Expression = {$_.ExpDate}}, @{Name="Days Remaining"; Expression = {$_.DaysRemain}}, ` | |
@{Name="Status"; Expression = {If ($_.DaysRemain -lt $licenseCritical) {"Critical"} ElseIf ($_.DaysRemain -lt $licenseWarn) {"Warning"} Else {"OK"}}} | ` | |
ConvertTo-HTML -Fragment | |
$bodyLicense = $subHead01 + "License/Support Renewal Date" + $subHead02 + $bodyLicense | |
# Combine HTML Output | |
$htmlOutput = $headerObj + $bodyTop + $bodyMasterT + $bodyMissing + $bodySuccess + $bodyrLines + $bodyjLines + $bodyRepo + $bodyProxy + $bodyReplica + $bodyServices + $bodyLicense + $footerObj | |
# Add color to output depending on results | |
$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>Warning<","<td style=""background-color: Yellow;"">Warning<") | |
$htmlOutput = $htmlOutput.Replace("<td>Stopped<","<td style=""background-color: Red;color: White;"">Stopped<") | |
$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 ($sendEmail) { | |
$emailSubject = $rptTitle | |
$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 | |
} | |
} else { | |
$htmlOutput | Out-File $file | |
Invoke-Expression $file | |
} | |
#endregion |
Thank you once again, now I no longer need to hack your code to see successfully backed up VMs too!
My script fail after opgrade to v8
Have you been looking at a update to v8.
I haven’t had a chance to look at v8 yet – I suspect a few things will break and will need to get reconfigured. Stay tuned!
Pingback: My Veeam Report v1.3 with VBR v8 Support | Shawn Masterson's Blog
I have multiple B&R servers, is it possible to combine servers in one report? Nice report BTW!
Unfortunately, at this time the report is targeted at a single B&R server.