Advertisement
Guest User

Untitled

a guest
Jan 20th, 2016
458
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.94 KB | None | 0 0
  1. Add-PSSnapin "VeeamPSSnapIn" -ErrorAction SilentlyContinue
  2.  
  3. #region User-Variables
  4. # Report mode - valid modes: any number of hours, Weekly or Monthly
  5. # 24, 48, "Weekly", "Monthly"
  6. $reportMode = 13
  7. # Report Title
  8. $rptTitle = "Veeam Report"
  9. # Append Report Mode to Report Title E.g. My Veeam Report (Last 24 Hours)
  10. $fullTitle = $true
  11. # Report Width in Pixels
  12. $rptWidth = 1024
  13. # Only show last session for each Job
  14. $onlyLast = $false
  15.  
  16. # Location of Veeam executable (Veeam.Backup.Shell.exe)
  17. $veeamExePath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Shell.exe"
  18. # Location of common dll - Needed for repository function - Get-vPCRepoInfo (Veeam.Backup.Core.dll)
  19. $veeamDllPath = "C:\Program Files\Veeam\Backup and Replication\Backup\Veeam.Backup.Core.dll"
  20. # vCenter server(s) - As seen in VBR server
  21. #$vcenters = "vcenter1","vcenter2"
  22. $vcenters = "ENTER VCENTER IP/DNS NAME"
  23.  
  24. # Show VMs with no successful backups within time frame ($reportMode)
  25. $showUnprotectedVMs = $true
  26. # Show VMs with successful backups within time frame ($reportMode)
  27. $showProtectedVMs = $true
  28. # To Exclude VMs from Missing and Successful Backups section add VM names to be excluded
  29. # $excludevms = @("vm1","vm2","*_replica")
  30. $excludeVMs = @("")
  31. # Exclude VMs from Missing and Successful Backups section in the following (vCenter) folder(s)
  32. # $excludeFolder = = @("folder1","folder2","*_testonly")
  33. $excludeFolder = @("")
  34. # Exclude VMs from Missing and Successful Backups section in the following (vCenter) datacenter(s)
  35. # $excludeDC = = @("dc1","dc2","dc*")
  36. $excludeDC = @("")
  37. # Show Running jobs
  38. $showRunning = $true
  39. # Show All Sessions w/Warnings or Failures within time frame ($reportMode)
  40. $showWarnFail = $true
  41. # Show All Successful Sessions within time frame ($reportMode)
  42. $showSuccess = $true
  43. # Show Repository Info
  44. $showRepo = $true
  45. # Show Proxy Info
  46. $showProxy = $true
  47. # Show Replica Target Info
  48. $showReplica = $true
  49. # Show Veeam Services Info (Windows Services)
  50. $showServices = $true
  51. # Show only Services that are NOT running
  52. $hideRunningSvc = $false
  53. # Show License expiry info
  54. $showLicExp = $true
  55.  
  56. # Save output to a file - $true or $false
  57. $saveFile = $false
  58. # File output path and filename
  59. $outFile = "C:\temp\MyVeeamReport_$(Get-Date -format MMddyyyy_hhmmss).htm"
  60. # Launch file after creation - $true or $false
  61. $launchFile = $false
  62.  
  63. # Email configuration
  64. $sendEmail = $true
  65. $emailHost = "exchange server"
  66. $emailUser = ""
  67. $emailPass = ""
  68. $emailFrom = "from email"
  69. $emailTo = "send to email"
  70. # Send report as attachment - $true or $false
  71. $emailAttach = $false
  72. # Email Subject
  73. $emailSubject = $rptTitle
  74. # Append Report Mode to Email Subject E.g. My Veeam Report (Last 24 Hours)
  75. $fullSubject = $true
  76.  
  77. # Highlighting Thresholds
  78. # Repository Free Space Remaining %
  79. $repoCritical = 10
  80. $repoWarn = 20
  81. # Replica Target Free Space Remaining %
  82. $replicaCritical = 10
  83. $replicaWarn = 20
  84. # License Days Remaining
  85. $licenseCritical = 30
  86. $licenseWarn = 90
  87. #endregion
  88.  
  89. #region VersionInfo
  90. $vPCARversion = "1.4.1"
  91. #
  92. # Version 1.4.1 - SM
  93. # Fixed issue with summary counts
  94. # Version 1.4 - SM
  95. # Misc minor tweaks/cleanup
  96. # Added variable for report width
  97. # Added variable for email subject
  98. # Added ability to show/hide all report sections
  99. # Added Protected/Unprotected VM Count to Summary
  100. # Added per object details for sessions w/no details
  101. # Added proxy host name to Proxy Details
  102. # Added repository host name to Repository Details
  103. # Added section showing successful sessions
  104. # Added ability to view only last session per job
  105. # Added Cluster field for protected/unprotected VMs
  106. # Added catch for cifs repositories greater than 4TB as erroneous data is returned
  107. # Added % Complete for Running Jobs
  108. # Added ability to exclude multiple (vCenter) folders from Missing and Successful Backups section
  109. # Added ability to exclude multiple (vCenter) datacenters from Missing and Successful Backups section
  110. # Tweaked license info for better reporting across different date formats
  111. #
  112. # Version 1.3 - SM
  113. # Now supports VBR v8
  114. # For VBR v7, use report version 1.2
  115. # Added more flexible options to save and launch file
  116. #
  117. # Version 1.2 - SM
  118. # Added option to show VMs Successfully backed up
  119. #
  120. # Version 1.1.4 - SM
  121. # Misc tweaks/bug fixes
  122. # Reconfigured HTML a bit to help with certain email clients
  123. # Added cell coloring to highlight status
  124. # Added $rptTitle variable to hold report title
  125. # Added ability to send report via email as attachment
  126. #
  127. # Version 1.1.3 - SM
  128. # Added Details to Sessions with Warnings or Failures
  129. #
  130. # Version 1.1.2 - SM
  131. # Minor tweaks/updates
  132. # Added Veeam version info to header
  133. #
  134. # Version 1.1.1 - Shawn Masterson
  135. # Based on vPowerCLI v6 Army Report (v1.1) by Thomas McConnell
  136. # http://www.vpowercli.co.uk/2012/01/23/vpowercli-v6-army-report/
  137. # http://pastebin.com/6p3LrWt7
  138. #
  139. # Tweaked HTML header (color, title)
  140. #
  141. # Changed report width to 1024px
  142. #
  143. # Moved hard-coded path to exe/dll files to user declared variables ($veeamExePath/$veeamDllPath)
  144. #
  145. # Adjusted sorting on all objects
  146. #
  147. # Modified info group/counts
  148. # Modified - Total Jobs = Job Runs
  149. # Added - Read (GB)
  150. # Added - Transferred (GB)
  151. # Modified - Warning = Warnings
  152. # Modified - Failed = Failures
  153. # Added - Failed (last session)
  154. # Added - Running (currently running sessions)
  155. #
  156. # Modified job lines
  157. # Renamed Header - Sessions with Warnings or Failures
  158. # Fixed Write (GB) - Broke with v7
  159. #
  160. # Added support license renewal
  161. # Credit - Gavin Townsend http://www.theagreeablecow.com/2012/09/sysadmin-modular-reporting-samreports.html
  162. # Original Credit - Arne Fokkema http://ict-freak.nl/2011/12/29/powershell-veeam-br-get-total-days-before-the-license-expires/
  163. #
  164. # Modified Proxy section
  165. # Removed Read/Write/Util - Broke in v7 - Workaround unknown
  166. #
  167. # Modified Services section
  168. # Added - $runningSvc variable to toggle displaying services that are running
  169. # Added - Ability to hide section if no results returned (all services are running)
  170. # Added - Scans proxies and repositories as well as the VBR server for services
  171. #
  172. # Added VMs Not Backed Up section
  173. # Credit - Tom Sightler - http://sightunseen.org/blog/?p=1
  174. # http://www.sightunseen.org/files/vm_backup_status_dev.ps1
  175. #
  176. # Modified $reportMode
  177. # Added ability to run with any number of hours (8,12,72 etc)
  178. # Added bits to allow for zero sessions (semi-gracefully)
  179. #
  180. # Added Running Jobs section
  181. # Added ability to toggle displaying running jobs
  182. #
  183. # Added catch to ensure running v7 or greater
  184. #
  185. #
  186. # Version 1.1
  187. # Added job lines as per a request on the website
  188. #
  189. # Version 1.0
  190. # Clean up for release
  191. #
  192. # Version 0.9
  193. # More cmdlet rewrite to improve perfomace, credit to @SethBartlett
  194. # for practically writing the Get-vPCRepoInfo
  195. #
  196. # Version 0.8
  197. # Added Read/Write stats for proxies at requests of @bsousapt
  198. # Performance improvement of proxy tear down due to rewrite of cmdlet
  199. # Replaced 2 other functions
  200. # Added Warning counter, .00 to all storage returns and fetch credentials for
  201. # remote WinLocal repos
  202. #
  203. # Version 0.7
  204. # Added Utilisation(Get-vPCDailyProxyUsage) and Modes 24, 48, Weekly, and Monthly
  205. # Minor performance tweaks
  206.  
  207. #endregion
  208.  
  209. #region NonUser-Variables
  210. # Get the B&R Server
  211. $vbrServer = Get-VBRLocalHost
  212. # Get all the VI proxies in your army
  213. $viProxyList = Get-VBRViProxy
  214. # Get all the backup repositories
  215. $repoList = Get-VBRBackupRepository
  216. # Get all sessions
  217. $allSesh = Get-VBRBackupSession
  218. # Get all the backup sessions for mode (timeframe)
  219. if ($reportMode -eq "Monthly") {
  220. $HourstoCheck = 720
  221. } elseif ($reportMode -eq "Weekly") {
  222. $HourstoCheck = 168
  223. } else {
  224. $HourstoCheck = $reportMode
  225. }
  226. $seshList = $allSesh | ?{($_.CreationTime -ge (Get-Date).AddHours(-$HourstoCheck)) -and ($_.State -ne "Working")}
  227.  
  228. #Get replica jobs
  229. $repList = Get-VBRJob | ?{$_.IsReplica}
  230.  
  231. # Get session information
  232. $totalxfer = 0
  233. $totalRead = 0
  234. $seshList | %{$totalxfer += $([Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2))}
  235. $seshList | %{$totalRead += $([Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2))}
  236. If ($onlyLast) {
  237. $tempSeshList = $seshList
  238. $seshList = @()
  239. foreach($job in (Get-VBRJob | ? {$_.JobType -eq "Backup"}))
  240. {
  241. $seshList += $TempSeshList | ?{$_.Jobname -eq $job.name} | Sort-Object CreationTime -Descending | Select-Object -First 1
  242. }
  243. }
  244. $succesSessions = @($seshList | ?{$_.Result -eq "Success"})
  245. $warningSessions = @($seshList | ?{$_.Result -eq "Warning"})
  246. $failsSessions = @($seshList | ?{$_.Result -eq "Failed"})
  247. $totalSessions = @($seshList | ?{$_.Result -eq "Failed" -Or $_.Result -eq "Success" -Or $_.Result -eq "Warning"})
  248. $runningSessions = @($allSesh | ?{$_.State -eq "Working"})
  249. $failedSessions = @($seshList | ?{($_.Result -eq "Failed") -and ($_.WillBeRetried -ne "True")})
  250.  
  251. # Append Report Mode to Report Title
  252. If ($fullTitle) {
  253. If (($reportMode -ne "Weekly") -And ($reportMode -ne "Monthly")) {
  254. $rptTitle = "$rptTitle (Last $reportMode Hrs)"
  255. } else {
  256. $rptTitle = "$rptTitle ($reportMode)"
  257. }
  258. }
  259.  
  260. # Append Report Mode to Email subject
  261. If ($fullSubject) {
  262. If (($reportMode -ne "Weekly") -And ($reportMode -ne "Monthly")) {
  263. $emailSubject = "$emailSubject (Last $reportMode Hrs)"
  264. } else {
  265. $emailSubject = "$emailSubject ($reportMode)"
  266. }
  267. }
  268. #endregion
  269.  
  270. #region Functions
  271.  
  272. function Get-vPCProxyInfo {
  273. $vPCObjAry = @()
  274. function Build-vPCObj {param ([PsObject]$inputObj)
  275. $ping = new-object system.net.networkinformation.ping
  276. $pinginfo = $ping.send("$($inputObj.Host.RealName)")
  277.  
  278. if ($pinginfo.Status -eq "Success") {
  279. $hostAlive = "Alive"
  280. } else {
  281. $hostAlive = "Dead"
  282. }
  283.  
  284. $vPCFuncObject = New-Object PSObject -Property @{
  285. ProxyName = $inputObj.Name
  286. RealName = $inputObj.Host.RealName.ToLower()
  287. Disabled = $inputObj.IsDisabled
  288. Status = $hostAlive
  289. IP = $pinginfo.Address
  290. Responce = $pinginfo.RoundtripTime
  291. }
  292. return $vPCFuncObject
  293. }
  294. Get-VBRViProxy | %{$vPCObjAry = $vPCObjAry + $(Build-vPCObj $_)}
  295. $vPCObjAry
  296. }
  297.  
  298. function Get-vPCRepoInfo {
  299. [CmdletBinding()]
  300. param (
  301. [Parameter(Position=0, ValueFromPipeline=$true)]
  302. [PSObject[]]$Repository
  303. )
  304. Begin {
  305. $outputAry = @()
  306. [Reflection.Assembly]::LoadFile($veeamDllPath) | Out-Null
  307. function Build-Object {param($name, $repohost, $path, $free, $total)
  308. $repoObj = New-Object -TypeName PSObject -Property @{
  309. Target = $name
  310. RepoHost = $repohost.ToLower()
  311. Storepath = $path
  312. StorageFree = [Math]::Round([Decimal]$free/1GB,2)
  313. StorageTotal = [Math]::Round([Decimal]$total/1GB,2)
  314. FreePercentage = [Math]::Round(($free/$total)*100)
  315. }
  316. return $repoObj | Select Target, RepoHost, Storepath, StorageFree, StorageTotal, FreePercentage
  317. }
  318. }
  319. Process {
  320. foreach ($r in $Repository) {
  321. if ($r.GetType().Name -eq [String]) {
  322. $r = Get-VBRBackupRepository -Name $r
  323. }
  324. if ($r.Type -eq "WinLocal") {
  325. $Server = $r.GetHost()
  326. $FileCommander = [Veeam.Backup.Core.CWinFileCommander]::Create($Server.Info)
  327. $storage = $FileCommander.GetDrives([ref]$null) | ?{$_.Name -eq $r.Path.Substring(0,3)}
  328. $outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.FreeSpace $storage.TotalSpace
  329. }
  330. elseif ($r.Type -eq "LinuxLocal") {
  331. $Server = $r.GetHost()
  332. $FileCommander = new-object Veeam.Backup.Core.CSshFileCommander $server.info
  333. $storage = $FileCommander.FindDirInfo($r.Path)
  334. $outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.FreeSpace $storage.TotalSize
  335. }
  336. elseif ($r.Type -eq "CifsShare") {
  337. $Server = $r.GetHost()
  338. $fso = New-Object -Com Scripting.FileSystemObject
  339. $storage = $fso.GetDrive($r.Path)
  340. # Catch shares with > 4TB space (not calculated correctly)
  341. If (!($storage.TotalSize) -or (($storage.TotalSize -eq 4398046510080) -and ($storage.AvailableSpace -eq 4398046510080))){
  342. $outputObj = New-Object -TypeName PSObject -Property @{
  343. Target = $r.Name
  344. RepoHost = $server.RealName.ToLower()
  345. Storepath = $r.Path
  346. StorageFree = "Unknown"
  347. StorageTotal = "Unknown"
  348. FreePercentage = "Unknown"
  349. }
  350. } Else {
  351. $outputObj = Build-Object $r.Name $server.RealName $r.Path $storage.AvailableSpace $storage.TotalSize
  352. }
  353. }
  354. $outputAry = $outputAry + $outputObj
  355. }
  356. }
  357. End {
  358. $outputAry
  359. }
  360. }
  361.  
  362. function Get-vPCReplicaTarget {
  363. [CmdletBinding()]
  364. param(
  365. [Parameter(ValueFromPipeline=$true)]
  366. [PSObject[]]$InputObj
  367. )
  368. BEGIN {
  369. $outputAry = @()
  370. $dsAry = @()
  371. if (($Name -ne $null) -and ($InputObj -eq $null)) {
  372. $InputObj = Get-VBRJob -Name $Name
  373. }
  374. }
  375. PROCESS {
  376. foreach ($obj in $InputObj) {
  377. if (($dsAry -contains $obj.ViReplicaTargetOptions.DatastoreName) -eq $false) {
  378. $esxi = $obj.GetTargetHost()
  379. $dtstr = $esxi | Find-VBRViEntity -Name $obj.ViReplicaTargetOptions.DatastoreName
  380. $objoutput = New-Object -TypeName PSObject -Property @{
  381. Target = $esxi.Name
  382. Datastore = $obj.ViReplicaTargetOptions.DatastoreName
  383. StorageFree = [Math]::Round([Decimal]$dtstr.FreeSpace/1GB,2)
  384. StorageTotal = [Math]::Round([Decimal]$dtstr.Capacity/1GB,2)
  385. FreePercentage = [Math]::Round(($dtstr.FreeSpace/$dtstr.Capacity)*100)
  386. }
  387. $dsAry = $dsAry + $obj.ViReplicaTargetOptions.DatastoreName
  388. $outputAry = $outputAry + $objoutput
  389. }
  390. else {
  391. return
  392. }
  393. }
  394. }
  395. END {
  396. $outputAry | Select Target, Datastore, StorageFree, StorageTotal, FreePercentage
  397. }
  398. }
  399.  
  400. function Get-VeeamVersion {
  401. $veeamExe = Get-Item $veeamExePath
  402. $VeeamVersion = $veeamExe.VersionInfo.ProductVersion
  403. Return $VeeamVersion
  404. }
  405.  
  406. function Get-VeeamSupportDate {
  407. #Get version and license info
  408.  
  409. $regBinary = (Get-Item 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\license').GetValue('Lic1')
  410. $veeamLicInfo = [string]::Join($null, ($regBinary | % { [char][int]$_; }))
  411.  
  412. if($script:VeeamVersion -like "5*"){
  413. $pattern = "EXPIRATION DATE\=\d{1,2}\/\d{1,2}\/\d{1,4}"
  414. }
  415. elseif($script:VeeamVersion -like "6*"){
  416. $pattern = "Expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}"
  417. }
  418. elseif($script:VeeamVersion -like "8*"){
  419. $pattern = "expiration date\=\d{1,2}\/\d{1,2}\/\d{1,4}"
  420. }
  421.  
  422. # Convert Binary key
  423. if($script:VeeamVersion -like "5*" -OR $script:VeeamVersion -like "6*" -OR $script:VeeamVersion -like "8*"){
  424. $expirationDate = [regex]::matches($VeeamLicInfo, $pattern)[0].Value.Split("=")[1]
  425. $datearray = $expirationDate -split '/'
  426. $expirationDate = Get-Date -Day $datearray[0] -Month $datearray[1] -Year $datearray[2]
  427. $totalDaysLeft = ($expirationDate - (get-date)).Totaldays.toString().split(",")[0]
  428. $totalDaysLeft = [int]$totalDaysLeft
  429. $objoutput = New-Object -TypeName PSObject -Property @{
  430. ExpDate = $expirationDate.ToShortDateString()
  431. DaysRemain = $totalDaysLeft
  432. }
  433. $objoutput
  434. }
  435. else{
  436. $objoutput = New-Object -TypeName PSObject -Property @{
  437. ExpDate = "Failed"
  438. DaysRemain = "Failed"
  439. }
  440. $objoutput
  441. }
  442. }
  443.  
  444. function Get-VeeamServers {
  445. $vservers=@{}
  446. $outputAry = @()
  447. $vservers.add($($script:vbrserver.realname),"VBRServer")
  448. foreach ($srv in $script:viProxyList) {
  449. If (!$vservers.ContainsKey($srv.Host.Realname)) {
  450. $vservers.Add($srv.Host.Realname,"ProxyServer")
  451. }
  452. }
  453. foreach ($srv in $script:repoList) {
  454. If (!$vservers.ContainsKey($srv.gethost().Realname)) {
  455. $vservers.Add($srv.gethost().Realname,"RepoServer")
  456. }
  457. }
  458. $vservers = $vservers.GetEnumerator() | Sort-Object Name
  459. foreach ($vserver in $vservers) {
  460. $outputAry += $vserver.Name
  461. }
  462. return $outputAry
  463. }
  464.  
  465. function Get-VeeamServices {
  466. param (
  467. [PSObject]$inputObj)
  468.  
  469. $outputAry = @()
  470. foreach ($obj in $InputObj) {
  471. $output = Get-Service -computername $obj -Name "*Veeam*" -exclude "SQLAgent*" |
  472. Select @{Name="Server Name"; Expression = {$obj.ToLower()}}, @{Name="Service Name"; Expression = {$_.DisplayName}}, Status
  473. $outputAry = $outputAry + $output
  474. }
  475. $outputAry
  476. }
  477.  
  478. function Get-VMsBackupStatus {
  479. param (
  480. [String]$vcenter)
  481.  
  482. # Convert exclusion list to simple regular expression
  483. $excludevms_regex = ('(?i)^(' + (($script:excludeVMs | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  484. $excludefolder_regex = ('(?i)^(' + (($script:excludeFolder | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  485. $excludedc_regex = ('(?i)^(' + (($script:excludeDC | ForEach {[regex]::escape($_)}) -join "|") + ')$') -replace "\\\*", ".*"
  486.  
  487. $outputary = @()
  488. $vcenterobj = Get-VBRServer -Name $vcenter
  489. $vmobjs = Find-VBRObject -Server $vcenterobj |
  490. Where-Object {$_.Type -eq "VirtualMachine" -and $_.VMFolderName -notmatch $excludefolder_regex} |
  491. Where-Object {$_.Name -notmatch $excludevms_regex} |
  492. Where-Object {$_.GetParent("Datacenter") -notmatch $excludedc_regex}
  493.  
  494.  
  495. $jobobjids = [Veeam.Backup.Core.CHierarchyObj]::GetObjectsOnHost($vcenterobj.id) | Where-Object {$_.Type -eq "Vm"}
  496.  
  497. foreach ($vm in $vmobjs) {
  498. $jobobjid = ($jobobjids | Where-Object {$_.ObjectId -eq $vm.Id}).Id
  499. if (!$jobobjid) {
  500. $jobobjid = $vm.FindParent("Datacenter").Id + "\" + $vm.Id
  501. }
  502. $vm | Add-Member -MemberType NoteProperty "JobObjId" -Value $jobobjid
  503. }
  504.  
  505. # Get a list of all VMs from vCenter and add to hash table, assume Unprotected
  506. $vms=@{}
  507. foreach ($vm in $vmobjs) {
  508. if(!$vms.ContainsKey($vm.JobObjId)) {
  509. $vmdc = [string]$vm.GetParent("Datacenter")
  510. Try {$vmclus = [string]$vm.GetParent("ClusterComputeResource")} Catch {$vmclus = ""}
  511. $vms.Add($vm.JobObjId, @("!", $vmdc, $vmclus, $vm.Name))
  512. }
  513. }
  514.  
  515. # Find all backup job sessions that have ended in the last x hours
  516. $vbrjobs = Get-VBRJob | Where-Object {$_.JobType -eq "Backup"}
  517. $vbrsessions = Get-VBRBackupSession | Where-Object {$_.JobType -eq "Backup" -and $_.EndTime -ge (Get-Date).addhours(-$script:HourstoCheck)}
  518.  
  519. # Find all Successfuly backed up VMs in selected sessions (i.e. VMs not ending in failure) and update status to "Protected"
  520. if ($vbrsessions) {
  521. foreach ($session in $vbrsessions) {
  522. foreach ($vm in ($session.gettasksessions() | Where-Object {$_.Status -ne "Failed"} | ForEach-Object { $_ })) {
  523. if($vms.ContainsKey($vm.Info.ObjectId)) {
  524. $vms[$vm.Info.ObjectId][0]=$session.JobName
  525. }
  526. }
  527. }
  528. }
  529. $vms.GetEnumerator() | Sort-Object Value
  530. }
  531.  
  532. function Get-VMsMissingBackup {
  533. param (
  534. $vms)
  535.  
  536. $outputary = @()
  537. foreach ($vm in $vms) {
  538. if ($vm.Value[0] -eq "!") {
  539. $objoutput = New-Object -TypeName PSObject -Property @{
  540. Datacenter = $vm.Value[1]
  541. Cluster = $vm.Value[2]
  542. Name = $vm.Value[3]
  543. }
  544. $outputAry += $objoutput
  545. }
  546. }
  547. $outputAry | Select Datacenter, Cluster, Name
  548. }
  549.  
  550. function Get-VMsSuccessBackup {
  551. param (
  552. $vms)
  553.  
  554. $outputary = @()
  555. foreach ($vm in $vms) {
  556. if ($vm.Value[0] -ne "!") {
  557. $objoutput = New-Object -TypeName PSObject -Property @{
  558. Datacenter = $vm.Value[1]
  559. Cluster = $vm.Value[2]
  560. Name = $vm.Value[3]
  561. }
  562. $outputAry += $objoutput
  563. }
  564. }
  565. $outputAry | Select Datacenter, Cluster, Name
  566. }
  567.  
  568. #endregion
  569.  
  570. #region Report
  571. # Get Veeam Version
  572. $VeeamVersion = Get-VeeamVersion
  573.  
  574. If ($VeeamVersion -lt 8) {
  575. Write-Host "Script requires VBR v8 or greater" -ForegroundColor Red
  576. exit
  577. }
  578.  
  579. # HTML Stuff
  580. $headerObj = @"
  581. <html>
  582. <head>
  583. <title>$rptTitle</title>
  584. <style>
  585. body {font-family: Tahoma; background-color:#fff;}
  586. table {font-family: Tahoma;width: $($rptWidth)px;font-size: 12px;border-collapse:collapse;}
  587. <!-- table tr:nth-child(odd) td {background: #e2e2e2;} -->
  588. th {background-color: #cccc99;border: 1px solid #a7a9ac;border-bottom: none;}
  589. td {background-color: #ffffff;border: 1px solid #a7a9ac;padding: 2px 3px 2px 3px;vertical-align: top;}
  590. </style>
  591. </head>
  592. "@
  593.  
  594. $bodyTop = @"
  595. <body>
  596. <center>
  597. <table cellspacing="0" cellpadding="0">
  598. <tr>
  599. <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>
  600. <td style="width: 20%;height: 45px;border: none;background-color: #003366;color: White;font-size: 12px;vertical-align:text-top;text-align:right;padding: 2px 3px 2px 3px;">v$vPCARversion</td>
  601. </tr>
  602. <tr>
  603. <td style="width: 80%;height: 35px;border: none;background-color: #003366;color: White;font-size: 10px;vertical-align: bottom;padding: 0px 0px 2px 3px;">Report generated: $(Get-Date -format g)</td>
  604. <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>
  605. </tr>
  606. </table>
  607. "@
  608.  
  609. $subHead01 = @"
  610. <table>
  611. <tr>
  612. <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;">
  613. "@
  614.  
  615. $subHead01err = @"
  616. <table>
  617. <tr>
  618. <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: none;border-bottom: none;">
  619. "@
  620.  
  621. $subHead02 = @"
  622. </td>
  623. </tr>
  624. </table>
  625. "@
  626.  
  627. $footerObj = @"
  628. </center>
  629. </body>
  630. </html>
  631. "@
  632.  
  633. #Get VM Backup Status
  634. $vmstatus = @()
  635. foreach ($vcenter in $vcenters) {
  636. $status = Get-VMsBackupStatus $vcenter
  637. $vmstatus += $status
  638. }
  639.  
  640. # VMs Missing Backups
  641. $missingVMs = @()
  642. $missingVMs = Get-VMsMissingBackup $vmstatus
  643.  
  644. # VMs Successfuly Backed Up
  645. $successVMs = @()
  646. $successVMs = Get-VMsSuccessBackup $vmstatus
  647.  
  648. # Get Summary Info
  649. $vbrMasterHash = @{
  650. "Coordinator" = "$((gc env:computername).ToLower())"
  651. "Failed" = @($failedSessions).Count
  652. "Sessions" = @($totalSessions).Count
  653. "Read" = $totalRead
  654. "Transferred" = $totalXfer
  655. "Successful" = @($succesSessions).Count
  656. "Warning" = @($warningSessions).Count
  657. "Fails" = @($failsSessions).Count
  658. "Running" = @($runningSessions).Count
  659. "SuccessVM" = @($successVMs).Count
  660. "FailedVM" = @($missingVMs).Count
  661. }
  662. $vbrMasterObj = New-Object -TypeName PSObject -Property $vbrMasterHash
  663.  
  664. If ($onlyLast) {
  665. $bodySummary = $vbrMasterObj | Select Coordinator, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
  666. @{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Jobs Run"; Expression = {$_.Sessions}},
  667. @{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
  668. @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  669. @{Name="Warnings"; Expression = {$_.Warning}},
  670. @{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
  671. } Else {
  672. $bodySummary = $vbrMasterObj | Select Coordinator, @{Name="Unprotected VMs"; Expression = {$_.FailedVM}},
  673. @{Name="Protected VMs"; Expression = {$_.SuccessVM}}, @{Name="Total Sessions"; Expression = {$_.Sessions}},
  674. @{Name="Read (GB)"; Expression = {$_.Read}}, @{Name="Transferred (GB)"; Expression = {$_.Transferred}},
  675. @{Name="Running"; Expression = {$_.Running}}, @{Name="Successful"; Expression = {$_.Successful}},
  676. @{Name="Warnings"; Expression = {$_.Warning}}, @{Name="Failures"; Expression = {$_.Fails}},
  677. @{Name="Failed"; Expression = {$_.Failed}} | ConvertTo-HTML -Fragment
  678. }
  679.  
  680.  
  681.  
  682. # Get VMs Missing Backups
  683. $bodyMissing = $null
  684. If ($showUnprotectedVMs) {
  685. If ($missingVMs -ne $null) {
  686. $missingVMs = $missingVMs | Sort Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
  687. $bodyMissing = $subHead01err + "VMs with No Successful Backups" + $subHead02 + $missingVMs
  688. }
  689. }
  690.  
  691. # Get VMs Successfuly Backed Up
  692. $bodySuccess = $null
  693. If ($showProtectedVMs) {
  694. If ($successVMs -ne $null) {
  695. $successVMs = $successVMs | Sort Datacenter, Cluster, Name | ConvertTo-HTML -Fragment
  696. $bodySuccess = $subHead01 + "VMs with Successful Backups" + $subHead02 + $successVMs
  697. }
  698. }
  699.  
  700. # Get Running Jobs
  701. $bodyRunning = $null
  702. if ($showRunning -eq $true) {
  703. if ($runningSessions.count -gt 0) {
  704. $bodyRunning = $runningSessions | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  705. @{Name="Start Time"; Expression = {$_.CreationTime}},
  706. @{Name="Duration (Mins)"; Expression = {[Math]::Round((New-TimeSpan $(Get-Date $_.Progress.StartTime) $(Get-Date)).TotalMinutes,2)}},
  707. @{Name="% Complete"; Expression = {$_.Progress.Percents}},
  708. @{Name="Read (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.ReadSize/1GB, 2)}},
  709. @{Name="Write (GB)"; Expression = {[Math]::Round([Decimal]$_.Progress.TransferedSize/1GB, 2)}} | ConvertTo-HTML -Fragment
  710. $bodyRunning = $subHead01 + "Running Jobs" + $subHead02 + $bodyRunning
  711. }
  712. }
  713.  
  714. # Get Sessions with Failures or Warnings
  715. $bodySessWF = $null
  716. if ($showWarnFail -eq $true) {
  717. $sessWF = @($warningSessions + $failsSessions)
  718. if ($sessWF.count -gt 0) {
  719. If ($onlyLast) {
  720. $headerWF = "Jobs with Warnings or Failures"
  721. } Else {
  722. $headerWF = "Sessions with Warnings or Failures"
  723. }
  724. $bodySessWF = $sessWF | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  725. @{Name="Start Time"; Expression = {$_.CreationTime}},
  726. @{Name="Stop Time"; Expression = {$_.EndTime}},
  727. @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
  728. @{Name="Details"; Expression = {
  729. If ($_.GetDetails() -eq ""){($_.GetDetails()).Replace("<br />"," - ") + ($_ | Get-VBRTaskSession | %{If ($_.GetDetails()){$_.Name + ": " + $_.GetDetails()}})}
  730. Else {($_.GetDetails()).Replace("<br />"," - ")}}},
  731. Result | ConvertTo-HTML -Fragment
  732. $bodySessWF = $subHead01 + $headerWF + $subHead02 + $bodySessWF
  733. }
  734. }
  735.  
  736. # Get Successful Sessions
  737. $bodySessSucc = $null
  738. if ($showSuccess -eq $true) {
  739. if ($succesSessions.count -gt 0) {
  740. If ($onlyLast) {
  741. $headerSucc = "Successful Jobs"
  742. } Else {
  743. $headerSucc = "Successful Sessions"
  744. }
  745. $bodySessSucc = $succesSessions | Sort Creationtime | Select @{Name="Job Name"; Expression = {$_.Name}},
  746. @{Name="Start Time"; Expression = {$_.CreationTime}},
  747. @{Name="Stop Time"; Expression = {$_.EndTime}},
  748. @{Name="Duration (Mins)"; Expression = {[Math]::Round($_.WorkDetails.WorkDuration.TotalMinutes,2)}},
  749. Result | ConvertTo-HTML -Fragment
  750. $bodySessSucc = $subHead01 + $headerSucc + $subHead02 + $bodySessSucc
  751. }
  752. }
  753.  
  754. # Get Proxy Info
  755. $bodyProxy = $null
  756. If ($showProxy) {
  757. if ($viProxyList -ne $null) {
  758. $bodyProxy = Get-vPCProxyInfo | Select @{Name="Proxy Name"; Expression = {$_.ProxyName}},
  759. @{Name="Proxy Host"; Expression = {$_.RealName}}, Disabled, @{Name="IP Address"; Expression = {$_.IP}},
  760. @{Name="RT (ms)"; Expression = {$_.Responce}}, Status | Sort "Proxy Host" | ConvertTo-HTML -Fragment
  761. $bodyProxy = $subHead01 + "Proxy Details" + $subHead02 + $bodyProxy
  762. }
  763. }
  764.  
  765. # Get Repository Info
  766. $bodyRepo = $null
  767. If ($showRepo) {
  768. if ($repoList -ne $null) {
  769. $bodyRepo = $repoList | Get-vPCRepoInfo | Select @{Name="Repository Name"; Expression = {$_.Target}},
  770. @{Name="Repository Host"; Expression = {$_.RepoHost}},
  771. @{Name="Path"; Expression = {$_.Storepath}}, @{Name="Free (GB)"; Expression = {$_.StorageFree}},
  772. @{Name="Total (GB)"; Expression = {$_.StorageTotal}}, @{Name="Free (%)"; Expression = {$_.FreePercentage}},
  773. @{Name="Status"; Expression = {
  774. If ($_.FreePercentage -lt $repoCritical) {"Critical"}
  775. ElseIf ($_.FreePercentage -lt $repoWarn) {"Warning"}
  776. ElseIf ($_.FreePercentage -eq "Unknown") {"Unknown"}
  777. Else {"OK"}}} | `
  778. Sort "Repository Name" | ConvertTo-HTML -Fragment
  779. $bodyRepo = $subHead01 + "Repository Details" + $subHead02 + $bodyRepo
  780. }
  781. }
  782.  
  783. # Get Replica Target Info
  784. $bodyReplica = $null
  785. If ($showReplica) {
  786. if ($repList -ne $null) {
  787. $bodyReplica = $repList | Get-vPCReplicaTarget | Select @{Name="Replica Target"; Expression = {$_.Target}}, Datastore,
  788. @{Name="Free (GB)"; Expression = {$_.StorageFree}}, @{Name="Total (GB)"; Expression = {$_.StorageTotal}},
  789. @{Name="Free (%)"; Expression = {$_.FreePercentage}},
  790. @{Name="Status"; Expression = {If ($_.FreePercentage -lt $replicaCritical) {"Critical"} ElseIf ($_.FreePercentage -lt $replicaWarn) {"Warning"} Else {"OK"}}} | `
  791. Sort "Replica Target" | ConvertTo-HTML -Fragment
  792. $bodyReplica = $subHead01 + "Replica Details" + $subHead02 + $bodyReplica
  793. }
  794. }
  795.  
  796. # Get Veeam Services Info
  797. $bodyServices = $null
  798. If ($showServices) {
  799. $bodyServices = Get-VeeamServers
  800. $bodyServices = Get-VeeamServices $bodyServices
  801. If ($hideRunningSvc) {$bodyServices = $bodyServices | ?{$_.Status -ne "Running"}}
  802. If ($bodyServices -ne $null) {
  803. $bodyServices = $bodyServices | Select "Server Name", "Service Name", Status | Sort "Server Name", "Service Name" | ConvertTo-HTML -Fragment
  804. $bodyServices = $subHead01 + "Veeam Services" + $subHead02 + $bodyServices
  805. }
  806. }
  807.  
  808. # Get License Info
  809. $bodyLicense = $null
  810. If ($showLicExp) {
  811. $bodyLicense = Get-VeeamSupportDate | Select @{Name="Expiry Date"; Expression = {$_.ExpDate}}, @{Name="Days Remaining"; Expression = {$_.DaysRemain}}, `
  812. @{Name="Status"; Expression = {If ($_.DaysRemain -lt $licenseCritical) {"Critical"} ElseIf ($_.DaysRemain -lt $licenseWarn) {"Warning"} ElseIf ($_.DaysRemain -eq "Failed") {"Failed"} Else {"OK"}}} | `
  813. ConvertTo-HTML -Fragment
  814. $bodyLicense = $subHead01 + "License/Support Renewal Date" + $subHead02 + $bodyLicense
  815. }
  816.  
  817. # Combine HTML Output
  818. $htmlOutput = $headerObj + $bodyTop + $bodySummary + $bodyMissing + $bodySuccess + $bodyRunning + $bodySessWF + $bodySessSucc + $bodyRepo + $bodyProxy + $bodyReplica + $bodyServices + $bodyLicense + $footerObj
  819.  
  820. # Add color to output depending on results
  821. #Green
  822. $htmlOutput = $htmlOutput.Replace("<td>Running<","<td style=""background-color: Green;color: White;"">Running<")
  823. $htmlOutput = $htmlOutput.Replace("<td>OK<","<td style=""background-color: Green;color: White;"">OK<")
  824. $htmlOutput = $htmlOutput.Replace("<td>Alive<","<td style=""background-color: Green;color: White;"">Alive<")
  825. $htmlOutput = $htmlOutput.Replace("<td>Success<","<td style=""background-color: Green;color: White;"">Success<")
  826. #Yellow
  827. $htmlOutput = $htmlOutput.Replace("<td>Warning<","<td style=""background-color: Yellow;"">Warning<")
  828. #Red
  829. $htmlOutput = $htmlOutput.Replace("<td>Stopped<","<td style=""background-color: Red;color: White;"">Stopped<")
  830. $htmlOutput = $htmlOutput.Replace("<td>Failed<","<td style=""background-color: Red;color: White;"">Failed<")
  831. $htmlOutput = $htmlOutput.Replace("<td>Critical<","<td style=""background-color: Red;color: White;"">Critical<")
  832. $htmlOutput = $htmlOutput.Replace("<td>Dead<","<td style=""background-color: Red;color: White;"">Dead<")
  833. #endregion
  834.  
  835. #region Output
  836. if ($sendEmail) {
  837. $smtp = New-Object System.Net.Mail.SmtpClient $emailHost
  838. $smtp.Credentials = New-Object System.Net.NetworkCredential($emailUser, $emailPass);
  839. $msg = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
  840. $msg.Subject = $emailSubject
  841. If ($emailAttach) {
  842. $body = "Veeam Report Attached"
  843. $msg.Body = $body
  844. $tempfile = "$env:TEMP\$rptTitle.htm"
  845. $htmlOutput | Out-File $tempfile
  846. $attachment = new-object System.Net.Mail.Attachment $tempfile
  847. $msg.Attachments.Add($attachment)
  848.  
  849. } Else {
  850. $body = $htmlOutput
  851. $msg.Body = $body
  852. $msg.isBodyhtml = $true
  853. }
  854. $smtp.send($msg)
  855. If ($emailAttach) {
  856. $attachment.dispose()
  857. Remove-Item $tempfile
  858. }
  859. }
  860.  
  861. If ($saveFile) {
  862. $htmlOutput | Out-File $outFile
  863. If ($launchFile) {
  864. Invoke-Item $outFile
  865. }
  866. }
  867. #endregion
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement