Advertisement
brodiemr

VEEAM PowerShell Script

Jan 26th, 2017
762
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Add-PSSnapin "VeeamPSSnapIn" -ErrorAction SilentlyContinue
  2.  
  3. #region User-Variables
  4. # VBR Server (Server Name, FQDN or IP)
  5. $vbrServer = "ONEBYTE"
  6. # Report mode - valid modes: any number of hours, Weekly or Monthly
  7. # 24, 48, "Weekly", "Monthly"
  8. $reportMode = "Weekly"
  9. # Report Title
  10. $rptTitle = "MOO - Veeam backup report"
  11. # Append Report Mode to Report Title E.g. My Veeam Report (Last 24 Hours)
  12. $modeTitle = $true
  13. # Append VBR Server name to Report Title
  14. $vbrTitle = $false
  15. # Report Width in Pixels
  16. $rptWidth = 1024
  17.  
  18. # Location of Veeam executable (Veeam.Backup.Shell.exe)
  19. $veeamExePath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Shell.exe"
  20.  
  21. # Show Backup Session Summary
  22. $showSummaryBk = $true
  23. # Show VMs with no successful backups within time frame ($reportMode)
  24. $showUnprotectedVMs = $true
  25. # Show VMs with successful backups within time frame ($reportMode)
  26. $showProtectedVMs = $true
  27. # To Exclude VMs from Missing and Successful Backups section add VM names to be excluded
  28. # $excludevms = @("")
  29. $excludeVMs = @("")
  30. # Exclude VMs from Missing and Successful Backups section in the following (vCenter) folder(s)
  31. # $excludeFolder =  = @("folder1","folder2","*_testonly")
  32. $excludeFolder = @("")
  33. # Exclude VMs from Missing and Successful Backups section in the following (vCenter) datacenter(s)
  34. # $excludeDC =  = @("")
  35. $excludeDC = @("")
  36. # Show Backup Job Status
  37. $showJobsBk = $true
  38. # Show Running Backup jobs
  39. $showRunningBk = $true
  40. # Only show last session for each Backup Job
  41. $onlyLastBk = $false
  42. # Show detailed information for Backup Jobs/Sessions (Avg Speed, Total(GB), Processed(GB), Read(GB), Transferred(GB))
  43. $showDetailedBk = $true
  44. # Show Backup Sessions w/Warnings or Failures within time frame ($reportMode)
  45. $showWarnFailBk = $true
  46. # Show Successful Backup Sessions within time frame ($reportMode)
  47. $showSuccessBk = $true
  48. # Show Running Restore VM Sessions within time frame ($reportMode)
  49. $showRestoRunVM = $true
  50. # Show Completed Restore VM Sessions within time frame ($reportMode)
  51. $showRestoreVM = $true
  52. # Show Endpoint Backup Session Summary
  53. $showSummaryEp = $true
  54. # Show Endpoint Backup Job Status
  55. $showJobsEp = $true
  56. # Show Running Endpoint Backup jobs
  57. $showRunningEp = $true
  58. # Only show last session for each Endpoint Backup Job
  59. $onlyLastEp = $false
  60. # Show Endpoint Backup Sessions w/Warnings or Failures within time frame ($reportMode)
  61. $showWarnFailEp = $true
  62. # Show Successful Endpoint Backup Sessions within time frame ($reportMode)
  63. $showSuccessEp = $true
  64. # Show Repository Info
  65. $showRepo = $true
  66. # Show Proxy Info
  67. $showProxy = $true
  68. # Show Replica Target Info
  69. $showReplica = $true
  70. # Show Veeam Services Info (Windows Services)
  71. $showServices = $true
  72. # Show only Services that are NOT running
  73. $hideRunningSvc = $false
  74. # Show License expiry info
  75. $showLicExp = $true
  76.  
  77. # Save output to a file - $true or $false
  78. $saveFile = $true
  79. # File output path and filename
  80. $outFile = "C:\temp\veeam reports\MyVeeamReport_$(Get-Date -format MMddyyyy_hhmmss).htm"
  81. # Launch file after creation - $true or $false
  82. $launchFile = $false
  83.  
  84. # Email configuration
  85. $sendEmail = $true
  86. $emailHost = ""
  87. $emailUser = "moo"
  88. $emailPass = "z0mgl33th4x0rz"
  89. $emailFrom = "backups@onebyte.org"
  90. $emailTo = "backups@onebyte.org"
  91. # Send report as attachment - $true or $false
  92. $emailAttach = $false
  93. # Email Subject
  94. $emailSubject = $rptTitle
  95. # Append Report Mode to Email Subject E.g. My Veeam Report (Last 24 Hours)
  96. $modeSubject = $true
  97. # Append VBR Server name to Email Subject
  98. $vbrSubject = $true
  99.  
  100. # Highlighting Thresholds
  101. # Repository Free Space Remaining %
  102. $repoCritical = 7
  103. $repoWarn = 10
  104. # Replica Target Free Space Remaining %
  105. $replicaCritical = 10
  106. $replicaWarn = 20
  107. # License Days Remaining
  108. $licenseCritical = 30
  109. $licenseWarn = 90
  110. #endregion
  111.  
  112. #region VersionInfo
  113. $MVRversion = "9.0.1"
  114. #
  115. # Version 9.0.1 - SM
  116. # Inital version for VBR v9
  117. # Updated version to follow VBR version (VeeamMajorVersion.VeeamUpdateVersion.MVRVersion)
  118. # Fixed Proxy Information (change in property names in v9)
  119. # Rewrote Repository Info to use newly available properties (yay!)
  120. # Updated Get-VMsBackupStatus to remove obsolete commandlet warning (Thanks tsightler!)
  121. # Added ability to run from console only install
  122. # Added ability to include VBR server in report title and email subject
  123. # Rewrote License Info gathering to allow remote info gathering
  124. # Misc minor tweaks/cleanup
  125. #
  126. # Version 2.0 - SM
  127. # Misc minor tweaks/cleanup
  128. # Proxy host IP info now always returns IPv4 address
  129. # Added ability to query Veeam database for Repository size info
  130. #   Big thanks to tsightler - http://forums.veeam.com/powershell-f26/get-vbrbackuprepository-why-no-size-info-t27296.html
  131. # Added report section - Backup Job Status
  132. # Added option to show detailed Backup Job/Session information (Avg Speed, Total(GB), Processed(GB), Read(GB), Transferred(GB))
  133. # Added report section - Running VM Restore Sessions
  134. # Added report section - Completed VM Restore Sessions
  135. # Added report section - Endpoint Backup Results Summary
  136. # Added report section - Endpoint Backup Job Status
  137. # Added report section - Running Endpoint Backup Jobs
  138. # Added report section - Endpoint Backup Jobs/Sessions with Warnings or Failures
  139. # Added report section - Successful Endpoint Backup Jobs/Sessions
  140. #
  141. # Version 1.4.1 - SM
  142. # Fixed issue with summary counts
  143. # Version 1.4 - SM
  144. # Misc minor tweaks/cleanup
  145. # Added variable for report width
  146. # Added variable for email subject
  147. # Added ability to show/hide all report sections
  148. # Added Protected/Unprotected VM Count to Summary
  149. # Added per object details for sessions w/no details
  150. # Added proxy host name to Proxy Details
  151. # Added repository host name to Repository Details
  152. # Added section showing successful sessions
  153. # Added ability to view only last session per job
  154. # Added Cluster field for protected/unprotected VMs
  155. # Added catch for cifs repositories greater than 4TB as erroneous data is returned
  156. # Added % Complete for Running Jobs
  157. # Added ability to exclude multiple (vCenter) folders from Missing and Successful Backups section
  158. # Added ability to exclude multiple (vCenter) datacenters from Missing and Successful Backups section
  159. # Tweaked license info for better reporting across different date formats
  160. #
  161. # Version 1.3 - SM
  162. # Now supports VBR v8
  163. # For VBR v7, use report version 1.2
  164. # Added more flexible options to save and launch file
  165. #
  166. # Version 1.2 - SM
  167. # Added option to show VMs Successfully backed up
  168. #
  169. # Version 1.1.4 - SM
  170. # Misc tweaks/bug fixes
  171. # Reconfigured HTML a bit to help with certain email clients
  172. # Added cell coloring to highlight status
  173. # Added $rptTitle variable to hold report title
  174. # Added ability to send report via email as attachment
  175. #
  176. # Version 1.1.3 - SM
  177. # Added Details to Sessions with Warnings or Failures
  178. #
  179. # Version 1.1.2 - SM
  180. # Minor tweaks/updates
  181. # Added Veeam version info to header
  182. #
  183. # Version 1.1.1 - Shawn Masterson
  184. # Based on vPowerCLI v6 Army Report (v1.1) by Thomas McConnell
  185. # http://www.vpowercli.co.uk/2012/01/23/vpowercli-v6-army-report/
  186. # http://pastebin.com/6p3LrWt7
  187. #
  188. # Tweaked HTML header (color, title)
  189. #
  190. # Changed report width to 1024px
  191. #
  192. # Moved hard-coded path to exe/dll files to user declared variables ($veeamExePath/$veeamDllPath)
  193. #
  194. # Adjusted sorting on all objects
  195. #
  196. # Modified info group/counts
  197. #   Modified - Total Jobs = Job Runs
  198. #   Added - Read (GB)
  199. #   Added - Transferred (GB)
  200. #   Modified - Warning = Warnings
  201. #   Modified - Failed = Failures
  202. #   Added - Failed (last session)
  203. #   Added - Running (currently running sessions)
  204. #
  205. # Modified job lines
  206. #   Renamed Header - Sessions with Warnings or Failures
  207. #   Fixed Write (GB) - Broke with v7
  208. #  
  209. # Added support license renewal
  210. #   Credit - Gavin Townsend  http://www.theagreeablecow.com/2012/09/sysadmin-modular-reporting-samreports.html
  211. #   Original  Credit - Arne Fokkema  http://ict-freak.nl/2011/12/29/powershell-veeam-br-get-total-days-before-the-license-expires/
  212. #
  213. # Modified Proxy section
  214. #   Removed Read/Write/Util - Broke in v7 - Workaround unknown
  215. #
  216. # Modified Services section
  217. #   Added - $runningSvc variable to toggle displaying services that are running
  218. #   Added - Ability to hide section if no results returned (all services are running)
  219. #   Added - Scans proxies and repositories as well as the VBR server for services
  220. #
  221. # Added VMs Not Backed Up section
  222. #   Credit - Tom Sightler - http://sightunseen.org/blog/?p=1
  223. #   http://www.sightunseen.org/files/vm_backup_status_dev.ps1
  224. #  
  225. # Modified $reportMode
  226. #   Added ability to run with any number of hours (8,12,72 etc)
  227. #   Added bits to allow for zero sessions (semi-gracefully)
  228. #
  229. # Added Running Jobs section
  230. #   Added ability to toggle displaying running jobs
  231. #
  232. # Added catch to ensure running v7 or greater
  233. #
  234. #
  235. # Version 1.1
  236. # Added job lines as per a request on the website
  237. #
  238. # Version 1.0
  239. # Clean up for release
  240. #
  241. # Version 0.9
  242. # More cmdlet rewrite to improve perfomace, credit to @SethBartlett
  243. # for practically writing the Get-vPCRepoInfo
  244. #
  245. # Version 0.8
  246. # Added Read/Write stats for proxies at requests of @bsousapt
  247. # Performance improvement of proxy tear down due to rewrite of cmdlet
  248. # Replaced 2 other functions
  249. # Added Warning counter, .00 to all storage returns and fetch credentials for
  250. # remote WinLocal repos
  251. #
  252. # Version 0.7
  253. # Added Utilisation(Get-vPCDailyProxyUsage) and Modes 24, 48, Weekly, and Monthly
  254. # Minor performance tweaks
  255.  
  256. #endregion
  257.  
  258. #region NonUser-Variables
  259. # Disconnect any existing VBR Server connection
  260. Disconnect-VBRServer
  261. # Connect to VBR Server
  262. Connect-VBRServer -server $vbrServer
  263. # Get VBR Server object
  264. $vbrserverobj = Get-VBRLocalhost
  265. # Get all Proxies
  266. $viProxyList = Get-VBRViProxy
  267. # Get all Repositories
  268. $repoList = Get-VBRBackupRepository
  269. # Get all Sessions (Backup/BackupCopy/Replica)
  270. $allSesh = Get-VBRBackupSession
  271. # Get all Restore Sessions
  272. $allResto = Get-VBRRestoreSession
  273.  
  274. # Convert mode (timeframe) to hours
  275. If ($reportMode -eq "Monthly") {
  276.         $HourstoCheck = 720
  277. } Elseif ($reportMode -eq "Weekly") {
  278.         $HourstoCheck = 168
  279. } Else {
  280.         $HourstoCheck = $reportMode
  281. }
  282.  
  283. # Gather Backup jobs
  284. $allJobsBk = @(Get-VBRJob | ? {$_.JobType -eq "Backup"})
  285.  
  286. # Gather all Backup sessions within timeframe
  287. $seshListBk = @($allSesh | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)) -and $_.JobType -eq "Backup"})
  288.  
  289. # Get Backup session information
  290. $totalxferBk = 0
  291. $totalReadBk = 0
  292. $seshListBk | %{$totalxferBk += $([Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2))}
  293. $seshListBk | %{$totalReadBk += $([Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2))}
  294. If ($onlyLastBk) {
  295.     $tempSeshListBk = $seshListBk
  296.     $seshListBk = @()
  297.     Foreach($job in (Get-VBRJob | ? {$_.JobType -eq "Backup"})) {
  298.         $seshListBk += $TempSeshListBk | ?{$_.Jobname -eq $job.name} | Sort-Object CreationTime -Descending | Select-Object -First 1
  299.     }
  300. }
  301. $successSessionsBk = @($seshListBk | ?{$_.Result -eq "Success"})
  302. $warningSessionsBk = @($seshListBk | ?{$_.Result -eq "Warning"})
  303. $failsSessionsBk = @($seshListBk | ?{$_.Result -eq "Failed"})
  304. $runningSessionsBk = @($allSesh | ?{$_.State -eq "Working" -and $_.JobType -eq "Backup"})
  305. $failedSessionsBk = @($seshListBk | ?{($_.Result -eq "Failed") -and ($_.WillBeRetried -ne "True")})
  306.  
  307. # Gather VM Restore sessions within timeframe
  308. $seshListResto = @($allResto | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck))})
  309.  
  310. # Get VM Restore session information
  311. $completeResto = @($seshListResto | ?{$_.IsCompleted})
  312. $runningResto = @($seshListResto | ?{!($_.IsCompleted)})
  313.  
  314. # Gather Endpoint Backup jobs
  315. $allJobsEp = @(Get-VBREPJob)
  316.  
  317. # Gather all Endpoint Backup sessions within timeframe
  318. $allSeshEp = Get-VBREPSession
  319. $seshListEp = $allSeshEp | ?{$_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)}
  320. If ($onlyLastEp) {
  321.     $tempSeshListEp = $seshListEp
  322.     $seshListEp = @()
  323.     Foreach($job in $allJobsEp) {
  324.         $seshListEp += $TempSeshListEp | ?{$_.JobId -eq $job.Id} | Sort-Object CreationTime -Descending | Select-Object -First 1
  325.     }
  326. }
  327. $successSessionsEp = @($seshListEp | ?{$_.Result -eq "Success"})
  328. $warningSessionsEp = @($seshListEp | ?{$_.Result -eq "Warning"})
  329. $failsSessionsEp = @($seshListEp | ?{$_.Result -eq "Failed"})
  330. $runningSessionsEp = @($allSeshEp | ?{$_.State -eq "Working"})
  331.  
  332. #Get Replica jobs
  333. $repList = @(Get-VBRJob | ?{$_.IsReplica})
  334.  
  335. # Append Report Mode to Report Title
  336. If ($modeTitle) {
  337.     If (($reportMode -ne "Weekly") -And ($reportMode -ne "Monthly")) {
  338.             $rptTitle = "$rptTitle (Last $reportMode Hrs)"
  339.     } Else {
  340.             $rptTitle = "$rptTitle ($reportMode)"
  341.     }
  342. }
  343.  
  344. # Append VBR Server to Report Title
  345. If ($vbrTitle) {
  346.     $rptTitle = "$rptTitle - $vbrserver"
  347. }
  348.  
  349. # Append Report Mode to Email subject
  350. If ($modeSubject) {
  351.     If (($reportMode -ne "Weekly") -And ($reportMode -ne "Monthly")) {
  352.             $emailSubject = "$emailSubject (Last $reportMode Hrs)"
  353.     } Else {
  354.             $emailSubject = "$emailSubject ($reportMode)"
  355.     }
  356. }
  357.  
  358. # Append VBR Server to Email subject
  359. If ($vbrSubject) {
  360.     $emailSubject = "$emailSubject - $vbrserver"
  361. }
  362. #endregion
  363.  
  364. #region Functions
  365.  
  366. Function Get-vPCProxyInfo {
  367.     $vPCObjAry = @()
  368.     Function Build-vPCObj {param ([PsObject]$inputObj)
  369.             $ping = new-object system.net.networkinformation.ping
  370.             $DNS = [Net.DNS]::GetHostEntry("$($inputObj.Host.Name)")
  371.             $IPv4 = ($DNS.get_AddressList() | Where {$_.AddressFamily -eq "InterNetwork"} | Select -First 1).IPAddressToString
  372.             $pinginfo = $ping.send("$($IPv4)")
  373.            
  374.             If ($pinginfo.Status -eq "Success") {
  375.                     $hostAlive = "Alive"
  376.             } Else {
  377.                     $hostAlive = "Dead"
  378.             }
  379.            
  380.             $vPCFuncObject = New-Object PSObject -Property @{
  381.                     ProxyName = $inputObj.Name
  382.                     RealName = $inputObj.Host.Name.ToLower()
  383.                     Disabled = $inputObj.IsDisabled
  384.                     Status  = $hostAlive
  385.                     IP = $IPv4
  386.                     Responce = $pinginfo.RoundtripTime
  387.             }
  388.             Return $vPCFuncObject
  389.     }  
  390.     Get-VBRViProxy | %{$vPCObjAry += $(Build-vPCObj $_)}
  391.     $vPCObjAry
  392. }
  393.  
  394. Function Get-vPCRepoInfo {
  395. [CmdletBinding()]
  396.         param (
  397.                 [Parameter(Position=0, ValueFromPipeline=$true)]
  398.                 [PSObject[]]$Repository
  399.                 )
  400.         Begin {
  401.                 $outputAry = @()
  402.                 Function Build-Object {param($name, $repohost, $path, $free, $total)
  403.                         $repoObj = New-Object -TypeName PSObject -Property @{
  404.                                         Target = $name
  405.                                         RepoHost = $repohost
  406.                                         Storepath = $path
  407.                                         StorageFree = [Math]::Round([Decimal]$free/1GB,2)
  408.                                         StorageTotal = [Math]::Round([Decimal]$total/1GB,2)
  409.                                         FreePercentage = [Math]::Round(($free/$total)*100)
  410.                                 }
  411.                         Return $repoObj | Select Target, RepoHost, Storepath, StorageFree, StorageTotal, FreePercentage
  412.                 }
  413.         }
  414.         Process {
  415.                 Foreach ($r in $Repository) {
  416.                     # Refresh Repository Size Info
  417.                     [Veeam.Backup.Core.CBackupRepositoryEx]::SyncSpaceInfoToDb($r, $true)
  418.                    
  419.                     If ($r.HostId -eq "00000000-0000-0000-0000-000000000000") {
  420.                         $HostName = ""
  421.                     }
  422.                     Else {
  423.                         $HostName = $($r.GetHost()).Name.ToLower()
  424.                     }
  425.                     $outputObj = Build-Object $r.Name $Hostname $r.Path $r.info.CachedFreeSpace $r.Info.CachedTotalSpace
  426.                     }
  427.                 $outputAry += $outputObj
  428.         }
  429.         End {
  430.                 $outputAry
  431.         }
  432. }
  433.  
  434. Function Get-vPCReplicaTarget {
  435. [CmdletBinding()]
  436.     param(
  437.         [Parameter(ValueFromPipeline=$true)]
  438.         [PSObject[]]$InputObj
  439.     )
  440.     BEGIN {
  441.         $outputAry = @()
  442.         $dsAry = @()
  443.         If (($Name -ne $null) -and ($InputObj -eq $null)) {
  444.                 $InputObj = Get-VBRJob -Name $Name
  445.         }
  446.     }
  447.     PROCESS {
  448.         Foreach ($obj in $InputObj) {
  449.                         If (($dsAry -contains $obj.ViReplicaTargetOptions.DatastoreName) -eq $false) {
  450.                         $esxi = $obj.GetTargetHost()
  451.                             $dtstr =  $esxi | Find-VBRViDatastore -Name $obj.ViReplicaTargetOptions.DatastoreName    
  452.                             $objoutput = New-Object -TypeName PSObject -Property @{
  453.                                     Target = $esxi.Name
  454.                                     Datastore = $obj.ViReplicaTargetOptions.DatastoreName
  455.                                     StorageFree = [Math]::Round([Decimal]$dtstr.FreeSpace/1GB,2)
  456.                                     StorageTotal = [Math]::Round([Decimal]$dtstr.Capacity/1GB,2)
  457.                                     FreePercentage = [Math]::Round(($dtstr.FreeSpace/$dtstr.Capacity)*100)
  458.                             }
  459.                             $dsAry = $dsAry + $obj.ViReplicaTargetOptions.DatastoreName
  460.                             $outputAry = $outputAry + $objoutput
  461.                         }
  462.                         Else {
  463.                                 return
  464.                         }
  465.         }
  466.     }
  467.     END {
  468.                 $outputAry | Select Target, Datastore, StorageFree, StorageTotal, FreePercentage
  469.     }
  470. }
  471.  
  472. Function Get-VeeamVersion {
  473.     $veeamExe = Get-Item $veeamExePath
  474.     $VeeamVersion = $veeamExe.VersionInfo.ProductVersion
  475.     Return $VeeamVersion
  476. }
  477.  
  478. Function Get-VeeamSupportDate {
  479.     param (
  480.         [string]$vbrserver)
  481.    
  482.     # Query (remote) registry with WMI for license info
  483.     Try{
  484.         $wmi = get-wmiobject -list "StdRegProv" -namespace root\default -computername $vbrserver -ErrorAction Stop
  485.         $hklm = 2147483650
  486.         $bKey = "SOFTWARE\Veeam\Veeam Backup and Replication\license"
  487.         $bValue = "Lic1"
  488.         $regBinary = ($wmi.GetBinaryValue($hklm, $bKey, $bValue)).uValue
  489.         $veeamLicInfo = [string]::Join($null, ($regBinary | % { [char][int]$_; }))     
  490.  
  491.         # Convert Binary key
  492.         $pattern = "expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}"
  493.         $expirationDate = [regex]::matches($VeeamLicInfo, $pattern)[0].Value.Split("=")[1]
  494.         $datearray = $expirationDate -split '/'
  495.         $expirationDate = Get-Date -Day $datearray[0] -Month $datearray[1] -Year $datearray[2]
  496.         $totalDaysLeft = ($expirationDate - (get-date)).Totaldays.toString().split(",")[0]
  497.         $totalDaysLeft = [int]$totalDaysLeft
  498.         $objoutput = New-Object -TypeName PSObject -Property @{
  499.             ExpDate = $expirationDate.ToShortDateString()
  500.             DaysRemain = $totalDaysLeft
  501.         }
  502.        
  503.     }
  504.     Catch{
  505.         $objoutput = New-Object -TypeName PSObject -Property @{
  506.             ExpDate = "Failed"
  507.             DaysRemain = "Failed"
  508.         }
  509.        
  510.     }
  511.     $objoutput
  512. }
  513.  
  514. Function Get-VeeamServers {
  515.     $vservers=@{}
  516.     $outputAry = @()
  517.     $vservers.add($($script:vbrserverobj.Name),"VBRServer")
  518.     Foreach ($srv in $script:viProxyList) {
  519.         If (!$vservers.ContainsKey($srv.Host.Name)) {
  520.           $vservers.Add($srv.Host.Name,"ProxyServer")
  521.         }
  522.     }
  523.     Foreach ($srv in $script:repoList) {
  524.         If (!$vservers.ContainsKey($srv.gethost().Name)) {
  525.           $vservers.Add($srv.gethost().Name,"RepoServer")
  526.         }
  527.     }
  528.     $vservers = $vservers.GetEnumerator() | Sort-Object Name
  529.     Foreach ($vserver in $vservers) {
  530.         $outputAry += $vserver.Name
  531.     }
  532.     return $outputAry
  533. }
  534.  
  535. Function Get-VeeamServices {
  536.     param (
  537.       [PSObject]$inputObj)
  538.      
  539.     $outputAry = @()
  540.     Foreach ($obj in $InputObj) {    
  541.         $output = Get-Service -computername $obj -Name "*Veeam*" -exclude "SQLAgent*" |
  542.         Select @{Name="Server Name"; Expression = {$obj.ToLower()}}, @{Name="Service Name"; Expression = {$_.DisplayName}}, Status
  543.         $outputAry = $outputAry + $output  
  544.     }
  545. $outputAry
  546. }
  547.  
  548. Function Get-VMsBackupStatus {
  549.     # Convert exclusion list to simple regular expression
  550.     $excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  551.     $excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  552.     $excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  553.    
  554.     $vms=@{}
  555.  
  556.     # Build a hash table of all VMs.  Key is either Job Object Id (for any VM ever in a Veeam job) or vCenter ID+MoRef
  557.     # Assume unprotected (!), and populate Cluster, DataCenter, and Name fields for hash key value
  558.     Find-VBRViEntity |
  559.         Where-Object {$_.Type -eq "Vm" -and $_.VmFolderName -notmatch $excludefolder_regex} |
  560.         Where-Object {$_.Name -notmatch $excludevms_regex} |
  561.         Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
  562.         ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[0], $_.Path.Split("\")[1], $_.Path.Split("\")[2], $_.Name))}
  563.        
  564.     Find-VBRViEntity -VMsandTemplates |
  565.         Where-Object {$_.Type -eq "Vm" -and $_.IsTemplate -eq "True" -and $_.VmFolderName -notmatch $excludefolder_regex} |
  566.         Where-Object {$_.Name -notmatch $excludevms_regex} |
  567.         Where-Object {$_.Path.Split("\")[1] -notmatch $excludedc_regex} |
  568.         ForEach {$vms.Add(($_.FindObject().Id, $_.Id -ne $null)[0], @("!", $_.Path.Split("\")[0], $_.Path.Split("\")[1], $_.VmHostName, "[template] $($_.Name)"))}
  569.      
  570.     # Find all backup task sessions that have ended in the last x hours and not ending in Failure
  571.     $vbrtasksessions = (Get-VBRBackupSession |
  572.         Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}) |
  573.         Get-VBRTaskSession | Where-Object {$_.Status -ne "Failed"}
  574.  
  575.     # Compare VM list to session list and update found VMs status to "Protected"
  576.     If ($vbrtasksessions) {
  577.       Foreach ($vmtask in $vbrtasksessions) {
  578.          If($vms.ContainsKey($vmtask.Info.ObjectId)) {
  579.             $vms[$vmtask.Info.ObjectId][0]=$vmtask.JobName
  580.          }
  581.       }
  582.    }
  583.    $vms.GetEnumerator() | Sort-Object Value
  584. }
  585.  
  586. Function Get-VMsMissingBackup {
  587.     param (
  588.         $vms)
  589.    
  590.     $outputary = @()  
  591.     Foreach ($vm in $vms) {
  592.       If ($vm.Value[0] -eq "!") {
  593.         $objoutput = New-Object -TypeName PSObject -Property @{
  594.         vCenter = $vm.Value[1]
  595.         Datacenter = $vm.Value[2]
  596.         Cluster = $vm.Value[3]
  597.         Name = $vm.Value[4]
  598.         }
  599.         $outputAry += $objoutput
  600.       }
  601.     }
  602.     $outputAry | Select vCenter, Datacenter, Cluster, Name
  603. }
  604.  
  605. Function Get-VMsSuccessBackup {
  606.     param (
  607.         $vms)
  608.    
  609.     $outputary = @()  
  610.     Foreach ($vm in $vms) {
  611.       If ($vm.Value[0] -ne "!") {
  612.         $objoutput = New-Object -TypeName PSObject -Property @{
  613.         vCenter = $vm.Value[1]
  614.         Datacenter = $vm.Value[2]
  615.         Cluster = $vm.Value[3]
  616.         Name = $vm.Value[4]
  617.         }
  618.         $outputAry += $objoutput
  619.       }
  620.     }
  621.     $outputAry | Select vCenter, Datacenter, Cluster, Name
  622. }
  623.  
  624. #endregion
  625.  
  626. #region Report
  627. # Get Veeam Version
  628. $VeeamVersion = Get-VeeamVersion
  629.  
  630. If ($VeeamVersion -lt 9) {
  631.     Write-Host "Script requires VBR v9" -ForegroundColor Red
  632.     exit
  633. }
  634.  
  635. # HTML Stuff
  636. $headerObj = @"
  637. <html>
  638.        <head>
  639.                <title>$rptTitle</title>
  640.                <style>  
  641.                        body {font-family: Tahoma; background-color:#fff;}
  642.                         table {font-family: Tahoma;width: $($rptWidth)px;font-size: 12px;border-collapse:collapse;}
  643.                        <!-- table tr:nth-child(odd) td {background: #e2e2e2;} -->
  644.                         th {background-color: #cccc99;border: 1px solid #a7a9ac;border-bottom: none;}
  645.                        td {background-color: #ffffff;border: 1px solid #a7a9ac;padding: 2px 3px 2px 3px;vertical-align: top;}
  646.                </style>
  647.        </head>
  648. "@
  649.  
  650. $bodyTop = @"
  651.        <body>
  652.             <center>
  653.                <table cellspacing="0" cellpadding="0">
  654.                        <tr>
  655.                                <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>
  656.                                 <td style="width: 20%;height: 45px;border: none;background-color: #003366;color: White;font-size: 12px;vertical-align:text-top;text-align:right;padding: 2px 3px 2px 3px;">MVR v$MVRversion</td>
  657.                         </tr>
  658.                         <tr>
  659.                                 <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 at $(Get-Date -format g) on $((gc env:computername).ToLower())</td>
  660.                                 <td style="width: 20%;height: 35px;border: none;background-color: #003366;color: White;font-size: 10px;vertical-align:bottom;text-align:right;padding: 2px 3px 2px 3px;">Veeam v$VeeamVersion</td>
  661.                         </tr>
  662.                 </table>
  663. "@
  664.  
  665. $subHead01 = @"
  666.                 <table>
  667.                         <tr>
  668.                                 <td style="height: 35px;background-color: #eeeeee;color: #003366;font-size: 16px;font-weight: bold;vertical-align: middle;padding: 5px 0 0 15px;border-top: 1px solid #cccc99;border-bottom: none;">
  669. "@
  670.  
  671. $subHead01err = @"
  672.                 <table>
  673.                         <tr>
  674.                                 <td style="height: 35px;background-color: #FF0000;color: #003366;font-size: 16px;font-weight: bold;vertical-align: middle;padding: 5px 0 0 15px;border-top: 1px solid #cccc99;border-bottom: none;">
  675. "@
  676.  
  677. $subHead02 = @"
  678.                                 </td>
  679.                         </tr>
  680.                 </table>
  681. "@
  682.  
  683. $footerObj = @"
  684. </center>
  685. </body>
  686. </html>
  687. "@
  688.  
  689. #Get VM Backup Status
  690. $vmstatus = Get-VMsBackupStatus
  691.  
  692. # VMs Missing Backups
  693. $missingVMs = @(Get-VMsMissingBackup $vmstatus)
  694.  
  695. # VMs Successfuly Backed Up
  696. $successVMs = @(Get-VMsSuccessBackup $vmstatus)
  697.  
  698. # Get Backup Summary Info
  699. $bodySummaryBk = $null
  700. If ($showSummaryBk) {
  701.     $vbrMasterHash = @{
  702.         "Coordinator" = $vbrServer
  703.         "Failed" = @($failedSessionsBk).Count
  704.         "Sessions" = If ($seshListBk) {@($seshListBk).Count} Else {0}
  705.         "Read" = $totalReadBk
  706.         "Transferred" = $totalXferBk
  707.         "Successful" = @($successSessionsBk).Count
  708.         "Warning" = @($warningSessionsBk).Count
  709.         "Fails" = @($failsSessionsBk).Count
  710.         "Running" = @($runningSessionsBk).Count
  711.         "SuccessVM" = @($successVMs).Count
  712.         "FailedVM" = @($missingVMs).Count
  713.     }
  714.     $vbrMasterObj = New-Object -TypeName PSObject -Property $vbrMasterHash
  715.  
  716.     If ($onlyLastBk) {
  717.         $bodySummaryBk =  $vbrMasterObj | Select @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
  718.         @{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Jobs Run"; Expression = {$_.Sessions}},
  719.         @{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
  720.         @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  721.         @{Name="Warnings"; Expression = {$_.Warning}},
  722.         @{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
  723.     } Else {
  724.         $bodySummaryBk =  $vbrMasterObj | Select @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
  725.         @{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Total Sessions"; Expression = {$_.Sessions}},
  726.         @{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
  727.         @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  728.         @{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}},
  729.         @{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
  730.     }
  731.                    
  732.     $bodySummaryBk = $subHead01 + "Backup Results Summary" + $subHead02 + $bodySummaryBk
  733. }
  734.  
  735. # Get VMs Missing Backups
  736. $bodyMissing = $null
  737. If ($showUnprotectedVMs) { 
  738.     If ($missingVMs -ne $null) {
  739.         $missingVMs = $missingVMs | Sort vCenter, Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
  740.         $bodyMissing = $subHead01err + "VMs with No Successful Backups" + $subHead02 + $missingVMs
  741.     }
  742. }
  743.  
  744. # Get VMs Successfuly Backed Up
  745. $bodySuccess = $null
  746. If ($showProtectedVMs) {   
  747.     If ($successVMs -ne $null) {
  748.         $successVMs = $successVMs | Sort vCenter, Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
  749.         $bodySuccess = $subHead01 + "VMs with Successful Backups" + $subHead02 + $successVMs
  750.     }
  751. }
  752.  
  753. # Get Backup Job Status
  754. $bodyJobsBk = @()
  755. If ($showJobsBk) {
  756.        If ($allJobsBk.count -gt 0) {
  757.            Foreach($bkJob in $allJobsBk) {
  758.                 $bodyJobsBk += $bkJob | Select @{Name="Job Name"; Expression = {$_.Name}},
  759.                 @{Name="Enabled"; Expression = {$_.Info.IsScheduleEnabled}},
  760.                 @{Name="Status"; Expression = {If ($_.IsRunning) {"Working"} Else {"Stopped"}}},
  761.                 @{Name="Target Repo"; Expression = {$(Get-VBRBackupRepository | Where {$_.Id -eq $bkJob.Info.TargetRepositoryId}).Name}},
  762.                 @{Name="Next Run"; Expression = {If ($_.ScheduleOptions.IsContinious) {"<Continious>"}
  763.                     ElseIf ($_.ScheduleOptions.NextRun) {$_.ScheduleOptions.NextRun}
  764.                     ElseIf ($_.IsScheduleEnabled -eq $false) {"<Disabled>"}
  765.                     ElseIf ($_.ScheduleOptions.OptionsScheduleAfterJob.IsEnabled) {"After [" + $(Get-VBRJob | Where {$_.Id -eq $bkJob.Info.ParentScheduleId}).Name + "]"}
  766.                     Else {"<not scheduled>"}}},
  767.                 @{Name="Last Result"; Expression = {If ($_.Info.LatestStatus -eq "None"){""}Else{$_.Info.LatestStatus}}}
  768.             }
  769.             $bodyJobsBk = $bodyJobsBk | Sort "Next Run" | ConvertTo-HTML -Fragment
  770.             $bodyJobsBk = $subHead01 + "Backup Job Status" + $subHead02 + $bodyJobsBk
  771.        }
  772. }
  773.  
  774. # Get Running Backup Jobs
  775. $bodyRunningBk = $null
  776. If ($showRunningBk) {
  777.        If ($runningSessionsBk.count -gt 0) {
  778.                $bodyRunningBk = $runningSessionsBk | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  779.                @{Name="Start Time"; Expression = {$_.CreationTime}},
  780.                @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.Progress.StartTime) $(Get-Date)).TotalMinutes,2)}},
  781.                 @{Name="% Complete"; Expression = {$_.Progress.Percents}},
  782.                @{Name="Read (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2)}},
  783.                @{Name="Write (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2)}} | ConvertTo-HTML -Fragment
  784.                $bodyRunningBk = $subHead01 + "Running Backup Jobs" + $subHead02 + $bodyRunningBk
  785.        }
  786. }
  787.  
  788. # Get Backup Sessions with Failures or Warnings
  789. $bodySessWFBk = $null
  790. If ($showWarnFailBk) {
  791.        $sessWF = @($warningSessionsBk + $failsSessionsBk)
  792.        If ($sessWF.count -gt 0) {
  793.                 If ($onlyLastBk) {
  794.                     $headerWF = "Backup Jobs with Warnings or Failures"
  795.                 } Else {
  796.                     $headerWF = "Backup Sessions with Warnings or Failures"
  797.                 }
  798.                If ($showDetailedBk) {
  799.                     $bodySessWFBk = $sessWF | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  800.                     @{Name="Start Time"; Expression = {$_.CreationTime}},
  801.                     @{Name="Stop Time"; Expression = {$_.EndTime}},
  802.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},                   
  803.                     @{Name="Avg Speed (MB/s)"; Expression = {[Math]::Round($_.Info.Progress.AvgSpeed/1MB,2)}},
  804.                     @{Name="Total (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedSize/1GB,2)}},
  805.                     @{Name="Processed (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedUsedSize/1GB,2)}},
  806.                     @{Name="Data Read (GB)"; Expression = {[Math]::Round($_.Info.Progress.ReadSize/1GB,2)}},
  807.                     @{Name="Transferred (GB)"; Expression = {[Math]::Round($_.Info.Progress.TransferedSize/1GB,2)}},                   
  808.                     @{Name="Details"; Expression = {
  809.                     If ($_.GetDetails() -eq ""){($_.GetDetails()).Replace("<br />"," - ") + ($_ | Get-VBRTaskSession | %{If ($_.GetDetails()){$_.Name + ": " + $_.GetDetails()}})}
  810.                     Else {($_.GetDetails()).Replace("<br />"," - ")}}},
  811.                     Result  | ConvertTo-HTML -Fragment
  812.                     $bodySessWFBk = $subHead01 + $headerWF + $subHead02 + $bodySessWFBk
  813.                 } Else {
  814.                     $bodySessWFBk = $sessWF | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  815.                     @{Name="Start Time"; Expression = {$_.CreationTime}},
  816.                     @{Name="Stop Time"; Expression = {$_.EndTime}},
  817.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
  818.                     @{Name="Details"; Expression = {
  819.                     If ($_.GetDetails() -eq ""){($_.GetDetails()).Replace("<br />"," - ") + ($_ | Get-VBRTaskSession | %{If ($_.GetDetails()){$_.Name + ": " + $_.GetDetails()}})}
  820.                     Else {($_.GetDetails()).Replace("<br />"," - ")}}},
  821.                     Result  | ConvertTo-HTML -Fragment
  822.                     $bodySessWFBk = $subHead01 + $headerWF + $subHead02 + $bodySessWFBk
  823.                 }
  824.        }
  825. }
  826.  
  827. # Get Successful Backup Sessions
  828. $bodySessSuccBk = $null
  829. If ($showSuccessBk) {
  830.       If ($successSessionsBk.count -gt 0) {
  831.                 If ($onlyLastBk) {
  832.                     $headerSucc = "Successful Backup Jobs"
  833.                 } Else {
  834.                     $headerSucc = "Successful Backup Sessions"
  835.                 }
  836.                If ($showDetailedBk) {
  837.                     $bodySessSuccBk = $successSessionsBk | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  838.                     @{Name="Start Time"; Expression = {$_.CreationTime}},
  839.                     @{Name="Stop Time"; Expression = {$_.EndTime}},
  840.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
  841.                     @{Name="Avg Speed (MB/s)"; Expression = {[Math]::Round($_.Info.Progress.AvgSpeed/1MB,2)}},
  842.                     @{Name="Total (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedSize/1GB,2)}},
  843.                     @{Name="Processed (GB)"; Expression = {[Math]::Round($_.Info.Progress.ProcessedUsedSize/1GB,2)}},
  844.                     @{Name="Data Read (GB)"; Expression = {[Math]::Round($_.Info.Progress.ReadSize/1GB,2)}},
  845.                     @{Name="Transferred (GB)"; Expression = {[Math]::Round($_.Info.Progress.TransferedSize/1GB,2)}},
  846.                     Result  | ConvertTo-HTML -Fragment
  847.                     $bodySessSuccBk = $subHead01 + $headerSucc + $subHead02 + $bodySessSuccBk
  848.                 } Else {
  849.                     $bodySessSuccBk = $successSessionsBk | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  850.                     @{Name="Start Time"; Expression = {$_.CreationTime}},
  851.                     @{Name="Stop Time"; Expression = {$_.EndTime}},
  852.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
  853.                     Result | ConvertTo-HTML -Fragment
  854.                     $bodySessSuccBk = $subHead01 + $headerSucc + $subHead02 + $bodySessSuccBk
  855.                 }
  856.        }
  857. }
  858.  
  859. # Get Running VM Restore Sessions
  860. $bodyRestoRunVM = $null
  861. If ($showRestoRunVM) {
  862.     If ($($runningResto).count -gt 0) {
  863.         $bodyRestoRunVM = $runningResto | Sort CreationTime | Select @{Name="VM Name"; Expression = {$_.Info.VmDisplayName}},
  864.         @{Name="Restore Type"; Expression = {$_.JobTypeString}}, @{Name="Start Time"; Expression = {$_.CreationTime}},     
  865.         @{Name="Initiator"; Expression = {$_.Info.Initiator.Name}},
  866.         @{Name="Reason"; Expression = {$_.Info.Reason}} | ConvertTo-HTML -Fragment
  867.         $bodyRestoRunVM = $subHead01 + "Running VM Restore Sessions" + $subHead02 + $bodyRestoRunVM
  868.     }
  869. }
  870.  
  871. # Get Completed VM Restore Sessions
  872. $bodyRestoreVM = $null
  873. If ($showRestoreVM) {
  874.     If ($($completeResto).count -gt 0) {
  875.         $bodyRestoreVM = $completeResto | Sort CreationTime | Select @{Name="VM Name"; Expression = {$_.Info.VmDisplayName}},
  876.         @{Name="Restore Type"; Expression = {$_.JobTypeString}},
  877.         @{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},        
  878.         @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $_.CreationTime $_.EndTime).TotalMinutes,2)}},     
  879.         @{Name="Initiator"; Expression = {$_.Info.Initiator.Name}}, @{Name="Reason"; Expression = {$_.Info.Reason}},
  880.         @{Name="Result"; Expression = {$_.Info.Result}} | ConvertTo-HTML -Fragment
  881.         $bodyRestoreVM = $subHead01 + "Completed VM Restore Sessions" + $subHead02 + $bodyRestoreVM
  882.     }
  883. }
  884.  
  885. # Get Endpoint Backup Summary Info
  886. $bodySummaryEp = $null
  887. If ($showSummaryEp) {
  888.     $vbrEpHash = @{
  889.         "Coordinator" = $vbrServer
  890.         "Sessions" = If ($seshListEp) {@($seshListEp).Count} Else {0}
  891.         "Successful" = @($successSessionsEp).Count
  892.         "Warning" = @($warningSessionsEp).Count
  893.         "Fails" = @($failsSessionsEp).Count
  894.         "Running" = @($runningSessionsEp).Count
  895.     }
  896.     $vbrEPObj = New-Object -TypeName PSObject -Property $vbrEpHash
  897.  
  898.     If ($onlyLastEp) {
  899.         $bodySummaryEp =  $vbrEPObj | Select @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Total Jobs"; Expression = {$_.Sessions}},
  900.         @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  901.         @{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}} | ConvertTo-HTML -Fragment
  902.     } Else {
  903.         $bodySummaryEp =  $vbrEPObj | Select @{Name="VBR Server"; Expression = {$_.Coordinator}}, @{Name="Total Sessions"; Expression = {$_.Sessions}},
  904.         @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  905.         @{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}} | ConvertTo-HTML -Fragment
  906.     }
  907.    
  908.     $bodySummaryEp = $subHead01 + "Endpoint Backup Results Summary" + $subHead02 + $bodySummaryEp
  909. }
  910.  
  911. # Get Endpoint Backup Job Status
  912. $bodyJobsEp = $null
  913. If ($showJobsEp) {
  914.        If ($allJobsEp.count -gt 0) {
  915.                $bodyJobsEp = $allJobsEp | Select @{Name="Job Name"; Expression = {$_.Name}},
  916.                @{Name="Enabled"; Expression = {$_.IsEnabled}},@{Name="Status"; Expression = {$_.LastState}},
  917.                 @{Name="Target Repo"; Expression = {$_.Target}},@{Name="Next Run"; Expression = {"Unknown"}},
  918.                @{Name="Last Result"; Expression = {If ($_.LastResult -eq "None"){""}Else{$_.LastResult}}} | Sort "Next Run" | ConvertTo-HTML -Fragment
  919.                $bodyJobsEp = $subHead01 + "Endpoint Backup Job Status" + $subHead02 + $bodyJobsEp
  920.        }
  921. }
  922.  
  923. # Get Running Endpoint Backup Jobs
  924. $bodyRunningEp = @()
  925. If ($showRunningEp) {
  926.    If ($runningSessionsEp.count -gt 0) {
  927.         Foreach($job in $allJobsEp) {
  928.             $bodyRunningEp += $runningSessionsEp | ?{$_.JobId -eq $job.Id} | Select @{Name="Job Name"; Expression = {$job.Name}},
  929.             @{Name="Start Time"; Expression = {$_.CreationTime}},
  930.            @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date)).TotalMinutes,2)}}
  931.         }              
  932.     $bodyRunningEp = $bodyRunningEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment
  933.    $bodyRunningEp = $subHead01 + "Running Endpoint Backup Jobs" + $subHead02 + $bodyRunningEp
  934.    }
  935. }
  936.  
  937. # Get Endpoint Backup Sessions with Failures or Warnings
  938. $bodySessWFEp = @()
  939. If ($showWarnFailEp) {
  940.        $sessWFEp = @($warningSessionsEp + $failsSessionsEp)
  941.        If ($sessWFEp.count -gt 0) {
  942.                 If ($onlyLastEp) {
  943.                     $headerWFEp = "Endpoint Backup Jobs with Warnings or Failures"
  944.                 } Else {
  945.                     $headerWFEp = "Endpoint Backup Sessions with Warnings or Failures"
  946.                 }
  947.                Foreach($job in $allJobsEp) {
  948.                     $bodySessWFEp += $sessWFEp | ?{$_.JobId -eq $job.Id} | Select @{Name="Job Name"; Expression = {$job.Name}},
  949.                     @{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},
  950.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date $_.EndTime)).TotalMinutes,2)}},
  951.                     Result
  952.                 }
  953.                 $bodySessWFEp = $bodySessWFEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment            
  954.                $bodySessWFEp = $subHead01 + $headerWFEp + $subHead02 + $bodySessWFEp
  955.        }
  956. }
  957.  
  958. # Get Successful Endpoint Backup Sessions
  959. $bodySessSuccEp = @()
  960. If ($showSuccessEp) {
  961.       If ($successSessionsEp.count -gt 0) {
  962.                 If ($onlyLastEp) {
  963.                     $headerSuccEp = "Successful Endpoint Backup Jobs"
  964.                 } Else {
  965.                     $headerSuccEp = "Successful Endpoint Backup Sessions"
  966.                 }
  967.                Foreach($job in $allJobsEp) {
  968.                     $bodySessSuccEp += $successSessionsEp | ?{$_.JobId -eq $job.Id} | Select @{Name="Job Name"; Expression = {$job.Name}},
  969.                     @{Name="Start Time"; Expression = {$_.CreationTime}}, @{Name="Stop Time"; Expression = {$_.EndTime}},
  970.                     @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.CreationTime) $(Get-Date $_.EndTime)).TotalMinutes,2)}},
  971.                     Result
  972.                 }
  973.                 $bodySessSuccEp = $bodySessSuccEp | Sort-Object "Start Time" | ConvertTo-HTML -Fragment            
  974.                $bodySessSuccEp = $subHead01 + $headerSuccEp + $subHead02 + $bodySessSuccEp
  975.        }
  976. }
  977.  
  978. # Get Proxy Info
  979. $bodyProxy = $null
  980. If ($showProxy) {
  981.     If ($viProxyList -ne $null) {
  982.             $bodyProxy = Get-vPCProxyInfo | Select @{Name="Proxy Name"; Expression = {$_.ProxyName}},
  983.             @{Name="Proxy Host"; Expression = {$_.RealName}}, Disabled, @{Name="IP Address"; Expression = {$_.IP}},
  984.             @{Name="RT (ms)"; Expression = {$_.Responce}}, Status | Sort "Proxy Host" |  ConvertTo-HTML -Fragment
  985.             $bodyProxy = $subHead01 + "Proxy Details" + $subHead02 + $bodyProxy
  986.     }
  987. }
  988.  
  989. # Get Repository Info
  990. $bodyRepo = $null
  991. If ($showRepo) {
  992.     If ($repoList -ne $null) {
  993.             $bodyRepo = $repoList | Get-vPCRepoInfo | Select @{Name="Repository Name"; Expression = {$_.Target}},
  994.             @{Name="Host"; Expression = {$_.RepoHost}},
  995.             @{Name="Path"; Expression = {$_.Storepath}}, @{Name="Free (GB)"; Expression = {$_.StorageFree}},
  996.             @{Name="Total (GB)"; Expression = {$_.StorageTotal}}, @{Name="Free (%)"; Expression = {$_.FreePercentage}},
  997.             @{Name="Status"; Expression = {
  998.                 If ($_.FreePercentage -lt $repoCritical) {"Critical"}
  999.                 ElseIf ($_.FreePercentage -lt $repoWarn) {"Warning"}
  1000.                 ElseIf ($_.FreePercentage -eq "Unknown") {"Unknown"}
  1001.                 Else {"OK"}}} | `
  1002.             Sort "Repository Name" | ConvertTo-HTML -Fragment
  1003.             $bodyRepo = $subHead01 + "Repository Details" + $subHead02 + $bodyRepo
  1004.     }
  1005. }
  1006.  
  1007. # Get Replica Target Info
  1008. $bodyReplica = $null
  1009. If ($showReplica) {
  1010.     If ($repList -ne $null) {
  1011.             $bodyReplica = $repList | Get-vPCReplicaTarget | Select @{Name="Replica Target"; Expression = {$_.Target}}, Datastore,
  1012.             @{Name="Free (GB)"; Expression = {$_.StorageFree}}, @{Name="Total (GB)"; Expression = {$_.StorageTotal}},
  1013.             @{Name="Free (%)"; Expression = {$_.FreePercentage}},
  1014.             @{Name="Status"; Expression = {If ($_.FreePercentage -lt $replicaCritical) {"Critical"} ElseIf ($_.FreePercentage -lt $replicaWarn) {"Warning"} Else {"OK"}}} | `
  1015.             Sort "Replica Target" | ConvertTo-HTML -Fragment
  1016.             $bodyReplica = $subHead01 + "Replica Details" + $subHead02 + $bodyReplica
  1017.     }
  1018. }
  1019.  
  1020. # Get Veeam Services Info
  1021. $bodyServices = $null
  1022. If ($showServices) {
  1023.     $bodyServices = Get-VeeamServers
  1024.     $bodyServices = Get-VeeamServices $bodyServices
  1025.     If ($hideRunningSvc) {$bodyServices = $bodyServices | ?{$_.Status -ne "Running"}}
  1026.     If ($bodyServices -ne $null) {
  1027.         $bodyServices = $bodyServices | Select "Server Name", "Service Name",
  1028.         @{Name="Status"; Expression = {If ($_.Status -eq "Stopped"){"Not Running"} Else {$_.Status}}} | Sort "Server Name", "Service Name" | ConvertTo-HTML -Fragment
  1029.         $bodyServices = $subHead01 + "Veeam Services" + $subHead02 + $bodyServices     
  1030.     }
  1031. }
  1032.  
  1033. # Get License Info
  1034. $bodyLicense = $null
  1035. If ($showLicExp) {
  1036.     $bodyLicense = Get-VeeamSupportDate $vbrServer | Select @{Name="Expiry Date"; Expression = {$_.ExpDate}}, @{Name="Days Remaining"; Expression = {$_.DaysRemain}}, `
  1037.         @{Name="Status"; Expression = {If ($_.DaysRemain -lt $licenseCritical) {"Critical"} ElseIf ($_.DaysRemain -lt $licenseWarn) {"Warning"} ElseIf ($_.DaysRemain -eq "Failed") {"Failed"} Else {"OK"}}} | `
  1038.         ConvertTo-HTML -Fragment
  1039.     $bodyLicense = $subHead01 + "License/Support Renewal Date" + $subHead02 + $bodyLicense
  1040. }
  1041.  
  1042. # Combine HTML Output
  1043. $htmlOutput = $headerObj + $bodyTop + $bodySummaryBK + $bodySummaryEp + $bodyMissing + $bodySuccess + $bodyJobsBk + $bodyRunningBk + $bodySessWFBk +
  1044.     $bodySessSuccBk + $bodyRestoRunVM + $bodyRestoreVM + $bodyJobsEp + $bodyRunningEp + $bodySessWFEp + $bodySessSuccEp + $bodyRepo + $bodyProxy +
  1045.     $bodyReplica + $bodyServices + $bodyLicense + $footerObj
  1046.  
  1047. # Add color to output depending on results
  1048. #Green
  1049. $htmlOutput = $htmlOutput.Replace("<td>Running<","<td style=""background-color: Green;color: White;"">Running<")
  1050. $htmlOutput = $htmlOutput.Replace("<td>OK<","<td style=""background-color: Green;color: White;"">OK<")
  1051. $htmlOutput = $htmlOutput.Replace("<td>Alive<","<td style=""background-color: Green;color: White;"">Alive<")
  1052. $htmlOutput = $htmlOutput.Replace("<td>Success<","<td style=""background-color: Green;color: White;"">Success<")
  1053. #Yellow
  1054. $htmlOutput = $htmlOutput.Replace("<td>Warning<","<td style=""background-color: Yellow;"">Warning<")
  1055. #Red
  1056. $htmlOutput = $htmlOutput.Replace("<td>Not Running<","<td style=""background-color: Red;color: White;"">Not Running<")
  1057. $htmlOutput = $htmlOutput.Replace("<td>Failed<","<td style=""background-color: Red;color: White;"">Failed<")
  1058. $htmlOutput = $htmlOutput.Replace("<td>Critical<","<td style=""background-color: Red;color: White;"">Critical<")
  1059. $htmlOutput = $htmlOutput.Replace("<td>Dead<","<td style=""background-color: Red;color: White;"">Dead<")
  1060. #endregion
  1061.  
  1062. #region Output
  1063. If ($sendEmail) {
  1064.        $smtp = New-Object System.Net.Mail.SmtpClient $emailHost
  1065.        $smtp.Credentials = New-Object System.Net.NetworkCredential($emailUser, $emailPass);
  1066.         $msg = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
  1067.         $msg.Subject = $emailSubject
  1068.         If ($emailAttach) {
  1069.             $body = "Veeam Report Attached"
  1070.             $msg.Body = $body
  1071.             $tempfile = "$env:TEMP\$rptTitle.htm"
  1072.             $htmlOutput | Out-File $tempfile
  1073.             $attachment = new-object System.Net.Mail.Attachment $tempfile
  1074.             $msg.Attachments.Add($attachment)
  1075.            
  1076.         } Else {
  1077.             $body = $htmlOutput
  1078.             $msg.Body = $body
  1079.             $msg.isBodyhtml = $true
  1080.         }      
  1081.        $smtp.send($msg)
  1082.         If ($emailAttach) {
  1083.             $attachment.dispose()
  1084.             Remove-Item $tempfile
  1085.         }
  1086. }
  1087.        
  1088. If ($saveFile) {      
  1089.     $htmlOutput | Out-File $outFile
  1090.     If ($launchFile) {
  1091.         Invoke-Item $outFile
  1092.     }
  1093. }
  1094. #endregion
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement