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 |
This comment has been minimized.
This comment has been minimized.
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.
|
This comment has been minimized.
This comment has been minimized.
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: Code: 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. |
This comment has been minimized.
This comment has been minimized.
Lovely Script. Thanks. Can we add all our Backup server in one script and merge it to one report. Nauman |
This comment has been minimized.
This comment has been minimized.
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. 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!! |
This comment has been minimized.
This comment has been minimized.
Great script, nice job on this. I would love to see a CSV output option |
This comment has been minimized.
This comment has been minimized.
Hi Shawn, thanks for a really nice report. P.S. Any possibility of getting jobtype "SqlLogBackup" included in the report ? |
This comment has been minimized.
This comment has been minimized.
Hey Bo
Unfortunately they never implemented PowerShell support for managed
agents...hopefully this will change in v10
…--shawn
On Mon, Oct 7, 2019 at 7:15 AM Bo Riis ***@***.***> wrote:
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
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<https://gist.github.com/9136468?email_source=notifications&email_token=ABFPTSMEOMFQMVBMZMJOQHDQNMK6FA5CNFSM4HXMP2RKYY3PNVWWK3TUL52HS4DFVNDWS43UINXW23LFNZ2KUY3PNVWWK3TUL5UWJTQAF2A3A#gistcomment-3047856>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABFPTSNE2SQSODCQFZRGVFLQNMK6FANCNFSM4HXMP2RA>
.
|
This comment has been minimized.
This comment has been minimized.
Hi Shawn, Can I use this script with Veeam 9.5 U4b? |
This comment has been minimized.
This comment has been minimized.
I believe it should work with the exception of managed agents,
…On Wed, Nov 6, 2019 at 10:40 PM jungkung6 ***@***.***> wrote:
Hi Shawn,
Can I use this script with Veeam 9.5 U4b?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<https://gist.github.com/9136468?email_source=notifications&email_token=ABFPTSP2MPQHPSQZ7Y2BHNLQSOE4VA5CNFSM4HXMP2RKYY3PNVWWK3TUL52HS4DFVNDWS43UINXW23LFNZ2KUY3PNVWWK3TUL5UWJTQAF3YOA#gistcomment-3076320>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABFPTSKX6OAD5WJE7JUCHKTQSOE4VANCNFSM4HXMP2RA>
.
|
This comment has been minimized.
This comment has been minimized.
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? |
This comment has been minimized.
This comment has been minimized.
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.
|
This comment has been minimized.
This comment has been minimized.
Kluge fix for the tape error message. Not tested with a tape drive.
|
This comment has been minimized.
This comment has been minimized.
Would you consider adding the possibility to exclude entire clusters? |
This comment has been minimized.
This comment has been minimized.
hi Guys, I would have a question. Thanks |
This comment has been minimized.
This comment has been minimized.
Hi All, any idea why the "MyVeeamreport.ps1" isn't able to generate a report from Veeam V10? |
This comment has been minimized.
This comment has been minimized.
@jnaude123 its not compatiblity whit veeam 10 |
This comment has been minimized.
This comment has been minimized.
@cemendoza1 what would be an alternative to achieve the same reporting functionality? |
This comment has been minimized.
This comment has been minimized.
@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 |
This comment has been minimized.
This comment has been minimized.
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) { |
This comment has been minimized.
This comment has been minimized.
@dpearceFL
Thank you for your input. Much appreciated
…On Wed, Apr 22, 2020 at 7:39 PM dpearceFL ***@***.***> wrote:
***@***.***FL* commented on this gist.
------------------------------
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
}
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<https://gist.github.com/9136468#gistcomment-3264790>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/APHHQQUD44Y3RDYU7NPQEN3RN4TT7ANCNFSM4HXMP2RA>
.
|
This comment has been minimized.
This comment has been minimized.
@dpearceFL muchas graciasmuchas gracias por todomuchas gracias amigomuchas gracias por la información Thanks a lot |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
this script is awesome, is there any way to exclude vm's they have a vCenter Tag? |
This comment has been minimized.
This comment has been minimized.
It is a wonderful script but unfortunately it is not executing in my environment. Any idea please....my Veeam Backup is on version 10A |
This comment has been minimized.
This comment has been minimized.
I have the same version of veeam 10a and it works fine just replace with the following Get Veeam Version $VeeamVersion = Get-VeeamVersion If ($VeeamVersion -lt 10.0.0.4461) { |
This comment has been minimized.
This comment has been minimized.
I love this script, I also had to make a couple changes for version 10 and still don't have everything working yet - Also had to change the VBR Version check that everyone else mentioned - One thing that I'm not sure of how to get working though that I've been trying to work on for a while is the reporting on both Linux Agent Backup jobs and Windows Agent Backup jobs. I know there are commands that can be run to at least query the last run date/time/status/etc. But unfortunately I'm not a powershell master like @smasterson so I can't figure out how to add that to the report so while I still have this report set to send me a daily email, I still have to log into my Veeam server to check those jobs. Can anyone help? Thanks, Ant |
This comment has been minimized.
This comment has been minimized.
Function Get-VBRSORepoInfo needs an update for v11+. https://forums.veeam.com/powershell-f26/v11-get-vbrbackuprepository-space-properties-t72415.html
|
This comment has been minimized.
this script is awesome, is there any way you can add exclude vm's with a certain tag?