Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
# Save output to a file - $true or $false
$saveFile = $true
# File output path and filename
$outFile = "E:\MyVeeamReport_$(Get-Date -format MMddyyyy_hhmmss).htm"
# Launch file after creation - $true or $false
$launchFile = $false
# 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.Core.dll)
$veeamDllPath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Core.dll"
# Email configuration
# Send Email - $true or $false
$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.3"
#
# Version 1.3 - SM
# Now supports VBR v8
# For VBR v7, use report version 1.2
# Added more flexable 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 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.CWinFileCommander]::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 "8*"){
$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 "8*"){
$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 {$_.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
If ($VeeamVersion -lt 8) {
Write-Host "Script requires VBR v8 or greater" -ForegroundColor Red
exit
}
# 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>
"@
# 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"} ElseIf ($_.DaysRemain -eq "Failed") {"Failed"} 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
}
}
If ($saveFile) {
$htmlOutput | Out-File $outFile
If ($launchFile) {
Invoke-Expression $outFile
}
}
#endregion
@nevs55

This comment has been minimized.

Copy link

@nevs55 nevs55 commented Feb 1, 2018

this script is awesome, is there any way you can add exclude vm's with a certain tag?

@Zuldan

This comment has been minimized.

Copy link

@Zuldan Zuldan commented Mar 8, 2018

Hi there, thanks for making a great script. We have been using it for years. We've recently upgraded Veeam to 9.5 Update 3 and so I've just upgraded the report script from 9.5.1 to 9.5.3 but have 2 issues.

  1. The HTML formatting is all out of wack (you have to keep scrolling side ways to see the report) and is showing some funky pink colour. The old report looks so much better.

  2. It's reporting a ton of servers with No Successful Backups within RPO. Are there new requirements for a successful RPO? I've checked the servers backup status in the Veeam console and they display "Successful"

9.5.1
before

9.5.3
after

@lovelesspl

This comment has been minimized.

Copy link

@lovelesspl lovelesspl commented Apr 6, 2018

While the documentation states you can copy your entire user variable section. This is not 100% accurate. As far as the report being extremely wide I found the following:

User Variable:
Line 42/43 - Was changed from Pixel settings to a Percentage

Code:
1391 HTML Stuff
PX was changed to a %

So if you want to use the new version 9.5.3. make sure you don't replace the new % based variable with your old Pixel variable or you report will be extra wide :)

Also under email settings in the User variable section there are a few more variables in the new version. Port, EnableSSL, etc.

@nominode4

This comment has been minimized.

Copy link

@nominode4 nominode4 commented Apr 12, 2018

Lovely Script. Thanks. Can we add all our Backup server in one script and merge it to one report.
is that possible ?
Regards

Nauman

@DVKrishna

This comment has been minimized.

Copy link

@DVKrishna DVKrishna commented Aug 3, 2018

Thankyou. Its a lovely script. I need some help for creating a PS script for getting the corrupted files report weekly send out to my email ID.
I have created some part of it and having trouble putting it in script
Get-VBRBackup | Get-VBRRestorePoint | where {$_.IsRecheckCorrupted -eq 'True'} | Select Name,DisplayName,VmName,State,CreationTime,Type,ApproxSize,Algorithm,IsCorrupted,IsRecheckCorrupted,IsConsistent,Info |ft -auto

Also I want it write the output to a CSV file on disk and append to the file, so we have a history of corrupted jobs

I am looking for some help!!

@digitalexpl0it

This comment has been minimized.

Copy link

@digitalexpl0it digitalexpl0it commented Jun 12, 2019

Great script, nice job on this. I would love to see a CSV output option

@Supergoof

This comment has been minimized.

Copy link

@Supergoof Supergoof commented Oct 7, 2019

Hi Shawn, thanks for a really nice report.
Could it be that the Veeam Agent commands have changed? I can't seem to get it to report on Agent backup jobs
In my Veeam server the jobtype of Agent backups is "EpAgentManagement"

P.S. Any possibility of getting jobtype "SqlLogBackup" included in the report ?

@smasterson

This comment has been minimized.

Copy link
Owner Author

@smasterson smasterson commented Oct 7, 2019

@jungkung6

This comment has been minimized.

Copy link

@jungkung6 jungkung6 commented Nov 7, 2019

Hi Shawn,

Can I use this script with Veeam 9.5 U4b?

@smasterson

This comment has been minimized.

Copy link
Owner Author

@smasterson smasterson commented Nov 7, 2019

@dpearceFL

This comment has been minimized.

Copy link

@dpearceFL dpearceFL commented Jan 6, 2020

We have some backup jobs defined but not enabled or scheduled because they are used only on demand. These jobs show up as "Warning". Would it be possible to have $excludeBackupJob?

@dpearceFL

This comment has been minimized.

Copy link

@dpearceFL dpearceFL commented Jan 6, 2020

Running 9.5.3.

When I run it at the command line, I get this message. I am using the Standard version with no defined tape jobs.

Get-VBRTapeVault : This functionality is available in the Enterprise (or higher) edition only.
At C:\ent\util\Scripts\MyVeeamReport\MyVeeamReport.ps1:668 char:16
+ $mediaVaults = Get-VBRTapeVault
+                ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-VBRTapeVault], LicenseException
    + FullyQualifiedErrorId : System.ComponentModel.LicenseException,Veeam.Backup.PowerShell.Cmdlets.GetVBRTapeVault
@dpearceFL

This comment has been minimized.

Copy link

@dpearceFL dpearceFL commented Jan 6, 2020

Kluge fix for the tape error message. Not tested with a tape drive.

# Get all Media Pools
$mediaPools = Get-VBRTapeMediaPool
if (Get-VBRTapeDrive) {
  # Get all Media Vaults
  $mediaVaults = Get-VBRTapeVault
}
@Borkason

This comment has been minimized.

Copy link

@Borkason Borkason commented Jan 28, 2020

Would you consider adding the possibility to exclude entire clusters?

@puehringer1984

This comment has been minimized.

Copy link

@puehringer1984 puehringer1984 commented Feb 27, 2020

hi Guys,
cools PS Script!

I would have a question.
How can I integrate the objects from the tape jobs (Get-VBRTapeJob)?

Thanks

@jnaude123

This comment has been minimized.

Copy link

@jnaude123 jnaude123 commented Apr 17, 2020

Hi All, any idea why the "MyVeeamreport.ps1" isn't able to generate a report from Veeam V10?

@cemendoza1

This comment has been minimized.

Copy link

@cemendoza1 cemendoza1 commented Apr 20, 2020

@jnaude123 its not compatiblity whit veeam 10

@jnaude123

This comment has been minimized.

Copy link

@jnaude123 jnaude123 commented Apr 22, 2020

@cemendoza1 what would be an alternative to achieve the same reporting functionality?

@cemendoza1

This comment has been minimized.

Copy link

@cemendoza1 cemendoza1 commented Apr 22, 2020

@jnaude123 I have been looking for an alternative but I have not found anything similar yet, it is very sad since it was very useful

@dpearceFL

This comment has been minimized.

Copy link

@dpearceFL dpearceFL commented Apr 22, 2020

I've had it working on V10 for weeks making this change:

Get Veeam Version

$VeeamVersion = Get-VeeamVersion

If ($VeeamVersion -lt 10.0.0.4461) {
Write-Host "Script requires VBR v10" -ForegroundColor Red
Write-Host "Version detected - $VeeamVersion" -ForegroundColor Red
exit
}

@jnaude123

This comment has been minimized.

Copy link

@jnaude123 jnaude123 commented Apr 24, 2020

@cemendoza1

This comment has been minimized.

Copy link

@cemendoza1 cemendoza1 commented Apr 24, 2020

@dpearceFL
great contribution, add it to my reports and they work again only now it takes 10 min to generate ,but they work and that's the important thing.

muchas graciasmuchas gracias por todomuchas gracias amigomuchas gracias por la información

Thanks a lot

@dpearceFL

This comment has been minimized.

Copy link

@dpearceFL dpearceFL commented Apr 24, 2020

I wish I could give credit where credit is due. Being an old gray beard, I can't remember where I saw this patch first.

@NathanSlim

This comment has been minimized.

Copy link

@NathanSlim NathanSlim commented Jul 23, 2020

Thanks for the v10 tip.

Ran into the following error: This cmdlet is obsolete and no longer supported. To get computer backup job use "Get-VBRComputerbackupJob" instead"

Did a find/replace in notepad ++

replace Get-VBRJob with Get-VBRComputerbackupJob

Works now. Thanks for the great script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.