From 5778cfca84c717df714479de03da0205cf0c5478 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Thu, 15 Feb 2024 15:18:55 -0400 Subject: [PATCH 01/17] Improve report readability --- Src/Private/Get-AbrVbrAgentBackupjobConf.ps1 | 3 +++ Src/Private/Get-AbrVbrBackupCopyjobConf.ps1 | 3 +++ Src/Private/Get-AbrVbrBackupRepository.ps1 | 1 + Src/Private/Get-AbrVbrBackupServerInfo.ps1 | 2 ++ Src/Private/Get-AbrVbrBackupToTape.ps1 | 7 ++++--- Src/Private/Get-AbrVbrBackupjobHyperV.ps1 | 9 ++++++--- Src/Private/Get-AbrVbrBackupjobVMware.ps1 | 9 ++++++--- Src/Private/Get-AbrVbrCloudConnectCG.ps1 | 1 + Src/Private/Get-AbrVbrCloudConnectGP.ps1 | 1 + Src/Private/Get-AbrVbrCloudConnectTenant.ps1 | 2 ++ Src/Private/Get-AbrVbrConfigurationBackupSetting.ps1 | 1 + Src/Private/Get-AbrVbrEmailNotificationSetting.ps1 | 1 + Src/Private/Get-AbrVbrEnterpriseManagerInfo.ps1 | 1 + Src/Private/Get-AbrVbrEventForwarding.ps1 | 1 + Src/Private/Get-AbrVbrFileShareBackupjobConf.ps1 | 7 ++++--- Src/Private/Get-AbrVbrFileToTape.ps1 | 7 ++++--- Src/Private/Get-AbrVbrGlobalNotificationSetting.ps1 | 4 ++++ Src/Private/Get-AbrVbrObjectRepository.ps1 | 2 ++ Src/Private/Get-AbrVbrReplFailoverPlan.ps1 | 1 + Src/Private/Get-AbrVbrRepljobHyperV.ps1 | 4 +++- Src/Private/Get-AbrVbrRepljobVMware.ps1 | 4 +++- Src/Private/Get-AbrVbrScaleOutRepository.ps1 | 1 + Src/Private/Get-AbrVbrSureBackup.ps1 | 2 ++ Src/Private/Get-AbrVbrSureBackupjobconf.ps1 | 7 ++++--- Src/Private/Get-AbrVbrTapeMediaPool.ps1 | 9 +++++++-- Src/Private/Get-AbrVbrTapeServer.ps1 | 1 + Src/Private/Get-AbrVbrTapeVault.ps1 | 1 + Src/Private/Get-AbrVbrUserRoleAssignment.ps1 | 1 + 28 files changed, 71 insertions(+), 22 deletions(-) diff --git a/Src/Private/Get-AbrVbrAgentBackupjobConf.ps1 b/Src/Private/Get-AbrVbrAgentBackupjobConf.ps1 index 12ae7b5..d8f3974 100644 --- a/Src/Private/Get-AbrVbrAgentBackupjobConf.ps1 +++ b/Src/Private/Get-AbrVbrAgentBackupjobConf.ps1 @@ -78,6 +78,7 @@ function Get-AbrVbrAgentBackupjobConf { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { @@ -481,6 +482,7 @@ function Get-AbrVbrAgentBackupjobConf { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -526,6 +528,7 @@ function Get-AbrVbrAgentBackupjobConf { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrBackupCopyjobConf.ps1 b/Src/Private/Get-AbrVbrBackupCopyjobConf.ps1 index 117b5e6..5ff21de 100644 --- a/Src/Private/Get-AbrVbrBackupCopyjobConf.ps1 +++ b/Src/Private/Get-AbrVbrBackupCopyjobConf.ps1 @@ -82,6 +82,7 @@ function Get-AbrVbrBackupCopyjobConf { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { @@ -251,6 +252,7 @@ function Get-AbrVbrBackupCopyjobConf { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -292,6 +294,7 @@ function Get-AbrVbrBackupCopyjobConf { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrBackupRepository.ps1 b/Src/Private/Get-AbrVbrBackupRepository.ps1 index 92c63ca..da58478 100644 --- a/Src/Private/Get-AbrVbrBackupRepository.ps1 +++ b/Src/Private/Get-AbrVbrBackupRepository.ps1 @@ -178,6 +178,7 @@ function Get-AbrVbrBackupRepository { Text "Best Practice:" -Bold Text "Veeam recommend to implement Immutability where it is supported. It is done for increased security: immutability protects your data from loss as a result of attacks, malware activity or any other injurious actions." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrBackupServerInfo.ps1 b/Src/Private/Get-AbrVbrBackupServerInfo.ps1 index 31a88e4..1a0b941 100644 --- a/Src/Private/Get-AbrVbrBackupServerInfo.ps1 +++ b/Src/Private/Get-AbrVbrBackupServerInfo.ps1 @@ -138,6 +138,7 @@ function Get-AbrVbrBackupServerInfo { Text 'Reference:' -Bold Text 'https://bp.veeam.com/vbr/Security/Security_domains.html' } + BlankLine } } #---------------------------------------------------------------------------------------------# @@ -201,6 +202,7 @@ function Get-AbrVbrBackupServerInfo { Text "Best Practice:" -Bold Text "Recommended Veeam Backup Server minimum configuration is two CPU cores and 8GB of RAM." } + BlankLine } } #---------------------------------------------------------------------------------------------# diff --git a/Src/Private/Get-AbrVbrBackupToTape.ps1 b/Src/Private/Get-AbrVbrBackupToTape.ps1 index bfb42c5..0a28ed3 100644 --- a/Src/Private/Get-AbrVbrBackupToTape.ps1 +++ b/Src/Private/Get-AbrVbrBackupToTape.ps1 @@ -52,12 +52,12 @@ function Get-AbrVbrBackupToTape { 'False' { 'Disabled' } default { $TBkjob.NextRun } } - 'Description' = $TBkjob.Description + 'Description' = ConvertTo-EmptyToFiller $TBkjob.Description } $OutObj = [pscustomobject]$inobj if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -71,13 +71,14 @@ function Get-AbrVbrBackupToTape { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrBackupjobHyperV.ps1 b/Src/Private/Get-AbrVbrBackupjobHyperV.ps1 index 7443b91..756236f 100644 --- a/Src/Private/Get-AbrVbrBackupjobHyperV.ps1 +++ b/Src/Private/Get-AbrVbrBackupjobHyperV.ps1 @@ -79,7 +79,7 @@ function Get-AbrVbrBackupjobHyperV { 'Total Backup Size' = ConvertTo-FileSizeString $CommonInfo.IncludedSize 'Target Address' = $CommonInfo.TargetDir 'Target File' = $CommonInfo.TargetFile - 'Description' = $CommonInfo.CommonInfo.Description + 'Description' = ConvertTo-EmptyToFiller $CommonInfo.CommonInfo.Description 'Modified By' = $CommonInfo.CommonInfo.ModifiedBy.FullName } $OutObj = [pscustomobject]$inobj @@ -89,7 +89,7 @@ function Get-AbrVbrBackupjobHyperV { } if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -103,13 +103,14 @@ function Get-AbrVbrBackupjobHyperV { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { @@ -341,6 +342,7 @@ function Get-AbrVbrBackupjobHyperV { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -403,6 +405,7 @@ function Get-AbrVbrBackupjobHyperV { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrBackupjobVMware.ps1 b/Src/Private/Get-AbrVbrBackupjobVMware.ps1 index 157c23e..fe0e5e9 100644 --- a/Src/Private/Get-AbrVbrBackupjobVMware.ps1 +++ b/Src/Private/Get-AbrVbrBackupjobVMware.ps1 @@ -79,7 +79,7 @@ function Get-AbrVbrBackupjobVMware { 'Total Backup Size' = ConvertTo-FileSizeString $CommonInfo.IncludedSize 'Target Address' = $CommonInfo.TargetDir 'Target File' = $CommonInfo.TargetFile - 'Description' = $CommonInfo.CommonInfo.Description + 'Description' = ConvertTo-EmptyToFiller $CommonInfo.CommonInfo.Description 'Modified By' = $CommonInfo.CommonInfo.ModifiedBy.FullName } $OutObj = [pscustomobject]$inobj @@ -89,7 +89,7 @@ function Get-AbrVbrBackupjobVMware { } if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -103,13 +103,14 @@ function Get-AbrVbrBackupjobVMware { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { @@ -384,6 +385,7 @@ function Get-AbrVbrBackupjobVMware { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -446,6 +448,7 @@ function Get-AbrVbrBackupjobVMware { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrCloudConnectCG.ps1 b/Src/Private/Get-AbrVbrCloudConnectCG.ps1 index 536f601..cd01ace 100644 --- a/Src/Private/Get-AbrVbrCloudConnectCG.ps1 +++ b/Src/Private/Get-AbrVbrCloudConnectCG.ps1 @@ -89,6 +89,7 @@ function Get-AbrVbrCloudConnectCG { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } diff --git a/Src/Private/Get-AbrVbrCloudConnectGP.ps1 b/Src/Private/Get-AbrVbrCloudConnectGP.ps1 index 4e77bb5..0791671 100644 --- a/Src/Private/Get-AbrVbrCloudConnectGP.ps1 +++ b/Src/Private/Get-AbrVbrCloudConnectGP.ps1 @@ -73,6 +73,7 @@ function Get-AbrVbrCloudConnectGP { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } diff --git a/Src/Private/Get-AbrVbrCloudConnectTenant.ps1 b/Src/Private/Get-AbrVbrCloudConnectTenant.ps1 index d97db5a..f9c0d19 100644 --- a/Src/Private/Get-AbrVbrCloudConnectTenant.ps1 +++ b/Src/Private/Get-AbrVbrCloudConnectTenant.ps1 @@ -77,6 +77,7 @@ function Get-AbrVbrCloudConnectTenant { Text "Best Practice:" -Bold Text "Validate if the tenant's resources are being utilized" } + BlankLine } } #---------------------------------------------------------------------------------------------# @@ -150,6 +151,7 @@ function Get-AbrVbrCloudConnectTenant { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrConfigurationBackupSetting.ps1 b/Src/Private/Get-AbrVbrConfigurationBackupSetting.ps1 index 2e4dee9..350b1ad 100644 --- a/Src/Private/Get-AbrVbrConfigurationBackupSetting.ps1 +++ b/Src/Private/Get-AbrVbrConfigurationBackupSetting.ps1 @@ -101,6 +101,7 @@ function Get-AbrVbrConfigurationBackupSetting { Text "Best Practice:" -Bold Text "It's a recommended best practice to enable the Backup Configuration job" } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrEmailNotificationSetting.ps1 b/Src/Private/Get-AbrVbrEmailNotificationSetting.ps1 index 4610b6c..71f39fa 100644 --- a/Src/Private/Get-AbrVbrEmailNotificationSetting.ps1 +++ b/Src/Private/Get-AbrVbrEmailNotificationSetting.ps1 @@ -70,6 +70,7 @@ function Get-AbrVbrEmailNotificationSetting { Text "Best Practice:" -Bold Text "Veeam recommends configuring email notifications to be able to receive alerts with the results of jobs performed on the backup server." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrEnterpriseManagerInfo.ps1 b/Src/Private/Get-AbrVbrEnterpriseManagerInfo.ps1 index 9accb52..67a2227 100644 --- a/Src/Private/Get-AbrVbrEnterpriseManagerInfo.ps1 +++ b/Src/Private/Get-AbrVbrEnterpriseManagerInfo.ps1 @@ -74,6 +74,7 @@ function Get-AbrVbrEnterpriseManagerInfo { Text "Best Practice:" -Bold Text "Veeam recommends centralized license management through Enterprise Manager." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrEventForwarding.ps1 b/Src/Private/Get-AbrVbrEventForwarding.ps1 index f15eca8..37147ed 100644 --- a/Src/Private/Get-AbrVbrEventForwarding.ps1 +++ b/Src/Private/Get-AbrVbrEventForwarding.ps1 @@ -64,6 +64,7 @@ function Get-AbrVbrEventForwarding { Text "Security Best Practice:" -Bold Text "It is a recommends best practice to configure Event Forwarding to an external SIEM or Log Collector to increase the organization security posture." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrFileShareBackupjobConf.ps1 b/Src/Private/Get-AbrVbrFileShareBackupjobConf.ps1 index ed92be1..53cd28b 100644 --- a/Src/Private/Get-AbrVbrFileShareBackupjobConf.ps1 +++ b/Src/Private/Get-AbrVbrFileShareBackupjobConf.ps1 @@ -51,7 +51,7 @@ function Get-AbrVbrFileShareBackupjobConf { 'Total Backup Size' = ConvertTo-FileSizeString $CommonInfo.IncludedSize 'Target Address' = $CommonInfo.TargetDir 'Target File' = $CommonInfo.TargetFile - 'Description' = $CommonInfo.CommonInfo.Description + 'Description' = ConvertTo-EmptyToFiller $CommonInfo.CommonInfo.Description 'Modified By' = $CommonInfo.CommonInfo.ModifiedBy.FullName } $OutObj = [pscustomobject]$inobj @@ -61,7 +61,7 @@ function Get-AbrVbrFileShareBackupjobConf { } if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -75,7 +75,7 @@ function Get-AbrVbrFileShareBackupjobConf { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { @@ -349,6 +349,7 @@ function Get-AbrVbrFileShareBackupjobConf { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrFileToTape.ps1 b/Src/Private/Get-AbrVbrFileToTape.ps1 index 8a8fdbf..9ca15b0 100644 --- a/Src/Private/Get-AbrVbrFileToTape.ps1 +++ b/Src/Private/Get-AbrVbrFileToTape.ps1 @@ -46,12 +46,12 @@ function Get-AbrVbrFileToTape { 'False' { 'Disabled' } default { $TBkjob.NextRun } } - 'Description' = $TBkjob.Description + 'Description' = ConvertTo-EmptyToFiller $TBkjob.Description } $OutObj = [pscustomobject]$inobj if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -65,13 +65,14 @@ function Get-AbrVbrFileToTape { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrGlobalNotificationSetting.ps1 b/Src/Private/Get-AbrVbrGlobalNotificationSetting.ps1 index 57aded2..c2c3301 100644 --- a/Src/Private/Get-AbrVbrGlobalNotificationSetting.ps1 +++ b/Src/Private/Get-AbrVbrGlobalNotificationSetting.ps1 @@ -60,6 +60,7 @@ function Get-AbrVbrGlobalNotificationSetting { Text "Best Practice:" -Bold Text "Veeam recommends configuring email notifications to be able to receive alerts with the results of jobs performed on the backup server." } + BlankLine } } Section -ExcludeFromTOC -Style NOTOCHeading5 'Production Datastore' { @@ -99,6 +100,7 @@ function Get-AbrVbrGlobalNotificationSetting { Text "Best Practice:" -Bold Text "Veeam recommends configuring email notifications to be able to receive alerts with the results of jobs performed on the backup server." } + BlankLine } } Section -ExcludeFromTOC -Style NOTOCHeading5 'Support Expiration' { @@ -128,6 +130,7 @@ function Get-AbrVbrGlobalNotificationSetting { Text "Best Practice:" -Bold Text "Veeam recommends configuring email notifications to be able to receive alerts with the results of jobs performed on the backup server." } + BlankLine } } Section -ExcludeFromTOC -Style NOTOCHeading5 'Update Notification' { @@ -153,6 +156,7 @@ function Get-AbrVbrGlobalNotificationSetting { Text "Best Practice:" -Bold Text "Veeam recommends configuring email notifications to be able to receive alerts with the results of jobs performed on the backup server." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrObjectRepository.ps1 b/Src/Private/Get-AbrVbrObjectRepository.ps1 index c7b8c94..c024ed8 100644 --- a/Src/Private/Get-AbrVbrObjectRepository.ps1 +++ b/Src/Private/Get-AbrVbrObjectRepository.ps1 @@ -156,6 +156,7 @@ function Get-AbrVbrObjectRepository { Text "Best Practice:" -Bold Text "Veeam recommend to implement Immutability where it is supported. It is done for increased security: immutability protects your data from loss as a result of attacks, malware activity or any other injurious actions." } + BlankLine } } } catch { @@ -237,6 +238,7 @@ function Get-AbrVbrObjectRepository { Text "Best Practice:" -Bold Text "Veeam recommend to implement Immutability where it is supported. It is done for increased security: immutability protects your data from loss as a result of attacks, malware activity or any other injurious actions." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrReplFailoverPlan.ps1 b/Src/Private/Get-AbrVbrReplFailoverPlan.ps1 index a009288..59972a5 100644 --- a/Src/Private/Get-AbrVbrReplFailoverPlan.ps1 +++ b/Src/Private/Get-AbrVbrReplFailoverPlan.ps1 @@ -71,6 +71,7 @@ function Get-AbrVbrReplFailoverPlan { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } if ($InfoLevel.Replication.FailoverPlan -ge 2) { diff --git a/Src/Private/Get-AbrVbrRepljobHyperV.ps1 b/Src/Private/Get-AbrVbrRepljobHyperV.ps1 index cf81229..3be0fdd 100644 --- a/Src/Private/Get-AbrVbrRepljobHyperV.ps1 +++ b/Src/Private/Get-AbrVbrRepljobHyperV.ps1 @@ -76,7 +76,7 @@ function Get-AbrVbrRepljobHyperV { 'Total Backup Size' = ConvertTo-FileSizeString $CommonInfo.IncludedSize 'Target Address' = $CommonInfo.TargetDir 'Target File' = $CommonInfo.TargetFile - 'Description' = $CommonInfo.CommonInfo.Description + 'Description' = ConvertTo-EmptyToFiller $CommonInfo.CommonInfo.Description 'Modified By' = $CommonInfo.CommonInfo.ModifiedBy.FullName } $OutObj = [pscustomobject]$inobj @@ -337,6 +337,7 @@ function Get-AbrVbrRepljobHyperV { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -399,6 +400,7 @@ function Get-AbrVbrRepljobHyperV { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrRepljobVMware.ps1 b/Src/Private/Get-AbrVbrRepljobVMware.ps1 index 0f26f58..fc6e2bb 100644 --- a/Src/Private/Get-AbrVbrRepljobVMware.ps1 +++ b/Src/Private/Get-AbrVbrRepljobVMware.ps1 @@ -76,7 +76,7 @@ function Get-AbrVbrRepljobVMware { 'Total Backup Size' = ConvertTo-FileSizeString $CommonInfo.IncludedSize 'Target Address' = $CommonInfo.TargetDir 'Target File' = $CommonInfo.TargetFile - 'Description' = $CommonInfo.CommonInfo.Description + 'Description' = ConvertTo-EmptyToFiller $CommonInfo.CommonInfo.Description 'Modified By' = $CommonInfo.CommonInfo.ModifiedBy.FullName } $OutObj = [pscustomobject]$inobj @@ -341,6 +341,7 @@ function Get-AbrVbrRepljobVMware { Text "Best Practice:" -Bold Text "It is recommended to use storage-level corruption guard for any backup job with no active full backups scheduled. Synthetic full backups are still 'incremental forever' and may suffer from corruption over time. Storage-level corruption guard was introduced to provide a greater level of confidence in integrity of the backups." } + BlankLine } } } catch { @@ -403,6 +404,7 @@ function Get-AbrVbrRepljobVMware { Text "Best Practice:" -Bold Text "Backup and replica data is a high potential source of vulnerability. To secure data stored in backups and replicas, use Veeam Backup & Replication inbuilt encryption to protect data in backups" } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrScaleOutRepository.ps1 b/Src/Private/Get-AbrVbrScaleOutRepository.ps1 index 7359218..7b7a458 100644 --- a/Src/Private/Get-AbrVbrScaleOutRepository.ps1 +++ b/Src/Private/Get-AbrVbrScaleOutRepository.ps1 @@ -126,6 +126,7 @@ function Get-AbrVbrScaleOutRepository { Text "Best Practice:" -Bold Text "Veeam Backup & Replication allows you to encrypt offloaded data. With the Encrypt data uploaded to object storage setting selected, the entire collection of blocks along with the metadata will be encrypted while being offloaded regardless of the jobs encryption settings. This helps you protect the data from an unauthorized access." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrSureBackup.ps1 b/Src/Private/Get-AbrVbrSureBackup.ps1 index 34356df..66f5ca7 100644 --- a/Src/Private/Get-AbrVbrSureBackup.ps1 +++ b/Src/Private/Get-AbrVbrSureBackup.ps1 @@ -242,6 +242,7 @@ function Get-AbrVbrSureBackup { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined notes. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } @@ -352,6 +353,7 @@ function Get-AbrVbrSureBackup { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined notes. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 b/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 index b9547ef..c1db9c8 100644 --- a/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 +++ b/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 @@ -46,12 +46,12 @@ function Get-AbrVbrSureBackupjobconf { 'False' { 'Disabled' } default { $SBkjob.NextRun } } - 'Description' = $SBkjob.Description + 'Description' = ConvertTo-EmptyToFiller $SBkjob.Description } $OutObj = [pscustomobject]$inobj if ($HealthCheck.Jobs.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -65,13 +65,14 @@ function Get-AbrVbrSureBackupjobconf { } $OutObj | Table @TableParams if ($HealthCheck.Jobs.BestPractice) { - if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $Null -like $_.'Description' }) { + if ($OutObj | Where-Object { $_.'Description' -match 'Created by' -or $_.'Description' -eq "--" }) { Paragraph "Health Check:" -Bold -Underline BlankLine Paragraph { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } diff --git a/Src/Private/Get-AbrVbrTapeMediaPool.ps1 b/Src/Private/Get-AbrVbrTapeMediaPool.ps1 index e14cce3..ec8363a 100644 --- a/Src/Private/Get-AbrVbrTapeMediaPool.ps1 +++ b/Src/Private/Get-AbrVbrTapeMediaPool.ps1 @@ -119,7 +119,11 @@ function Get-AbrVbrTapeMediaPool { 'Total Space' = ConvertTo-FileSizeString $Capacity 'Free Space' = ConvertTo-FileSizeString $FreeSpace 'Add Tape from Free Media Pool Automatically when more Tape are Required' = ConvertTo-TextYN $PoolObj.MoveFromFreePool - 'Description' = $TapeLibraryObj.Description + 'Description' = Switch ([string]::IsNullOrEmpty($TapeLibraryObj.Description)) { + $true { "--" } + $false { $TapeLibraryObj.Description } + default { "Unknown" } + } 'Library Mode' = Switch ($PoolObj.GlobalOptions.Mode) { 'CrossLibraryParalleing' { 'Active (Used Always)' } 'Failover' { 'Passive (Used for Failover Only)' } @@ -141,7 +145,7 @@ function Get-AbrVbrTapeMediaPool { $OutObj = [pscustomobject]$inobj if ($HealthCheck.Tape.BestPractice) { - $OutObj | Where-Object { $Null -like $_.'Description' } | Set-Style -Style Warning -Property 'Description' + $OutObj | Where-Object { $_.'Description' -eq "--" } | Set-Style -Style Warning -Property 'Description' $OutObj | Where-Object { $_.'Description' -match "Created by" } | Set-Style -Style Warning -Property 'Description' } @@ -163,6 +167,7 @@ function Get-AbrVbrTapeMediaPool { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } #---------------------------------------------------------------------------------------------# diff --git a/Src/Private/Get-AbrVbrTapeServer.ps1 b/Src/Private/Get-AbrVbrTapeServer.ps1 index 6162bb6..30cca70 100644 --- a/Src/Private/Get-AbrVbrTapeServer.ps1 +++ b/Src/Private/Get-AbrVbrTapeServer.ps1 @@ -72,6 +72,7 @@ function Get-AbrVbrTapeServer { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrTapeVault.ps1 b/Src/Private/Get-AbrVbrTapeVault.ps1 index 95440ab..243ae5e 100644 --- a/Src/Private/Get-AbrVbrTapeVault.ps1 +++ b/Src/Private/Get-AbrVbrTapeVault.ps1 @@ -70,6 +70,7 @@ function Get-AbrVbrTapeVault { Text "Best Practice:" -Bold Text "It is a general rule of good practice to establish well-defined descriptions. This helps to speed up the fault identification process, as well as enabling better documentation of the environment." } + BlankLine } } } catch { diff --git a/Src/Private/Get-AbrVbrUserRoleAssignment.ps1 b/Src/Private/Get-AbrVbrUserRoleAssignment.ps1 index 05dd540..5581946 100644 --- a/Src/Private/Get-AbrVbrUserRoleAssignment.ps1 +++ b/Src/Private/Get-AbrVbrUserRoleAssignment.ps1 @@ -148,6 +148,7 @@ function Get-AbrVbrUserRoleAssignment { Paragraph { Text "*** Veeam recommends configuring Four-eye Authorization to be able to protect against accidental deletion of backup and repositories by requiring an approval from another Backup Administrator." } + BlankLine } } } From 5489b2ce23be076577c498af6cbf909d4e8ed876 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Thu, 15 Feb 2024 23:15:45 -0400 Subject: [PATCH 02/17] Fix No Virtual Labs after v0.8.4 #142 --- CHANGELOG.md | 2 ++ Src/Private/Get-AbrVbrSureBackup.ps1 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 014bce7..1dc4fad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed Graphviz install check code. (No need to manually install Graphviz) - Code Cleanup +- Increased Veeam.Diagrammer module requirement to v0.5.9 ### Fixed - Improved error handling on the Diagram section. - Fixed issue with the Veeam.Diagrammer module. +- Resolved issue that prevented SureBackup Virtual Lab information to be collected. Fix [#142](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/142) ## [0.8.4] - 2024-01-16 diff --git a/Src/Private/Get-AbrVbrSureBackup.ps1 b/Src/Private/Get-AbrVbrSureBackup.ps1 index 66f5ca7..315ac3d 100644 --- a/Src/Private/Get-AbrVbrSureBackup.ps1 +++ b/Src/Private/Get-AbrVbrSureBackup.ps1 @@ -113,7 +113,7 @@ function Get-AbrVbrSureBackup { } } } - if ($SureBackupVL) { + if ($SureBackupVLs) { try { Section -Style Heading4 'Virtual Labs' { Paragraph "The following section provides a summary about SureBackup Virtual Lab." From cba69cf68ad1fe5b8568fe2e6c4aae25086ba969 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Thu, 15 Feb 2024 23:53:07 -0400 Subject: [PATCH 03/17] Fix No Virtual Labs after v0.8.4 #142 --- Src/Private/Get-AbrVbrSureBackupjob.ps1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Src/Private/Get-AbrVbrSureBackupjob.ps1 b/Src/Private/Get-AbrVbrSureBackupjob.ps1 index 515c9c8..e4b869f 100644 --- a/Src/Private/Get-AbrVbrSureBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrSureBackupjob.ps1 @@ -46,7 +46,11 @@ function Get-AbrVbrSureBackupjob { 'True' { 'Scheduled' } } 'Latest Result' = $SBkjob.LastResult - 'Virtual Lab' = $SBkjob.VirtualLab.Name + 'Virtual Lab' = Switch ($SBkjob.VirtualLab.Name) { + $true {"Not applicable​​​​"} + $false {$SBkjob.VirtualLab.Name} + default {"--"} + } } $OutObj += [pscustomobject]$inobj } catch { From 58ed75d20f0169cc8e77ed673b8b21be72276756 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Sat, 24 Feb 2024 13:35:03 -0400 Subject: [PATCH 04/17] Improved chart sample data --- Src/Private/Get-AbrVbrBackupjob.ps1 | 12 ++++++++++-- Src/Private/Get-AbrVbrSecurityCompliance.ps1 | 13 +++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Src/Private/Get-AbrVbrBackupjob.ps1 b/Src/Private/Get-AbrVbrBackupjob.ps1 index 086ac81..d53646b 100644 --- a/Src/Private/Get-AbrVbrBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrBackupjob.ps1 @@ -77,9 +77,17 @@ function Get-AbrVbrBackupjob { if ((Get-VBRSureBackupJob -ErrorAction SilentlyContinue).LastResult) { $Alljobs += (Get-VBRSureBackupJob -ErrorAction SilentlyContinue).LastResult } - $sampleData = $Alljobs | Group-Object - $chartFileItem = Get-ColumnChart -SampleData $sampleData -ChartName 'BackupJobs' -XField 'Name' -YField 'Count' -ChartAreaName 'Infrastructure' -AxisXTitle 'Status' -AxisYTitle 'Count' -ChartTitleName 'BackupJobs' -ChartTitleText 'Jobs Latest Result' + $sampleData = @{ + 'Success' = ($Alljobs | Where-Object { $_ -eq "Success" } | Measure-Object).Count + 'Warning' = ($Alljobs | Where-Object { $_ -eq "Warning" } | Measure-Object).Count + 'Failed' = ($Alljobs | Where-Object { $_ -eq "Failed" } | Measure-Object).Count + 'None' = ($Alljobs | Where-Object { $_ -eq "None" } | Measure-Object).Count + } + + $sampleDataObj = $sampleData.GetEnumerator() | Select-Object @{ Name = 'Category'; Expression = { $_.key } }, @{ Name = 'Value'; Expression = { $_.value } } | Sort-Object -Property 'Category' + + $chartFileItem = Get-ColumnChart -SampleData $sampleDataObj -ChartName 'BackupJobs' -XField 'Category' -YField 'Value' -ChartAreaName 'Infrastructure' -AxisXTitle 'Status' -AxisYTitle 'Count' -ChartTitleName 'BackupJobs' -ChartTitleText 'Jobs Latest Result' } catch { Write-PScriboMessage -IsWarning "Backup Jobs chart section: $($_.Exception.Message)" diff --git a/Src/Private/Get-AbrVbrSecurityCompliance.ps1 b/Src/Private/Get-AbrVbrSecurityCompliance.ps1 index 8b89f44..9d3cb17 100644 --- a/Src/Private/Get-AbrVbrSecurityCompliance.ps1 +++ b/Src/Private/Get-AbrVbrSecurityCompliance.ps1 @@ -108,9 +108,18 @@ function Get-AbrVbrSecurityCompliance { } try { - $sampleData = $OutObj.status | Group-Object - $chartFileItem = Get-ColumnChart -SampleData $sampleData -ChartName 'SecurityCompliance' -XField 'Name' -YField 'Count' -ChartAreaName 'Infrastructure' -AxisXTitle 'Status' -AxisYTitle 'Count' -ChartTitleName 'SecurityCompliance' -ChartTitleText 'Best Practices' + $sampleData = @{ + 'Passed' = ($OutObj.status | Where-Object { $_ -eq "Passed" } | Measure-Object).Count + 'Not Implemented' = ($OutObj.status | Where-Object { $_ -eq "Not Implemented" } | Measure-Object).Count + 'Unable to detect' = ($OutObj.status | Where-Object { $_ -eq "Unable to detect" } | Measure-Object).Count + 'Suppressed' = ($OutObj.status | Where-Object { $_ -eq "Suppressed" } | Measure-Object).Count + } + + $sampleDataObj = $sampleData.GetEnumerator() | Select-Object @{ Name = 'Category'; Expression = { $_.key } }, @{ Name = 'Value'; Expression = { $_.value } } | Sort-Object -Property 'Category' + + $chartFileItem = Get-ColumnChart -SampleData $sampleDataObj -ChartName 'SecurityCompliance' -XField 'Category' -YField 'Value' -ChartAreaName 'Infrastructure' -AxisXTitle 'Status' -AxisYTitle 'Count' -ChartTitleName 'SecurityCompliance' -ChartTitleText 'Best Practices' + } catch { Write-PScriboMessage -IsWarning "Security & Compliance chart section: $($_.Exception.Message)" } From e9a7dc457e015ecfc88d3c8c28106a5450fc3d9c Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Tue, 27 Feb 2024 11:15:54 -0400 Subject: [PATCH 05/17] Error in Get-AbrVbrGlobalExclusion #145 --- Src/Private/Get-AbrVbrGlobalExclusion.ps1 | 96 ++++++++++++----------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/Src/Private/Get-AbrVbrGlobalExclusion.ps1 b/Src/Private/Get-AbrVbrGlobalExclusion.ps1 index 22266ee..e392c0d 100644 --- a/Src/Private/Get-AbrVbrGlobalExclusion.ps1 +++ b/Src/Private/Get-AbrVbrGlobalExclusion.ps1 @@ -5,7 +5,7 @@ function Get-AbrVbrGlobalExclusion { .DESCRIPTION Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo. .NOTES - Version: 0.8.3 + Version: 0.8.4 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux @@ -27,60 +27,64 @@ function Get-AbrVbrGlobalExclusion { try { $MalwareDetectionExclusions = Get-VBRMalwareDetectionExclusion $VMExclusions = Get-VBRVMExclusion - Section -Style Heading4 'Global Exclusions' { - try { - Write-PScriboMessage "Discovering Veeam VBR Malware Detection Exclusions settings information from $System." - Section -ExcludeFromTOC -Style Heading5 'Malware Detection Exclusions' { - foreach ($MalwareDetectionExclusion in $MalwareDetectionExclusions) { - $OutObj = @() + if ($MalwareDetectionExclusions) { + Section -Style Heading4 'Global Exclusions' { + try { + Write-PScriboMessage "Discovering Veeam VBR Malware Detection Exclusions settings information from $System." + Section -ExcludeFromTOC -Style Heading5 'Malware Detection Exclusions' { + foreach ($MalwareDetectionExclusion in $MalwareDetectionExclusions) { + $OutObj = @() - $inObj = [ordered] @{ - 'Name' = $MalwareDetectionExclusion.Name - 'Platform' = $MalwareDetectionExclusion.Platform - 'Note' = ConvertTo-EmptyToFiller $MalwareDetectionExclusion.Note + $inObj = [ordered] @{ + 'Name' = $MalwareDetectionExclusion.Name + 'Platform' = $MalwareDetectionExclusion.Platform + 'Note' = ConvertTo-EmptyToFiller $MalwareDetectionExclusion.Note + } + $OutObj += [pscustomobject]$inobj } - $OutObj += [pscustomobject]$inobj - } - $TableParams = @{ - Name = "Malware Detection Exclusions - $VeeamBackupServer" - List = $false - ColumnWidths = 33, 33, 34 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" + $TableParams = @{ + Name = "Malware Detection Exclusions - $VeeamBackupServer" + List = $false + ColumnWidths = 33, 33, 34 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Sort-Object -Property Name | Table @TableParams } - $OutObj | Sort-Object -Property Name | Table @TableParams + } catch { + Write-PScriboMessage -IsWarning "Malware Detection Exclusions Section: $($_.Exception.Message)" } - } catch { - Write-PScriboMessage -IsWarning "Malware Detection Exclusions Section: $($_.Exception.Message)" - } - try { - Write-PScriboMessage "Discovering Veeam VBR VM Exclusions settings information from $System." - Section -ExcludeFromTOC -Style Heading5 'VM Exclusions' { - foreach ($VMExclusion in $VMExclusions) { - $OutObj = @() + if ($VMExclusions) { + try { + Write-PScriboMessage "Discovering Veeam VBR VM Exclusions settings information from $System." + Section -ExcludeFromTOC -Style Heading5 'VM Exclusions' { + foreach ($VMExclusion in $VMExclusions) { + $OutObj = @() - $inObj = [ordered] @{ - 'Name' = $VMExclusion.Name - 'Platform' = $VMExclusion.Platform - 'Note' = ConvertTo-EmptyToFiller $VMExclusion.Note - } - $OutObj += [pscustomobject]$inobj - } + $inObj = [ordered] @{ + 'Name' = $VMExclusion.Name + 'Platform' = $VMExclusion.Platform + 'Note' = ConvertTo-EmptyToFiller $VMExclusion.Note + } + $OutObj += [pscustomobject]$inobj + } - $TableParams = @{ - Name = "VM Exclusions - $VeeamBackupServer" - List = $false - ColumnWidths = 33, 33, 34 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" + $TableParams = @{ + Name = "VM Exclusions - $VeeamBackupServer" + List = $false + ColumnWidths = 33, 33, 34 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Sort-Object -Property Name | Table @TableParams + } + } catch { + Write-PScriboMessage -IsWarning "VM Exclusions Section: $($_.Exception.Message)" } - $OutObj | Sort-Object -Property Name | Table @TableParams } - } catch { - Write-PScriboMessage -IsWarning "VM Exclusions Section: $($_.Exception.Message)" } } } catch { From 4d3b47b55674d3b9e13419f166e986ba25a35853 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Tue, 27 Feb 2024 11:18:54 -0400 Subject: [PATCH 06/17] Error in Get-AbrVbrGlobalExclusion #145 --- CHANGELOG.md | 1 + Src/Private/Get-AbrVbrGlobalExclusion.ps1 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc4fad..505b676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved error handling on the Diagram section. - Fixed issue with the Veeam.Diagrammer module. - Resolved issue that prevented SureBackup Virtual Lab information to be collected. Fix [#142](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/142) +- Resolved issue in the Malware Global Exclusions section. Fix [#145](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/145) ## [0.8.4] - 2024-01-16 diff --git a/Src/Private/Get-AbrVbrGlobalExclusion.ps1 b/Src/Private/Get-AbrVbrGlobalExclusion.ps1 index e392c0d..d827659 100644 --- a/Src/Private/Get-AbrVbrGlobalExclusion.ps1 +++ b/Src/Private/Get-AbrVbrGlobalExclusion.ps1 @@ -5,7 +5,7 @@ function Get-AbrVbrGlobalExclusion { .DESCRIPTION Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo. .NOTES - Version: 0.8.4 + Version: 0.8.5 Author: Jonathan Colon Twitter: @jcolonfzenpr Github: rebelinux From d628ea1f67fb5b53794c702f97322c735bbbcda9 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Wed, 28 Feb 2024 12:41:48 -0400 Subject: [PATCH 07/17] Test DC connection before calling New-PSSession --- Src/Private/Get-AbrVbrBackupProxy.ps1 | 921 +++++++++++++------------- 1 file changed, 464 insertions(+), 457 deletions(-) diff --git a/Src/Private/Get-AbrVbrBackupProxy.ps1 b/Src/Private/Get-AbrVbrBackupProxy.ps1 index 2f96aca..3fd643c 100644 --- a/Src/Private/Get-AbrVbrBackupProxy.ps1 +++ b/Src/Private/Get-AbrVbrBackupProxy.ps1 @@ -121,435 +121,14 @@ function Get-AbrVbrBackupProxy { $BackupProxies = Get-VBRViProxy | Where-Object { $_.Host.Type -eq "Windows" } if ($BackupProxies) { $vSphereVBProxyObj = foreach ($BackupProxy in $BackupProxies) { - try { - Write-PScriboMessage "Collecting Backup Proxy Inventory Summary from $($BackupProxy.Host.Name)." - $CimSession = New-CimSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication - $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue - if ($PssSession) { - $HW = Invoke-Command -Session $PssSession -ScriptBlock { Get-ComputerInfo } - } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Hardware/Software Inventory: Unable to connect to $($BackupProxy.Host.Name)" } - if ($HW) { - $License = Get-CimInstance -Query 'Select * from SoftwareLicensingProduct' -CimSession $CimSession | Where-Object { $_.LicenseStatus -eq 1 } - $HWCPU = Get-CimInstance -Class Win32_Processor -CimSession $CimSession - $HWBIOS = Get-CimInstance -Class Win32_Bios -CimSession $CimSession - Section -Style Heading5 $($BackupProxy.Host.Name.Split(".")[0]) { - $OutObj = @() - $inObj = [ordered] @{ - 'Name' = $HW.CsDNSHostName - 'Windows Product Name' = $HW.WindowsProductName - 'Windows Current Version' = $HW.WindowsCurrentVersion - 'Windows Build Number' = $HW.OsVersion - 'Windows Install Type' = $HW.WindowsInstallationType - 'Active Directory Domain' = $HW.CsDomain - 'Windows Installation Date' = $HW.OsInstallDate - 'Time Zone' = $HW.TimeZone - 'License Type' = $License.ProductKeyChannel - 'Partial Product Key' = $License.PartialProductKey - 'Manufacturer' = $HW.CsManufacturer - 'Model' = $HW.CsModel - 'Serial Number' = $HWBIOS.SerialNumber - 'Bios Type' = $HW.BiosFirmwareType - 'BIOS Version' = $HWBIOS.Version - 'Processor Manufacturer' = $HWCPU[0].Manufacturer - 'Processor Model' = $HWCPU[0].Name - 'Number of CPU Cores' = $HWCPU[0].NumberOfCores - 'Number of Logical Cores' = $HWCPU[0].NumberOfLogicalProcessors - 'Physical Memory (GB)' = ConvertTo-FileSizeString $HW.CsTotalPhysicalMemory - } - $OutObj += [pscustomobject]$inobj - - if ($HealthCheck.Infrastructure.Server) { - $OutObj | Where-Object { $_.'Number of CPU Cores' -lt 4 } | Set-Style -Style Warning -Property 'Number of CPU Cores' - if ([int]([regex]::Matches($OutObj.'Physical Memory (GB)', "\d+(?!.*\d+)").value) -lt 8) { $OutObj | Set-Style -Style Warning -Property 'Physical Memory (GB)' } - } - - $TableParams = @{ - Name = "Backup Proxy Inventory - $($BackupProxy.Host.Name.Split(".")[0])" - List = $true - ColumnWidths = 40, 60 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $OutObj | Table @TableParams - #---------------------------------------------------------------------------------------------# - # Backup Proxy Local Disk Inventory Section # - #---------------------------------------------------------------------------------------------# - if ($InfoLevel.Infrastructure.Proxy -ge 3) { - try { - $HostDisks = Invoke-Command -Session $PssSession -ScriptBlock { Get-Disk | Where-Object { $_.BusType -ne "iSCSI" -and $_.BusType -ne "Fibre Channel" } } - if ($HostDisks) { - Section -Style NOTOCHeading6 -ExcludeFromTOC 'Local Disks' { - $LocalDiskReport = @() - ForEach ($Disk in $HostDisks) { - try { - $TempLocalDiskReport = [PSCustomObject]@{ - 'Disk Number' = $Disk.Number - 'Model' = $Disk.Model - 'Serial Number' = $Disk.SerialNumber - 'Partition Style' = $Disk.PartitionStyle - 'Disk Size' = "$([Math]::Round($Disk.Size / 1Gb)) GB" - } - $LocalDiskReport += $TempLocalDiskReport - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Local Disks $($Disk.Number) Section: $($_.Exception.Message)" - } - } - $TableParams = @{ - Name = "Local Disks - $($BackupProxies.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 20, 20, 20, 20, 20 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $LocalDiskReport | Sort-Object -Property 'Disk Number' | Table @TableParams - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel Section: $($_.Exception.Message)" - } - #---------------------------------------------------------------------------------------------# - # Backup Proxy SAN Disk Inventory Section # - #---------------------------------------------------------------------------------------------# - try { - $SanDisks = Invoke-Command -Session $PssSession -ScriptBlock { Get-Disk | Where-Object { $_.BusType -Eq "iSCSI" -or $_.BusType -Eq "Fibre Channel" } } - if ($SanDisks) { - Section -Style NOTOCHeading6 -ExcludeFromTOC 'SAN Disks' { - $SanDiskReport = @() - ForEach ($Disk in $SanDisks) { - try { - $TempSanDiskReport = [PSCustomObject]@{ - 'Disk Number' = $Disk.Number - 'Model' = $Disk.Model - 'Serial Number' = $Disk.SerialNumber - 'Partition Style' = $Disk.PartitionStyle - 'Disk Size' = "$([Math]::Round($Disk.Size / 1Gb)) GB" - } - $SanDiskReport += $TempSanDiskReport - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel $($Disk.Number) Section: $($_.Exception.Message)" - } - } - $TableParams = @{ - Name = "SAN Disks - $($BackupProxies.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 20, 20, 20, 20, 20 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $SanDiskReport | Sort-Object -Property 'Disk Number' | Table @TableParams - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel Section: $($_.Exception.Message)" - } - } - try { - $HostVolumes = Invoke-Command -Session $PssSession -ScriptBlock { Get-Volume | Where-Object { $_.DriveType -ne "CD-ROM" -and $NUll -ne $_.DriveLetter } } - if ($HostVolumes) { - Section -Style NOTOCHeading6 -ExcludeFromTOC 'Host Volumes' { - $HostVolumeReport = @() - ForEach ($HostVolume in $HostVolumes) { - try { - $TempHostVolumeReport = [PSCustomObject]@{ - 'Drive Letter' = $HostVolume.DriveLetter - 'File System Label' = $HostVolume.FileSystemLabel - 'File System' = $HostVolume.FileSystem - 'Size' = "$([Math]::Round($HostVolume.Size / 1gb)) GB" - 'Free Space' = "$([Math]::Round($HostVolume.SizeRemaining / 1gb)) GB" - 'Health Status' = $HostVolume.HealthStatus - } - $HostVolumeReport += $TempHostVolumeReport - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Host Volumes $($HostVolume.DriveLetter) Section: $($_.Exception.Message)" - } - } - $TableParams = @{ - Name = "Volumes - $($BackupProxies.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 15, 15, 15, 20, 20, 15 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $HostVolumeReport | Sort-Object -Property 'Drive Letter' | Table @TableParams - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Host Volumes Section: $($_.Exception.Message)" - } - #---------------------------------------------------------------------------------------------# - # Backup Proxy Network Inventory Section # - #---------------------------------------------------------------------------------------------# - if ($InfoLevel.Infrastructure.Proxy -ge 2) { - try { - $HostAdapters = Invoke-Command -Session $PssSession { Get-NetAdapter } - if ($HostAdapters) { - Section -Style NOTOCHeading3 -ExcludeFromTOC 'Network Adapters' { - $HostAdaptersReport = @() - ForEach ($HostAdapter in $HostAdapters) { - try { - $TempHostAdaptersReport = [PSCustomObject]@{ - 'Adapter Name' = $HostAdapter.Name - 'Adapter Description' = $HostAdapter.InterfaceDescription - 'Mac Address' = $HostAdapter.MacAddress - 'Link Speed' = $HostAdapter.LinkSpeed - } - $HostAdaptersReport += $TempHostAdaptersReport - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Network Adapter $($HostAdapter.Name) Section: $($_.Exception.Message)" - } - } - $TableParams = @{ - Name = "Network Adapters - $($BackupProxies.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 30, 35, 20, 15 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $HostAdaptersReport | Sort-Object -Property 'Adapter Name' | Table @TableParams - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Network Adapter Section: $($_.Exception.Message)" - } - try { - $NetIPs = Invoke-Command -Session $PssSession { Get-NetIPConfiguration | Where-Object -FilterScript { ($_.NetAdapter.Status -Eq "Up") } } - if ($NetIPs) { - Section -Style NOTOCHeading3 -ExcludeFromTOC 'IP Address' { - $NetIpsReport = @() - ForEach ($NetIp in $NetIps) { - try { - $TempNetIpsReport = [PSCustomObject]@{ - 'Interface Name' = $NetIp.InterfaceAlias - 'Interface Description' = $NetIp.InterfaceDescription - 'IPv4 Addresses' = $NetIp.IPv4Address.IPAddress -Join "," - 'Subnet Mask' = $NetIp.IPv4Address[0].PrefixLength - 'IPv4 Gateway' = $NetIp.IPv4DefaultGateway.NextHop - } - $NetIpsReport += $TempNetIpsReport - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies IP Address $($NetIp.InterfaceAlias) Section: $($_.Exception.Message)" - } - } - $TableParams = @{ - Name = "IP Address - $($BackupProxies.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 25, 25, 20, 10, 20 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $NetIpsReport | Sort-Object -Property 'Interface Name' | Table @TableParams - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies IP Address Section: $($_.Exception.Message)" - } - } - } - Remove-PSSession -Session $PssSession - Remove-CimSession $CimSession - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Section: $($_.Exception.Message)" - } - } - if ($vSphereVBProxyObj) { - Section -Style Heading4 "Hardware & Software Inventory" { - $vSphereVBProxyObj - } - } - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Hardware & Software Inventory Section: $($_.Exception.Message)" - } - #---------------------------------------------------------------------------------------------# - # VMware Backup Proxy Service information Section # - #---------------------------------------------------------------------------------------------# - if ($HealthCheck.Infrastructure.Server) { - try { - if ($InfoLevel.Infrastructure.Proxy -ge 1) { - Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." - Write-PScriboMessage "Collecting Veeam Services Information." - $BackupProxies = Get-VBRViProxy | Where-Object { $_.Host.Type -eq "Windows" } - foreach ($BackupProxy in $BackupProxies) { - try { - $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue - if ($PssSession) { - $Available = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service "W32Time" | Select-Object DisplayName, Name, Status } - Write-PScriboMessage "Collecting Backup Proxy Service information from $($BackupProxy.Name)." - $Services = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service Veeam* } - if ($PssSession) { - Remove-PSSession -Session $PssSession - } - if ($Available -and $Services) { - Section -Style NOTOCHeading4 -ExcludeFromTOC "HealthCheck - $($BackupProxy.Host.Name.Split(".")[0]) Services Status" { - $OutObj = @() - foreach ($Service in $Services) { - Write-PScriboMessage "Collecting '$($Service.DisplayName)' status on $($BackupProxy.Name)." - $inObj = [ordered] @{ - 'Display Name' = $Service.DisplayName - 'Short Name' = $Service.Name - 'Status' = $Service.Status - } - $OutObj += [pscustomobject]$inobj - } - - if ($HealthCheck.Infrastructure.Server) { - $OutObj | Where-Object { $_.'Status' -notlike 'Running' } | Set-Style -Style Warning -Property 'Status' - } - - $TableParams = @{ - Name = "HealthCheck - Services Status - $($BackupProxy.Host.Name.Split(".")[0])" - List = $false - ColumnWidths = 45, 35, 20 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $OutObj | Sort-Object -Property 'Display Name' | Table @TableParams - } - } - } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Services Status Section: Unable to connect to $($BackupProxy.Host.Name)" } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies $($BackupProxy.Host.Name) Services Status Section: $($_.Exception.Message)" - } - } - } - } catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxies Services Status Section: $($_.Exception.Message)" - } - } - if ($Options.EnableDiagrams) { - Try { - Try { - $Graph = New-VeeamDiagram -Target $System -Credential $Credential -Format base64 -Direction top-to-bottom -DiagramType "Backup-to-vSphere-Proxy" - } Catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxy Diagram: $($_.Exception.Message)" - } - if ($Graph) { - Section -Style Heading3 "VMware Backup Proxy Diagram." { - Image -Base64 $Graph -Text "VMware Backup Proxy Diagram" -Percent (Get-ImagePercent -Graph $Graph) -Align Center - Paragraph "Image preview: Opens the image in a new tab to view it at full resolution." -Tabs 2 - } - BlankLine - } - } Catch { - Write-PScriboMessage -IsWarning "VMware Backup Proxy Diagram Section: $($_.Exception.Message)" - } - } - } - } - #---------------------------------------------------------------------------------------------# - # Hyper-V Backup Prxy information Section # - #---------------------------------------------------------------------------------------------# - try { - $BackupProxies = Get-VBRHvProxy - if ($BackupProxies) { - Section -Style Heading4 'Hyper-V Backup Proxies' { - $OutObj = @() - if ($InfoLevel.Infrastructure.Proxy -eq 1) { - Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." - Write-PScriboMessage "Collecting Summary Information." - foreach ($BackupProxy in $BackupProxies) { - try { - Write-PScriboMessage "Discovered $($BackupProxy.Name) Proxy." - $inObj = [ordered] @{ - 'Name' = $BackupProxy.Name - 'Type' = $BackupProxy.Type - 'Max Tasks Count' = $BackupProxy.MaxTasksCount - 'Disabled' = ConvertTo-TextYN $BackupProxy.IsDisabled - 'Status' = Switch (($BackupProxy.Host).IsUnavailable) { - 'False' { 'Available' } - 'True' { 'Unavailable' } - default { ($BackupProxy.Host).IsUnavailable } - } - } - $OutObj += [pscustomobject]$inobj - } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies $($BackupProxy.Name) Section: $($_.Exception.Message)" - } - } - - if ($HealthCheck.Infrastructure.Proxy) { - $OutObj | Where-Object { $_.'Status' -eq 'Unavailable' } | Set-Style -Style Warning -Property 'Status' - } - - $TableParams = @{ - Name = "Backup Proxy - $VeeamBackupServer" - List = $false - ColumnWidths = 35, 15, 15, 15, 20 - } - - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $OutObj | Table @TableParams - } - if ($InfoLevel.Infrastructure.Proxy -ge 2) { - Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." - Write-PScriboMessage "Collecting Detailed Information." - foreach ($BackupProxy in $BackupProxies) { - try { - Write-PScriboMessage "Discovered $($BackupProxy.Name) Repository." - $inObj = [ordered] @{ - 'Name' = $BackupProxy.Name - 'Host Name' = $BackupProxy.Host.Name - 'Type' = $BackupProxy.Type - 'Disabled' = ConvertTo-TextYN $BackupProxy.IsDisabled - 'Max Tasks Count' = $BackupProxy.MaxTasksCount - 'AutoDetect Volumes' = ConvertTo-TextYN $BackupProxy.Options.IsAutoDetectVolumes - 'OS Type' = $BackupProxy.Host.Type - 'Services Credential' = ConvertTo-EmptyToFiller $BackupProxy.Host.ProxyServicesCreds.Name - 'Status' = Switch (($BackupProxy.Host).IsUnavailable) { - 'False' { 'Available' } - 'True' { 'Unavailable' } - default { ($BackupProxy.Host).IsUnavailable } - } - } - $OutObj = [pscustomobject]$inobj - - if ($HealthCheck.Infrastructure.Proxy) { - $OutObj | Where-Object { $_.'Status' -eq 'Unavailable' } | Set-Style -Style Warning -Property 'Status' - } - - $TableParams = @{ - Name = "Backup Proxy - $($BackupProxy.Host.Name.Split(".")[0])" - List = $true - ColumnWidths = 40, 60 - } - if ($Report.ShowTableCaptions) { - $TableParams['Caption'] = "- $($TableParams.Name)" - } - $OutObj | Table @TableParams - } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies $($BackupProxy.Name) Section: $($_.Exception.Message)" - } - } - } - #---------------------------------------------------------------------------------------------# - # Hyper-V Backup Proxy Inventory Summary Section # - #---------------------------------------------------------------------------------------------# - try { - Write-PScriboMessage "Hardware Inventory Status set as $($Options.EnableHardwareInventory)." - if ($Options.EnableHardwareInventory) { - Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." - Write-PScriboMessage "Collecting Hardware/Software Inventory Summary." - $BackupProxies = Get-VBRHvProxy - if ($BackupProxies) { - $HyperVBProxyObj = foreach ($BackupProxy in $BackupProxies) { + if (Test-Connection -ComputerName $BackupProxy.Host.Name -Quiet -Count 2) { try { Write-PScriboMessage "Collecting Backup Proxy Inventory Summary from $($BackupProxy.Host.Name)." $CimSession = New-CimSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue if ($PssSession) { $HW = Invoke-Command -Session $PssSession -ScriptBlock { Get-ComputerInfo } - } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Inventory Section: Unable to connect to $($BackupProxy.Host.Name)" } + } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Hardware/Software Inventory: Unable to connect to $($BackupProxy.Host.Name)" } if ($HW) { $License = Get-CimInstance -Query 'Select * from SoftwareLicensingProduct' -CimSession $CimSession | Where-Object { $_.LicenseStatus -eq 1 } $HWCPU = Get-CimInstance -Class Win32_Processor -CimSession $CimSession @@ -614,7 +193,7 @@ function Get-AbrVbrBackupProxy { } $LocalDiskReport += $TempLocalDiskReport } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk $($Disk.Number) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Local Disks $($Disk.Number) Section: $($_.Exception.Message)" } } $TableParams = @{ @@ -629,7 +208,7 @@ function Get-AbrVbrBackupProxy { } } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel Section: $($_.Exception.Message)" } #---------------------------------------------------------------------------------------------# # Backup Proxy SAN Disk Inventory Section # @@ -650,7 +229,7 @@ function Get-AbrVbrBackupProxy { } $SanDiskReport += $TempSanDiskReport } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies SAN Disk $($Disk.Number) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel $($Disk.Number) Section: $($_.Exception.Message)" } } $TableParams = @{ @@ -665,12 +244,9 @@ function Get-AbrVbrBackupProxy { } } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Fibre Channel Section: $($_.Exception.Message)" } } - #---------------------------------------------------------------------------------------------# - # Backup Proxy Volume Inventory Section # - #---------------------------------------------------------------------------------------------# try { $HostVolumes = Invoke-Command -Session $PssSession -ScriptBlock { Get-Volume | Where-Object { $_.DriveType -ne "CD-ROM" -and $NUll -ne $_.DriveLetter } } if ($HostVolumes) { @@ -688,7 +264,7 @@ function Get-AbrVbrBackupProxy { } $HostVolumeReport += $TempHostVolumeReport } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Host Volume $($HostVolume.DriveLetter) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Host Volumes $($HostVolume.DriveLetter) Section: $($_.Exception.Message)" } } $TableParams = @{ @@ -703,7 +279,7 @@ function Get-AbrVbrBackupProxy { } } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Host Volume Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Host Volumes Section: $($_.Exception.Message)" } #---------------------------------------------------------------------------------------------# # Backup Proxy Network Inventory Section # @@ -712,7 +288,7 @@ function Get-AbrVbrBackupProxy { try { $HostAdapters = Invoke-Command -Session $PssSession { Get-NetAdapter } if ($HostAdapters) { - Section -Style NOTOCHeading6 -ExcludeFromTOC 'Network Adapters' { + Section -Style NOTOCHeading3 -ExcludeFromTOC 'Network Adapters' { $HostAdaptersReport = @() ForEach ($HostAdapter in $HostAdapters) { try { @@ -724,7 +300,7 @@ function Get-AbrVbrBackupProxy { } $HostAdaptersReport += $TempHostAdaptersReport } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Network Adapter $($HostAdapter.Name) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Network Adapter $($HostAdapter.Name) Section: $($_.Exception.Message)" } } $TableParams = @{ @@ -739,12 +315,12 @@ function Get-AbrVbrBackupProxy { } } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Network Adapter Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Network Adapter Section: $($_.Exception.Message)" } try { $NetIPs = Invoke-Command -Session $PssSession { Get-NetIPConfiguration | Where-Object -FilterScript { ($_.NetAdapter.Status -Eq "Up") } } if ($NetIPs) { - Section -Style NOTOCHeading6 -ExcludeFromTOC 'IP Address' { + Section -Style NOTOCHeading3 -ExcludeFromTOC 'IP Address' { $NetIpsReport = @() ForEach ($NetIp in $NetIps) { try { @@ -757,7 +333,7 @@ function Get-AbrVbrBackupProxy { } $NetIpsReport += $TempNetIpsReport } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies IP Address $($NetIp.InterfaceAlias) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies IP Address $($NetIp.InterfaceAlias) Section: $($_.Exception.Message)" } } $TableParams = @{ @@ -772,7 +348,7 @@ function Get-AbrVbrBackupProxy { } } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies IP Address Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies IP Address Section: $($_.Exception.Message)" } } } @@ -780,30 +356,31 @@ function Get-AbrVbrBackupProxy { Remove-CimSession $CimSession } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Hardware & Software Inventory Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies Section: $($_.Exception.Message)" } } - if ($HyperVBProxyObj) { - Section -Style Heading4 'Hardware & Software Inventory' { - $HyperVBProxyObj - } - + } + if ($vSphereVBProxyObj) { + Section -Style Heading4 "Hardware & Software Inventory" { + $vSphereVBProxyObj } } } - } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Section: $($_.Exception.Message)" } - #---------------------------------------------------------------------------------------------# - # Hyper-V Backup Proxy Service information Section # - #---------------------------------------------------------------------------------------------# - if ($HealthCheck.Infrastructure.Server) { - try { - if ($InfoLevel.Infrastructure.Proxy -ge 1) { - Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." - Write-PScriboMessage "Collecting Veeam Service Information." - $BackupProxies = Get-VBRHvProxy - foreach ($BackupProxy in $BackupProxies) { + } catch { + Write-PScriboMessage -IsWarning "VMware Backup Proxies Hardware & Software Inventory Section: $($_.Exception.Message)" + } + #---------------------------------------------------------------------------------------------# + # VMware Backup Proxy Service information Section # + #---------------------------------------------------------------------------------------------# + if ($HealthCheck.Infrastructure.Server) { + try { + if ($InfoLevel.Infrastructure.Proxy -ge 1) { + Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." + Write-PScriboMessage "Collecting Veeam Services Information." + $BackupProxies = Get-VBRViProxy | Where-Object { $_.Host.Type -eq "Windows" } + foreach ($BackupProxy in $BackupProxies) { + if (Test-Connection -ComputerName $BackupProxy.Host.Name -Quiet -Count 2) { try { $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue if ($PssSession) { @@ -843,7 +420,437 @@ function Get-AbrVbrBackupProxy { } } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Services Status Section: Unable to connect to $($BackupProxy.Host.Name)" } } catch { - Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Services Status - $($BackupProxy.Host.Name.Split(".")[0]) Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "VMware Backup Proxies $($BackupProxy.Host.Name) Services Status Section: $($_.Exception.Message)" + } + } + } + } + } catch { + Write-PScriboMessage -IsWarning "VMware Backup Proxies Services Status Section: $($_.Exception.Message)" + } + } + if ($Options.EnableDiagrams) { + Try { + Try { + $Graph = New-VeeamDiagram -Target $System -Credential $Credential -Format base64 -Direction top-to-bottom -DiagramType "Backup-to-vSphere-Proxy" + } Catch { + Write-PScriboMessage -IsWarning "VMware Backup Proxy Diagram: $($_.Exception.Message)" + } + if ($Graph) { + Section -Style Heading3 "VMware Backup Proxy Diagram." { + Image -Base64 $Graph -Text "VMware Backup Proxy Diagram" -Percent (Get-ImagePercent -Graph $Graph) -Align Center + Paragraph "Image preview: Opens the image in a new tab to view it at full resolution." -Tabs 2 + } + BlankLine + } + } Catch { + Write-PScriboMessage -IsWarning "VMware Backup Proxy Diagram Section: $($_.Exception.Message)" + } + } + } + } + #---------------------------------------------------------------------------------------------# + # Hyper-V Backup Prxy information Section # + #---------------------------------------------------------------------------------------------# + try { + $BackupProxies = Get-VBRHvProxy + if ($BackupProxies) { + Section -Style Heading4 'Hyper-V Backup Proxies' { + $OutObj = @() + if ($InfoLevel.Infrastructure.Proxy -eq 1) { + Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." + Write-PScriboMessage "Collecting Summary Information." + foreach ($BackupProxy in $BackupProxies) { + try { + Write-PScriboMessage "Discovered $($BackupProxy.Name) Proxy." + $inObj = [ordered] @{ + 'Name' = $BackupProxy.Name + 'Type' = $BackupProxy.Type + 'Max Tasks Count' = $BackupProxy.MaxTasksCount + 'Disabled' = ConvertTo-TextYN $BackupProxy.IsDisabled + 'Status' = Switch (($BackupProxy.Host).IsUnavailable) { + 'False' { 'Available' } + 'True' { 'Unavailable' } + default { ($BackupProxy.Host).IsUnavailable } + } + } + $OutObj += [pscustomobject]$inobj + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies $($BackupProxy.Name) Section: $($_.Exception.Message)" + } + } + + if ($HealthCheck.Infrastructure.Proxy) { + $OutObj | Where-Object { $_.'Status' -eq 'Unavailable' } | Set-Style -Style Warning -Property 'Status' + } + + $TableParams = @{ + Name = "Backup Proxy - $VeeamBackupServer" + List = $false + ColumnWidths = 35, 15, 15, 15, 20 + } + + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } + if ($InfoLevel.Infrastructure.Proxy -ge 2) { + Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." + Write-PScriboMessage "Collecting Detailed Information." + foreach ($BackupProxy in $BackupProxies) { + try { + Write-PScriboMessage "Discovered $($BackupProxy.Name) Repository." + $inObj = [ordered] @{ + 'Name' = $BackupProxy.Name + 'Host Name' = $BackupProxy.Host.Name + 'Type' = $BackupProxy.Type + 'Disabled' = ConvertTo-TextYN $BackupProxy.IsDisabled + 'Max Tasks Count' = $BackupProxy.MaxTasksCount + 'AutoDetect Volumes' = ConvertTo-TextYN $BackupProxy.Options.IsAutoDetectVolumes + 'OS Type' = $BackupProxy.Host.Type + 'Services Credential' = ConvertTo-EmptyToFiller $BackupProxy.Host.ProxyServicesCreds.Name + 'Status' = Switch (($BackupProxy.Host).IsUnavailable) { + 'False' { 'Available' } + 'True' { 'Unavailable' } + default { ($BackupProxy.Host).IsUnavailable } + } + } + $OutObj = [pscustomobject]$inobj + + if ($HealthCheck.Infrastructure.Proxy) { + $OutObj | Where-Object { $_.'Status' -eq 'Unavailable' } | Set-Style -Style Warning -Property 'Status' + } + + $TableParams = @{ + Name = "Backup Proxy - $($BackupProxy.Host.Name.Split(".")[0])" + List = $true + ColumnWidths = 40, 60 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies $($BackupProxy.Name) Section: $($_.Exception.Message)" + } + } + } + #---------------------------------------------------------------------------------------------# + # Hyper-V Backup Proxy Inventory Summary Section # + #---------------------------------------------------------------------------------------------# + try { + Write-PScriboMessage "Hardware Inventory Status set as $($Options.EnableHardwareInventory)." + if ($Options.EnableHardwareInventory) { + Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." + Write-PScriboMessage "Collecting Hardware/Software Inventory Summary." + $BackupProxies = Get-VBRHvProxy + if ($BackupProxies) { + $HyperVBProxyObj = foreach ($BackupProxy in $BackupProxies) { + if (Test-Connection -ComputerName $BackupProxy.Host.Name -Quiet -Count 2) { + try { + Write-PScriboMessage "Collecting Backup Proxy Inventory Summary from $($BackupProxy.Host.Name)." + $CimSession = New-CimSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication + $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue + if ($PssSession) { + $HW = Invoke-Command -Session $PssSession -ScriptBlock { Get-ComputerInfo } + } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Inventory Section: Unable to connect to $($BackupProxy.Host.Name)" } + if ($HW) { + $License = Get-CimInstance -Query 'Select * from SoftwareLicensingProduct' -CimSession $CimSession | Where-Object { $_.LicenseStatus -eq 1 } + $HWCPU = Get-CimInstance -Class Win32_Processor -CimSession $CimSession + $HWBIOS = Get-CimInstance -Class Win32_Bios -CimSession $CimSession + Section -Style Heading5 $($BackupProxy.Host.Name.Split(".")[0]) { + $OutObj = @() + $inObj = [ordered] @{ + 'Name' = $HW.CsDNSHostName + 'Windows Product Name' = $HW.WindowsProductName + 'Windows Current Version' = $HW.WindowsCurrentVersion + 'Windows Build Number' = $HW.OsVersion + 'Windows Install Type' = $HW.WindowsInstallationType + 'Active Directory Domain' = $HW.CsDomain + 'Windows Installation Date' = $HW.OsInstallDate + 'Time Zone' = $HW.TimeZone + 'License Type' = $License.ProductKeyChannel + 'Partial Product Key' = $License.PartialProductKey + 'Manufacturer' = $HW.CsManufacturer + 'Model' = $HW.CsModel + 'Serial Number' = $HWBIOS.SerialNumber + 'Bios Type' = $HW.BiosFirmwareType + 'BIOS Version' = $HWBIOS.Version + 'Processor Manufacturer' = $HWCPU[0].Manufacturer + 'Processor Model' = $HWCPU[0].Name + 'Number of CPU Cores' = $HWCPU[0].NumberOfCores + 'Number of Logical Cores' = $HWCPU[0].NumberOfLogicalProcessors + 'Physical Memory (GB)' = ConvertTo-FileSizeString $HW.CsTotalPhysicalMemory + } + $OutObj += [pscustomobject]$inobj + + if ($HealthCheck.Infrastructure.Server) { + $OutObj | Where-Object { $_.'Number of CPU Cores' -lt 4 } | Set-Style -Style Warning -Property 'Number of CPU Cores' + if ([int]([regex]::Matches($OutObj.'Physical Memory (GB)', "\d+(?!.*\d+)").value) -lt 8) { $OutObj | Set-Style -Style Warning -Property 'Physical Memory (GB)' } + } + + $TableParams = @{ + Name = "Backup Proxy Inventory - $($BackupProxy.Host.Name.Split(".")[0])" + List = $true + ColumnWidths = 40, 60 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Table @TableParams + #---------------------------------------------------------------------------------------------# + # Backup Proxy Local Disk Inventory Section # + #---------------------------------------------------------------------------------------------# + if ($InfoLevel.Infrastructure.Proxy -ge 3) { + try { + $HostDisks = Invoke-Command -Session $PssSession -ScriptBlock { Get-Disk | Where-Object { $_.BusType -ne "iSCSI" -and $_.BusType -ne "Fibre Channel" } } + if ($HostDisks) { + Section -Style NOTOCHeading6 -ExcludeFromTOC 'Local Disks' { + $LocalDiskReport = @() + ForEach ($Disk in $HostDisks) { + try { + $TempLocalDiskReport = [PSCustomObject]@{ + 'Disk Number' = $Disk.Number + 'Model' = $Disk.Model + 'Serial Number' = $Disk.SerialNumber + 'Partition Style' = $Disk.PartitionStyle + 'Disk Size' = "$([Math]::Round($Disk.Size / 1Gb)) GB" + } + $LocalDiskReport += $TempLocalDiskReport + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk $($Disk.Number) Section: $($_.Exception.Message)" + } + } + $TableParams = @{ + Name = "Local Disks - $($BackupProxies.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 20, 20, 20, 20, 20 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $LocalDiskReport | Sort-Object -Property 'Disk Number' | Table @TableParams + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk Section: $($_.Exception.Message)" + } + #---------------------------------------------------------------------------------------------# + # Backup Proxy SAN Disk Inventory Section # + #---------------------------------------------------------------------------------------------# + try { + $SanDisks = Invoke-Command -Session $PssSession -ScriptBlock { Get-Disk | Where-Object { $_.BusType -Eq "iSCSI" -or $_.BusType -Eq "Fibre Channel" } } + if ($SanDisks) { + Section -Style NOTOCHeading6 -ExcludeFromTOC 'SAN Disks' { + $SanDiskReport = @() + ForEach ($Disk in $SanDisks) { + try { + $TempSanDiskReport = [PSCustomObject]@{ + 'Disk Number' = $Disk.Number + 'Model' = $Disk.Model + 'Serial Number' = $Disk.SerialNumber + 'Partition Style' = $Disk.PartitionStyle + 'Disk Size' = "$([Math]::Round($Disk.Size / 1Gb)) GB" + } + $SanDiskReport += $TempSanDiskReport + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies SAN Disk $($Disk.Number) Section: $($_.Exception.Message)" + } + } + $TableParams = @{ + Name = "SAN Disks - $($BackupProxies.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 20, 20, 20, 20, 20 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $SanDiskReport | Sort-Object -Property 'Disk Number' | Table @TableParams + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Local Disk Section: $($_.Exception.Message)" + } + } + #---------------------------------------------------------------------------------------------# + # Backup Proxy Volume Inventory Section # + #---------------------------------------------------------------------------------------------# + try { + $HostVolumes = Invoke-Command -Session $PssSession -ScriptBlock { Get-Volume | Where-Object { $_.DriveType -ne "CD-ROM" -and $NUll -ne $_.DriveLetter } } + if ($HostVolumes) { + Section -Style NOTOCHeading6 -ExcludeFromTOC 'Host Volumes' { + $HostVolumeReport = @() + ForEach ($HostVolume in $HostVolumes) { + try { + $TempHostVolumeReport = [PSCustomObject]@{ + 'Drive Letter' = $HostVolume.DriveLetter + 'File System Label' = $HostVolume.FileSystemLabel + 'File System' = $HostVolume.FileSystem + 'Size' = "$([Math]::Round($HostVolume.Size / 1gb)) GB" + 'Free Space' = "$([Math]::Round($HostVolume.SizeRemaining / 1gb)) GB" + 'Health Status' = $HostVolume.HealthStatus + } + $HostVolumeReport += $TempHostVolumeReport + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Host Volume $($HostVolume.DriveLetter) Section: $($_.Exception.Message)" + } + } + $TableParams = @{ + Name = "Volumes - $($BackupProxies.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 15, 15, 15, 20, 20, 15 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $HostVolumeReport | Sort-Object -Property 'Drive Letter' | Table @TableParams + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Host Volume Section: $($_.Exception.Message)" + } + #---------------------------------------------------------------------------------------------# + # Backup Proxy Network Inventory Section # + #---------------------------------------------------------------------------------------------# + if ($InfoLevel.Infrastructure.Proxy -ge 2) { + try { + $HostAdapters = Invoke-Command -Session $PssSession { Get-NetAdapter } + if ($HostAdapters) { + Section -Style NOTOCHeading6 -ExcludeFromTOC 'Network Adapters' { + $HostAdaptersReport = @() + ForEach ($HostAdapter in $HostAdapters) { + try { + $TempHostAdaptersReport = [PSCustomObject]@{ + 'Adapter Name' = $HostAdapter.Name + 'Adapter Description' = $HostAdapter.InterfaceDescription + 'Mac Address' = $HostAdapter.MacAddress + 'Link Speed' = $HostAdapter.LinkSpeed + } + $HostAdaptersReport += $TempHostAdaptersReport + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Network Adapter $($HostAdapter.Name) Section: $($_.Exception.Message)" + } + } + $TableParams = @{ + Name = "Network Adapters - $($BackupProxies.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 30, 35, 20, 15 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $HostAdaptersReport | Sort-Object -Property 'Adapter Name' | Table @TableParams + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Network Adapter Section: $($_.Exception.Message)" + } + try { + $NetIPs = Invoke-Command -Session $PssSession { Get-NetIPConfiguration | Where-Object -FilterScript { ($_.NetAdapter.Status -Eq "Up") } } + if ($NetIPs) { + Section -Style NOTOCHeading6 -ExcludeFromTOC 'IP Address' { + $NetIpsReport = @() + ForEach ($NetIp in $NetIps) { + try { + $TempNetIpsReport = [PSCustomObject]@{ + 'Interface Name' = $NetIp.InterfaceAlias + 'Interface Description' = $NetIp.InterfaceDescription + 'IPv4 Addresses' = $NetIp.IPv4Address.IPAddress -Join "," + 'Subnet Mask' = $NetIp.IPv4Address[0].PrefixLength + 'IPv4 Gateway' = $NetIp.IPv4DefaultGateway.NextHop + } + $NetIpsReport += $TempNetIpsReport + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies IP Address $($NetIp.InterfaceAlias) Section: $($_.Exception.Message)" + } + } + $TableParams = @{ + Name = "IP Address - $($BackupProxies.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 25, 25, 20, 10, 20 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $NetIpsReport | Sort-Object -Property 'Interface Name' | Table @TableParams + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies IP Address Section: $($_.Exception.Message)" + } + } + } + Remove-PSSession -Session $PssSession + Remove-CimSession $CimSession + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Hardware & Software Inventory Section: $($_.Exception.Message)" + } + } + } + if ($HyperVBProxyObj) { + Section -Style Heading4 'Hardware & Software Inventory' { + $HyperVBProxyObj + } + } + } + } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Section: $($_.Exception.Message)" + } + #---------------------------------------------------------------------------------------------# + # Hyper-V Backup Proxy Service information Section # + #---------------------------------------------------------------------------------------------# + if ($HealthCheck.Infrastructure.Server) { + try { + if ($InfoLevel.Infrastructure.Proxy -ge 1) { + Write-PScriboMessage "Backup Proxy InfoLevel set at $($InfoLevel.Infrastructure.Proxy)." + Write-PScriboMessage "Collecting Veeam Service Information." + $BackupProxies = Get-VBRHvProxy + foreach ($BackupProxy in $BackupProxies) { + if (Test-Connection -ComputerName $BackupProxy.Host.Name -Quiet -Count 2) { + try { + $PssSession = New-PSSession $BackupProxy.Host.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction SilentlyContinue + if ($PssSession) { + $Available = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service "W32Time" | Select-Object DisplayName, Name, Status } + Write-PScriboMessage "Collecting Backup Proxy Service information from $($BackupProxy.Name)." + $Services = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service Veeam* } + if ($PssSession) { + Remove-PSSession -Session $PssSession + } + if ($Available -and $Services) { + Section -Style NOTOCHeading4 -ExcludeFromTOC "HealthCheck - $($BackupProxy.Host.Name.Split(".")[0]) Services Status" { + $OutObj = @() + foreach ($Service in $Services) { + Write-PScriboMessage "Collecting '$($Service.DisplayName)' status on $($BackupProxy.Name)." + $inObj = [ordered] @{ + 'Display Name' = $Service.DisplayName + 'Short Name' = $Service.Name + 'Status' = $Service.Status + } + $OutObj += [pscustomobject]$inobj + } + + if ($HealthCheck.Infrastructure.Server) { + $OutObj | Where-Object { $_.'Status' -notlike 'Running' } | Set-Style -Style Warning -Property 'Status' + } + + $TableParams = @{ + Name = "HealthCheck - Services Status - $($BackupProxy.Host.Name.Split(".")[0])" + List = $false + ColumnWidths = 45, 35, 20 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $OutObj | Sort-Object -Property 'Display Name' | Table @TableParams + } + } + } else { Write-PScriboMessage -IsWarning "VMware Backup Proxies Services Status Section: Unable to connect to $($BackupProxy.Host.Name)" } + } catch { + Write-PScriboMessage -IsWarning "Hyper-V Backup Proxies Services Status - $($BackupProxy.Host.Name.Split(".")[0]) Section: $($_.Exception.Message)" + } } } } From c36467bcf27f79f742d90127645b720451cf2ef7 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Wed, 28 Feb 2024 12:42:14 -0400 Subject: [PATCH 08/17] Fix chart temporary save location --- Src/Private/SharedUtilsFunctions.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Src/Private/SharedUtilsFunctions.ps1 b/Src/Private/SharedUtilsFunctions.ps1 index 662ed8f..7552642 100644 --- a/Src/Private/SharedUtilsFunctions.ps1 +++ b/Src/Private/SharedUtilsFunctions.ps1 @@ -335,7 +335,9 @@ function Get-PieChart { } Add-ChartTitle @addChartTitleParams - $ChartImage = Export-Chart -Chart $exampleChart -Path (Get-Location).Path -Format "PNG" -PassThru + $TempPath = Resolve-Path ([System.IO.Path]::GetTempPath()) + + $ChartImage = Export-Chart -Chart $exampleChart -Path $TempPath.Path -Format "PNG" -PassThru $Base64Image = [convert]::ToBase64String((Get-Content $ChartImage -Encoding byte)) @@ -419,7 +421,9 @@ function Get-ColumnChart { } Add-ChartTitle @addChartTitleParams - $ChartImage = Export-Chart -Chart $exampleChart -Path (Get-Location).Path -Format "PNG" -PassThru + $TempPath = Resolve-Path ([System.IO.Path]::GetTempPath()) + + $ChartImage = Export-Chart -Chart $exampleChart -Path $TempPath.Path -Format "PNG" -PassThru if ($PassThru) { Write-Output -InputObject $chartFileItem From 777ff6d8a72667786c9b956f0c7b1d00c856ad14 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Mon, 11 Mar 2024 14:21:52 -0400 Subject: [PATCH 09/17] Error running report when license don't support Protection Group #146 --- Src/Private/Get-AbrVbrInventorySummary.ps1 | 28 +++++++++++-------- Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 | 20 +++++++------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/Src/Private/Get-AbrVbrInventorySummary.ps1 b/Src/Private/Get-AbrVbrInventorySummary.ps1 index 05408de..8ce9b89 100644 --- a/Src/Private/Get-AbrVbrInventorySummary.ps1 +++ b/Src/Private/Get-AbrVbrInventorySummary.ps1 @@ -32,7 +32,11 @@ function Get-AbrVbrInventorySummary { $ESXi = Get-VBRServer | Where-Object { $_.Type -eq 'ESXi' } $HvCluster = Get-VBRServer | Where-Object { $_.Type -eq 'HvCluster' } $HvServer = Get-VBRServer | Where-Object { $_.Type -eq 'HvServer' } - $ProtectionGroups = Get-VBRProtectionGroup + $ProtectionGroups = try { + Get-VBRProtectionGroup | Sort-Object -Property Name + } catch { + Write-PScriboMessage -IsWarning "Physical Infrastructure Inventory Summary Cmdlet: $($_.Exception.Message)" + } if ($VbrVersion -lt 12.1) { $Shares = Get-VBRNASServer -WarningAction SilentlyContinue } else { @@ -42,25 +46,25 @@ function Get-AbrVbrInventorySummary { $ObjectStorage = Get-VBRUnstructuredServer | Where-Object { $_.Type -eq "AzureBlobServer" -or $_.Type -eq "AmazonS3Server" -or $_.Type -eq "S3CompatibleServer" } } $inObj = [ordered] @{ - 'vCenter Servers' = $vCenter.Count - 'ESXi Servers' = $ESXi.Count - 'Hyper-V Clusters' = $HvCluster.Count - 'Hyper-V Servers' = $HvServer.Count - 'Protection Groups' = $ProtectionGroups.Count + 'vCenter Servers' = ($vCenter | Measure-Object).Count + 'ESXi Servers' = ($ESXi | Measure-Object).Count + 'Hyper-V Clusters' = ($HvCluster | Measure-Object).Count + 'Hyper-V Servers' = ($HvServer | Measure-Object).Count + 'Protection Groups' = ($ProtectionGroups | Measure-Object).Count } if ($VbrVersion -lt 12.1) { - $inObj.add('File Shares', $Shares.Count) + $inObj.add('File Shares', ($Shares | Measure-Object).Count) } else { - $inObj.add('File Server', $FileServers.Count) - $inObj.add('NAS Fillers', $NASFillers.Count) - $inObj.add('File Shares', $FileShares.Count) - $inObj.add('Object Storage', $ObjectStorage.Count) + $inObj.add('File Server', ($FileServers | Measure-Object).Count) + $inObj.add('NAS Fillers', ($NASFillers | Measure-Object).Count) + $inObj.add('File Shares', ($FileShares | Measure-Object).Count) + $inObj.add('Object Storage', ($ObjectStorage | Measure-Object).Count) } $OutObj += [pscustomobject]$inobj } catch { - Write-PScriboMessage -IsWarning "Inventory Summary Section: $($_.Exception.Message)" + Write-PScriboMessage -IsWarning "Inventory Summary Table: $($_.Exception.Message)" } $TableParams = @{ diff --git a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 index 2da86fc..8ddcea6 100644 --- a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 +++ b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 @@ -21,21 +21,21 @@ function Invoke-AsBuiltReport.Veeam.VBR { [PSCredential] $Credential ) - Write-PScriboMessage -IsWarning "Please refer to the AsBuiltReport.Veeam.VBR github website for more detailed information about this project." - Write-PScriboMessage -IsWarning "Do not forget to update your report configuration file after each new version release." - Write-PScriboMessage -IsWarning "Documentation: https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR" - Write-PScriboMessage -IsWarning "Issues or bug reporting: https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues" + Write-PScriboMessage -Plugin "Module" -IsWarning "Please refer to the AsBuiltReport.Veeam.VBR github website for more detailed information about this project." + Write-PScriboMessage -Plugin "Module" -IsWarning "Do not forget to update your report configuration file after each new version release." + Write-PScriboMessage -Plugin "Module" -IsWarning "Documentation: https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR" + Write-PScriboMessage -Plugin "Module" -IsWarning "Issues or bug reporting: https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues" # Check the current AsBuiltReport.Veeam.VBR module Try { $InstalledVersion = Get-Module -ListAvailable -Name AsBuiltReport.Veeam.VBR -ErrorAction SilentlyContinue | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version if ($InstalledVersion) { - Write-PScriboMessage -IsWarning "AsBuiltReport.Veeam.VBR $($InstalledVersion.ToString()) is currently installed." + Write-PScriboMessage -Plugin "Module" -IsWarning "AsBuiltReport.Veeam.VBR $($InstalledVersion.ToString()) is currently installed." $LatestVersion = Find-Module -Name AsBuiltReport.Veeam.VBR -Repository PSGallery -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Version if ($LatestVersion -gt $InstalledVersion) { - Write-PScriboMessage -IsWarning "AsBuiltReport.Veeam.VBR $($LatestVersion.ToString()) is available." - Write-PScriboMessage -IsWarning "Run 'Update-Module -Name AsBuiltReport.Veeam.VBR -Force' to install the latest version." + Write-PScriboMessage -Plugin "Module" -IsWarning "AsBuiltReport.Veeam.VBR $($LatestVersion.ToString()) is available." + Write-PScriboMessage -Plugin "Module" -IsWarning "Run 'Update-Module -Name AsBuiltReport.Veeam.VBR -Force' to install the latest version." } } } Catch { @@ -253,7 +253,11 @@ function Invoke-AsBuiltReport.Veeam.VBR { } Write-PScriboMessage "Physical Inventory InfoLevel set at $($InfoLevel.Inventory.PHY)." if ($InfoLevel.Inventory.PHY -ge 1) { - $InventObjs = Get-VBRProtectionGroup | Sort-Object -Property Name + $InventObjs = try { + Get-VBRProtectionGroup | Sort-Object -Property Name + } catch { + Write-PScriboMessage -IsWarning "Physical Infrastructure Summary Cmdlet Section: $($_.Exception.Message)" + } Get-AbrVbrPhysicalInfrastructure From 72a3b56b3c67b0e90cae5590efa20360c9c8c0be Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Mon, 11 Mar 2024 14:26:04 -0400 Subject: [PATCH 10/17] Fix CodeQL Alert #6 --- Src/Private/Get-AbrVbrSureBackupjob.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/Private/Get-AbrVbrSureBackupjob.ps1 b/Src/Private/Get-AbrVbrSureBackupjob.ps1 index e4b869f..a84624e 100644 --- a/Src/Private/Get-AbrVbrSureBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrSureBackupjob.ps1 @@ -47,9 +47,9 @@ function Get-AbrVbrSureBackupjob { } 'Latest Result' = $SBkjob.LastResult 'Virtual Lab' = Switch ($SBkjob.VirtualLab.Name) { - $true {"Not applicable​​​​"} - $false {$SBkjob.VirtualLab.Name} - default {"--"} + $true { "Not applicable" } + $false { $SBkjob.VirtualLab.Name } + default { "--" } } } $OutObj += [pscustomobject]$inobj From 078fd207bb7eed9a298b47930cdb60d7a627efcd Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Tue, 12 Mar 2024 22:54:07 -0400 Subject: [PATCH 11/17] Fix for chart title --- Src/Private/Get-AbrVbrBackupRepository.ps1 | 2 +- Src/Private/Get-AbrVbrCloudConnectSummary.ps1 | 2 +- .../Get-AbrVbrInfrastructureSummary.ps1 | 2 +- Src/Private/Get-AbrVbrInstalledLicense.ps1 | 6 +- Src/Private/Get-AbrVbrInventorySummary.ps1 | 2 +- Src/Private/Get-AbrVbrReplInfraSummary.ps1 | 2 +- Src/Private/Get-AbrVbrStorageInfraSummary.ps1 | 2 +- Src/Private/Get-AbrVbrTapeInfraSummary.ps1 | 2 +- Src/Private/SharedUtilsFunctions.ps1 | 64 +++++++++++++++++++ 9 files changed, 74 insertions(+), 10 deletions(-) diff --git a/Src/Private/Get-AbrVbrBackupRepository.ps1 b/Src/Private/Get-AbrVbrBackupRepository.ps1 index da58478..ca006de 100644 --- a/Src/Private/Get-AbrVbrBackupRepository.ps1 +++ b/Src/Private/Get-AbrVbrBackupRepository.ps1 @@ -90,7 +90,7 @@ function Get-AbrVbrBackupRepository { Paragraph "The following section provides Backup Repository summary information." BlankLine if ($Options.EnableCharts -and $chartFileItem) { - Image -Text 'Backup Repository - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Backup Repository - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Sort-Object -Property 'Name' | Table @TableParams diff --git a/Src/Private/Get-AbrVbrCloudConnectSummary.ps1 b/Src/Private/Get-AbrVbrCloudConnectSummary.ps1 index 4c14349..2c1f740 100644 --- a/Src/Private/Get-AbrVbrCloudConnectSummary.ps1 +++ b/Src/Private/Get-AbrVbrCloudConnectSummary.ps1 @@ -68,7 +68,7 @@ function Get-AbrVbrCloudConnectSummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Cloud Connect Infrastructure' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Cloud Connect Infrastructure - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Cloud Connect Infrastructure - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/Get-AbrVbrInfrastructureSummary.ps1 b/Src/Private/Get-AbrVbrInfrastructureSummary.ps1 index befdedf..2ab2c2a 100644 --- a/Src/Private/Get-AbrVbrInfrastructureSummary.ps1 +++ b/Src/Private/Get-AbrVbrInfrastructureSummary.ps1 @@ -96,7 +96,7 @@ function Get-AbrVbrInfrastructureSummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Backup Infrastructure Inventory' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Backup Infrastructure - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Backup Infrastructure - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/Get-AbrVbrInstalledLicense.ps1 b/Src/Private/Get-AbrVbrInstalledLicense.ps1 index 2624ea4..c44cfa1 100644 --- a/Src/Private/Get-AbrVbrInstalledLicense.ps1 +++ b/Src/Private/Get-AbrVbrInstalledLicense.ps1 @@ -118,7 +118,7 @@ function Get-AbrVbrInstalledLicense { if ($OutObj) { Section -Style NOTOCHeading5 -ExcludeFromTOC 'Instance License Usage' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Instance License Usage - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Instance License Usage - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams @@ -206,7 +206,7 @@ function Get-AbrVbrInstalledLicense { if ($OutObj) { Section -Style NOTOCHeading5 -ExcludeFromTOC 'CPU Socket License Usage' { if ($chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'CPU Socket License Usage - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'CPU Socket License Usage - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } $OutObj | Table @TableParams } @@ -258,7 +258,7 @@ function Get-AbrVbrInstalledLicense { if ($OutObj) { Section -Style NOTOCHeading5 -ExcludeFromTOC 'Capacity License Usage' { if ($chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Capacity License Usage - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Capacity License Usage - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } $OutObj | Table @TableParams } diff --git a/Src/Private/Get-AbrVbrInventorySummary.ps1 b/Src/Private/Get-AbrVbrInventorySummary.ps1 index 8ce9b89..c67773d 100644 --- a/Src/Private/Get-AbrVbrInventorySummary.ps1 +++ b/Src/Private/Get-AbrVbrInventorySummary.ps1 @@ -88,7 +88,7 @@ function Get-AbrVbrInventorySummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Inventory' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Inventory - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Inventory - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/Get-AbrVbrReplInfraSummary.ps1 b/Src/Private/Get-AbrVbrReplInfraSummary.ps1 index 8cb44e8..e446a5a 100644 --- a/Src/Private/Get-AbrVbrReplInfraSummary.ps1 +++ b/Src/Private/Get-AbrVbrReplInfraSummary.ps1 @@ -56,7 +56,7 @@ function Get-AbrVbrReplInfraSummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Replication Inventory' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Replication Inventory - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Replication Inventory - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/Get-AbrVbrStorageInfraSummary.ps1 b/Src/Private/Get-AbrVbrStorageInfraSummary.ps1 index 8ab8e98..00e3398 100644 --- a/Src/Private/Get-AbrVbrStorageInfraSummary.ps1 +++ b/Src/Private/Get-AbrVbrStorageInfraSummary.ps1 @@ -63,7 +63,7 @@ function Get-AbrVbrStorageInfraSummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Storage Infrastructure Inventory' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Storage Infrastructure Inventory - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Storage Infrastructure Inventory - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/Get-AbrVbrTapeInfraSummary.ps1 b/Src/Private/Get-AbrVbrTapeInfraSummary.ps1 index d94ddc3..4c333bb 100644 --- a/Src/Private/Get-AbrVbrTapeInfraSummary.ps1 +++ b/Src/Private/Get-AbrVbrTapeInfraSummary.ps1 @@ -68,7 +68,7 @@ function Get-AbrVbrTapeInfraSummary { if ($OutObj) { Section -Style NOTOCHeading3 -ExcludeFromTOC 'Tape Infrastructure' { if ($Options.EnableCharts -and $chartFileItem -and ($inObj.Values | Measure-Object -Sum).Sum -ne 0) { - Image -Text 'Tape Infrastructure - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Tape Infrastructure - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } BlankLine $OutObj | Table @TableParams diff --git a/Src/Private/SharedUtilsFunctions.ps1 b/Src/Private/SharedUtilsFunctions.ps1 index 7552642..189a575 100644 --- a/Src/Private/SharedUtilsFunctions.ps1 +++ b/Src/Private/SharedUtilsFunctions.ps1 @@ -505,3 +505,67 @@ function Get-WindowsTimePeriod { return $OutObj } # end + +function Get-TimeDuration { + <# + .SYNOPSIS + Used by As Built Report to convert job session Duration time to TimeFormat. + .DESCRIPTION + .NOTES + Version: 0.1.0 + Author: Jonathan Colon + .EXAMPLE + Get-TimeDuration -$JobTimeSpan + .LINK + #> + + [CmdletBinding()] + Param + ( + [Parameter ( + Position = 0, + Mandatory + )] + [TimeSpan] $JobTimeSpan + ) + + if ($JobTimeSpan.Days -gt 0) { + $JobTimeSpan.ToString("dd\.hh\:mm\:ss") + } else { + $JobTimeSpan.ToString("hh\:mm\:ss") + } +} + +function Get-AvgTimeDuration { + <# + .SYNOPSIS + Used by As Built Report to convert jobs session Duration time to AVG TimeFormat. + .DESCRIPTION + .NOTES + Version: 0.1.0 + Author: Jonathan Colon + .EXAMPLE + Get-TimeDuration -$JobTimeSpan + .LINK + #> + + [CmdletBinding()] + Param + ( + [Parameter ( + Position = 0, + Mandatory + )] + $JobSessions + ) + + $TimeDurationObj = @() + foreach ($JobSession in $JobSessions) { + $TimeDurationObj += New-TimeSpan -Start $JobSession.CreationTime -End $JobSession.EndTime + } + + # Calculate AVG TimeDuration of job sessions + $AverageTimeSpan = New-TimeSpan -Seconds (($TimeDurationObj.TotalSeconds | Measure-Object -Sum).Sum / $JobSessions.Count) + + return (Get-TimeDuration -JobTimeSpan $AverageTimeSpan) +} \ No newline at end of file From 1d31267dee52b0552f1f02cfeaaa8c629b083897 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Tue, 12 Mar 2024 22:54:19 -0400 Subject: [PATCH 12/17] Add Backup Time to the Jobs Summary #144 --- Src/Private/Get-AbrVbrBackupjob.ps1 | 77 +++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/Src/Private/Get-AbrVbrBackupjob.ps1 b/Src/Private/Get-AbrVbrBackupjob.ps1 index d53646b..3e6aaa8 100644 --- a/Src/Private/Get-AbrVbrBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrBackupjob.ps1 @@ -26,11 +26,11 @@ function Get-AbrVbrBackupjob { process { try { - if (($Bkjobs = Get-VBRJob -WarningAction SilentlyContinue | Where-Object { $_.TypeToString -ne 'Windows Agent Backup' -and $_.TypeToString -ne 'Hyper-V Replication' -and $_.TypeToString -ne 'VMware Replication' } | Sort-Object -Property Name).count -gt 0) { + $Bkjobs = Get-VBRJob -WarningAction SilentlyContinue | Where-Object { $_.TypeToString -ne 'Windows Agent Backup' -and $_.TypeToString -ne 'Hyper-V Replication' -and $_.TypeToString -ne 'VMware Replication' } | Sort-Object -Property Name + if ($Bkjobs) { $OutObj = @() foreach ($Bkjob in $Bkjobs) { try { - Write-PScriboMessage "Discovered $($Bkjob.Name) backup job." $inObj = [ordered] @{ 'Name' = $Bkjob.Name 'Type' = $Bkjob.TypeToString @@ -45,17 +45,17 @@ function Get-AbrVbrBackupjob { default { 'Unknown' } } } - $OutObj += [pscustomobject]$inobj + $OutObj += [pscustomobject]$inObj } catch { Write-PScriboMessage -IsWarning "Backup Jobs Section: $($_.Exception.Message)" } } if ($HealthCheck.Jobs.Status) { - $OutObj | Where-Object { $_.'Latest Result' -eq 'Failed' } | Set-Style -Style Critical -Property 'Latest Result' - $OutObj | Where-Object { $_.'Latest Result' -eq 'Warning' } | Set-Style -Style Warning -Property 'Latest Result' - $OutObj | Where-Object { $_.'Status' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Status' - $OutObj | Where-Object { $_.'Scheduled?' -eq 'No' } | Set-Style -Style Warning -Property 'Scheduled?' + $BackupJobsStatus | Where-Object { $_.'Latest Result' -eq 'Failed' } | Set-Style -Style Critical -Property 'Latest Result' + $BackupJobsStatus | Where-Object { $_.'Latest Result' -eq 'Warning' } | Set-Style -Style Warning -Property 'Latest Result' + $BackupJobsStatus | Where-Object { $_.'Status' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Status' + $BackupJobsStatus | Where-Object { $_.'Scheduled?' -eq 'No' } | Set-Style -Style Warning -Property 'Scheduled?' } $TableParams = @{ @@ -94,12 +94,71 @@ function Get-AbrVbrBackupjob { } if ($OutObj) { if ($chartFileItem) { - Image -Text 'Backup Repository - Diagram' -Align 'Center' -Percent 100 -Base64 $chartFileItem + Image -Text 'Backup Repository - Chart' -Align 'Center' -Percent 100 -Base64 $chartFileItem } Section -Style Heading3 'Backup Jobs' { Paragraph "The following section list backup jobs created in Veeam Backup & Replication." BlankLine - $OutObj | Sort-Object -Property Name | Table @TableParams + Section -ExcludeFromTOC -Style NOTOCHeading4 'Backup Job Status' { + $OutObj | Sort-Object -Property Name | Table @TableParams + } + if ($Bkjobs) { + try { + Section -ExcludeFromTOC -Style NOTOCHeading4 'Backup Jobs Duration' { + $OutObj = @() + foreach ($Bkjob in $Bkjobs) { + try { + $BKJobSession = Get-VBRSession -Job $Bkjob | Select-Object -First 10 + $Duration = $Null + if ($BKJobSession) { + try { + $Duration = Get-AvgTimeDuration -JobSessions $BKJobSession + } catch { + Out-Null + } + } + $inObj = [ordered] @{ + 'Name' = $Bkjob.Name + 'Last Backup Start Time' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { + $true { '--' } + $false { $BKJobSession[0].CreationTime } + } + 'Last Backup End Time' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { + $true { '--' } + $false { $BKJobSession[0].EndTime } + } + 'Last Backup Duration' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { + $true { '--' } + $false { Get-TimeDuration -JobTimeSpan (New-TimeSpan -Start $BKJobSession[0].CreationTime -End $BKJobSession[0].EndTime) } + } + 'Last 10 Backup AVG Duration' = Switch ([string]::IsNullOrEmpty($Duration)) { + $true { '--' } + $false { $Duration } + default { 'Unknown' } + } + # '% Deviation' = 0 + } + $OutObj += [pscustomobject]$inObj + } catch { + Write-PScriboMessage -IsWarning "Backup Jobs $($Bkjob.Name) Time Table: $($_.Exception.Message)" + } + } + + $TableParams = @{ + Name = "Backup Jobs Time - $VeeamBackupServer" + List = $false + ColumnWidths = 36, 16, 16, 16, 16 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + + $OutObj | Sort-Object -Property Name | Table @TableParams + } + } catch { + Write-PScriboMessage -IsWarning "Backup Jobs Time Section: $($_.Exception.Message)" + } + } } } } From 7137f68b12657878f5594aec70cd2add2381884c Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Thu, 14 Mar 2024 00:21:52 -0400 Subject: [PATCH 13/17] Add a section about Restore Points #143 --- AsBuiltreport.Veeam.VBR.json | 43 +++--- CHANGELOG.md | 5 +- Src/Private/Get-AbrVbrBackupJobsRP.ps1 | 77 +++++++++++ Src/Private/Get-AbrVbrBackupjob.ps1 | 24 ++-- Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 | 77 +++++++++++ Src/Private/SharedUtilsFunctions.ps1 | 123 +++++++++++++++++- Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 | 16 +++ 7 files changed, 331 insertions(+), 34 deletions(-) create mode 100644 Src/Private/Get-AbrVbrBackupJobsRP.ps1 create mode 100644 Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 diff --git a/AsBuiltreport.Veeam.VBR.json b/AsBuiltreport.Veeam.VBR.json index e774fe3..95e815e 100644 --- a/AsBuiltreport.Veeam.VBR.json +++ b/AsBuiltreport.Veeam.VBR.json @@ -10,63 +10,64 @@ }, "Options": { "BackupServerPort": 9392, - "PSDefaultAuthentication": "Default", "EnableCharts": false, "EnableHardwareInventory": false, - "EnableDiagrams": false + "EnableDiagrams": false, + "PSDefaultAuthentication": "Default" }, "InfoLevel": { "_comment_": "Please refer to the AsBuiltReport project contributing guide for information about how to define InfoLevels.", "_comment_": "0 = Disabled, 1 = Enabled, 2 = Adv Summary, 3 = Detailed", "Infrastructure": { "BackupServer": 1, - "Proxy": 1, - "Settings": 1, "BR": 1, "Licenses": 1, + "Proxy": 1, + "Settings": 1, "SOBR": 1, - "WANAccel": 1, "ServiceProvider": 1, - "SureBackup": 1 + "SureBackup": 1, + "WANAccel": 1 }, "Tape": { - "Server": 1, "Library": 1, "MediaPool": 1, - "Vault": 1, - "NDMP": 1 + "NDMP": 1, + "Server": 1, + "Vault": 1 }, "Inventory": { - "VI": 1, + "FileShare": 1, "PHY": 1, - "FileShare": 1 + "VI": 1 }, "Storage": { - "ONTAP": 1, - "ISILON": 1 + "ISILON": 1, + "ONTAP": 1 }, "Replication": { "FailoverPlan": 1, "Replica": 1 }, "CloudConnect": { + "BackupStorage": 1, "Certificate": 1, - "PublicIP": 1, "CloudGateway": 1, "GatewayPools": 1, - "Tenants": 1, - "BackupStorage": 1, - "ReplicaResources": 1 + "PublicIP": 1, + "ReplicaResources": 1, + "Tenants": 1 }, "Jobs": { + "Agent": 1, "Backup": 1, "BackupCopy": 1, - "Tape": 1, - "Surebackup": 1, - "Agent": 1, "FileShare": 1, - "Replication": 1 + "Surebackup": 1, + "Replication": 1, + "Restores": 1, + "Tape": 1 } }, "HealthCheck": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 505b676..04dda73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.8.5] - 2024-01-30 +## [0.8.5] - 2024-03-15 ### Added @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Auto logoff on inactivity setting - Added Four-eye Authorization setting - Added HealthCheck conditions +- Added Backup Time Duration table to the Jobs Summary. Close [#144](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/144) +- Added Restore point reporting. Close [#143](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/143) ### Changed @@ -28,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed issue with the Veeam.Diagrammer module. - Resolved issue that prevented SureBackup Virtual Lab information to be collected. Fix [#142](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/142) - Resolved issue in the Malware Global Exclusions section. Fix [#145](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/145) +- Resolved an issue related to Protection Group licensing. Fix [#146](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/146) ## [0.8.4] - 2024-01-16 diff --git a/Src/Private/Get-AbrVbrBackupJobsRP.ps1 b/Src/Private/Get-AbrVbrBackupJobsRP.ps1 new file mode 100644 index 0000000..ff664ad --- /dev/null +++ b/Src/Private/Get-AbrVbrBackupJobsRP.ps1 @@ -0,0 +1,77 @@ +function Get-AbrVbrBackupJobsRP { + <# + .SYNOPSIS + Used by As Built Report to retrieve Veeam VBR Restore Point + .DESCRIPTION + Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo. + .NOTES + Version: 0.8.5 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + Credits: Iain Brighton (@iainbrighton) - PScribo module + + .LINK + https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR + #> + [CmdletBinding()] + param ( + ) + + begin { + Write-PScriboMessage "RestorePoint InfoLevel set at $($InfoLevel.Restore.RestorePoint)." + } + + process { + try { + $BackupJobs = Get-VBRBackup | Sort-Object -Property Name + if ($BackupJobs) { + Write-PScriboMessage "Collecting Veeam VBR Restore Point." + Section -Style Heading3 'Backup Jobs' { + Paragraph "The following section summarizes the backup jobs restore points." + BlankLine + foreach ($BackupJob in $BackupJobs) { + $BackupJobRestorePoints = Get-VBRRestorePoint -Backup $BackupJob | Sort-Object -Property VMName, CreationTimeUt, Type + if ($BackupJobRestorePoints) { + Section -Style Heading4 $BackupJob.Name { + $RestorePointInfo = @() + foreach ($RestorePoint in $BackupJobRestorePoints) { + try { + $DedupRatio = $RestorePoint.GetStorage().stats.DedupRatio + $CompressRatio = $RestorePoint.GetStorage().stats.CompressRatio + if ($DedupRatio -gt 1) { $DedupRatio = 100 / $DedupRatio } else { $DedupRatio = 1 } + if ($CompressRatio -gt 1) { $CompressRatio = 100 / $CompressRatio } else { $CompressRatio = 1 } + $inObj = [ordered] @{ + 'VM Name' = $RestorePoint.VMName + 'Backup Type' = $RestorePoint.Algorithm + 'Backup Size' = (ConvertTo-FileSizeString -Size $RestorePoint.GetStorage().stats.BackupSize) + 'Dedub Ratio' = [Math]::Round($DedupRatio, 2) + 'Compress Ratio' = [Math]::Round($CompressRatio, 2) + 'Reduction' = [Math]::Round(($DedupRatio * $CompressRatio), 2) + } + $RestorePointInfo += [PSCustomObject]$InObj + } catch { + Write-PScriboMessage -IsWarning "Restore Point table: $($_.Exception.Message)" + } + } + + $TableParams = @{ + Name = "Restore Points - $($BackupJob.Name)" + List = $false + ColumnWidths = 20, 16, 16, 16, 16, 16 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $RestorePointInfo | Table @TableParams + } + } + } + } + } + } catch { + Write-PScriboMessage -IsWarning "Restore Point Section: $($_.Exception.Message)" + } + } + end {} +} \ No newline at end of file diff --git a/Src/Private/Get-AbrVbrBackupjob.ps1 b/Src/Private/Get-AbrVbrBackupjob.ps1 index 3e6aaa8..354f6f9 100644 --- a/Src/Private/Get-AbrVbrBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrBackupjob.ps1 @@ -110,6 +110,7 @@ function Get-AbrVbrBackupjob { try { $BKJobSession = Get-VBRSession -Job $Bkjob | Select-Object -First 10 $Duration = $Null + $StandardDeviation = $Null if ($BKJobSession) { try { $Duration = Get-AvgTimeDuration -JobSessions $BKJobSession @@ -117,16 +118,15 @@ function Get-AbrVbrBackupjob { Out-Null } } + if ($BKJobSession) { + try { + $StandardDeviation = (Get-StrdDevDuration -JobSessions $BKJobSession).StandardDeviation + } catch { + Out-Null + } + } $inObj = [ordered] @{ 'Name' = $Bkjob.Name - 'Last Backup Start Time' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { - $true { '--' } - $false { $BKJobSession[0].CreationTime } - } - 'Last Backup End Time' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { - $true { '--' } - $false { $BKJobSession[0].EndTime } - } 'Last Backup Duration' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { $true { '--' } $false { Get-TimeDuration -JobTimeSpan (New-TimeSpan -Start $BKJobSession[0].CreationTime -End $BKJobSession[0].EndTime) } @@ -136,7 +136,11 @@ function Get-AbrVbrBackupjob { $false { $Duration } default { 'Unknown' } } - # '% Deviation' = 0 + 'Standard Deviation' = Switch ([string]::IsNullOrEmpty($StandardDeviation)) { + $true { '--' } + $false { $StandardDeviation } + default { 'Unknown' } + } } $OutObj += [pscustomobject]$inObj } catch { @@ -147,7 +151,7 @@ function Get-AbrVbrBackupjob { $TableParams = @{ Name = "Backup Jobs Time - $VeeamBackupServer" List = $false - ColumnWidths = 36, 16, 16, 16, 16 + ColumnWidths = 40, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" diff --git a/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 b/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 new file mode 100644 index 0000000..8e43f2c --- /dev/null +++ b/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 @@ -0,0 +1,77 @@ +function Get-AbrVbrTapeBackupJobsRP { + <# + .SYNOPSIS + Used by As Built Report to retrieve Veeam VBR Tape Backup Job Restore Point + .DESCRIPTION + Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo. + .NOTES + Version: 0.8.5 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + Credits: Iain Brighton (@iainbrighton) - PScribo module + + .LINK + https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR + #> + [CmdletBinding()] + param ( + ) + + begin { + Write-PScriboMessage "RestorePoint InfoLevel set at $($InfoLevel.Restore.RestorePoint)." + } + + process { + try { + $BackupJobs = Get-VBRTapeBackup -WarningAction SilentlyContinue | Sort-Object -Property Name + if ($BackupJobs) { + Write-PScriboMessage "Collecting Veeam VBR Tape Restore Point." + Section -Style Heading3 'Tape Backup Jobs ' { + Paragraph "The following section summarizes the tape backup jobs restore points." + BlankLine + foreach ($BackupJob in $BackupJobs) { + $BackupJobRestorePoints = Get-VBRRestorePoint -Backup $BackupJob | Sort-Object -Property VMName, CreationTimeUt, Type + if ($BackupJobRestorePoints) { + Section -Style Heading4 $BackupJob.Name { + $RestorePointInfo = @() + foreach ($RestorePoint in $BackupJobRestorePoints) { + try { + $DedupRatio = $RestorePoint.GetStorage().stats.DedupRatio + $CompressRatio = $RestorePoint.GetStorage().stats.CompressRatio + if ($DedupRatio -gt 1) { $DedupRatio = 100 / $DedupRatio } else { $DedupRatio = 1 } + if ($CompressRatio -gt 1) { $CompressRatio = 100 / $CompressRatio } else { $CompressRatio = 1 } + $inObj = [ordered] @{ + 'VM Name' = $RestorePoint.VMName + 'Backup Type' = $RestorePoint.Algorithm + 'Backup Size' = (ConvertTo-FileSizeString -Size $RestorePoint.GetStorage().stats.BackupSize) + 'Dedub Ratio' = [Math]::Round($DedupRatio, 2) + 'Compress Ratio' = [Math]::Round($CompressRatio, 2) + 'Reduction' = [Math]::Round(($DedupRatio * $CompressRatio), 2) + } + $RestorePointInfo += [PSCustomObject]$InObj + } catch { + Write-PScriboMessage -IsWarning "Tape Restore Point table: $($_.Exception.Message)" + } + } + + $TableParams = @{ + Name = "Tape Restore Points - $($BackupJob.Name)" + List = $false + ColumnWidths = 20, 16, 16, 16, 16, 16 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $RestorePointInfo | Table @TableParams + } + } + } + } + } + } catch { + Write-PScriboMessage -IsWarning "Tape Restore Point Section: $($_.Exception.Message)" + } + } + end {} +} \ No newline at end of file diff --git a/Src/Private/SharedUtilsFunctions.ps1 b/Src/Private/SharedUtilsFunctions.ps1 index 189a575..d2c4326 100644 --- a/Src/Private/SharedUtilsFunctions.ps1 +++ b/Src/Private/SharedUtilsFunctions.ps1 @@ -565,7 +565,126 @@ function Get-AvgTimeDuration { } # Calculate AVG TimeDuration of job sessions - $AverageTimeSpan = New-TimeSpan -Seconds (($TimeDurationObj.TotalSeconds | Measure-Object -Sum).Sum / $JobSessions.Count) + $AverageTimeSpan = New-TimeSpan -Seconds (($TimeDurationObj.TotalSeconds | Measure-Object -Average).Average) return (Get-TimeDuration -JobTimeSpan $AverageTimeSpan) -} \ No newline at end of file +} + +function Get-StrdDevDuration { + <# + .SYNOPSIS + Used by As Built Report to convert jobs session Duration time to Standard Deviation TimeFormat. + .DESCRIPTION + .NOTES + Version: 0.1.0 + Author: Jonathan Colon + .EXAMPLE + Get-StrdDevDuration -$JobTimeSpan + .LINK + #> + + [CmdletBinding()] + Param + ( + [Parameter ( + Position = 0, + Mandatory + )] + $JobSessions + ) + + $TimeDurationObj = @() + foreach ($JobSession in $JobSessions) { + $TimeDurationObj += (New-TimeSpan -Start $JobSession.CreationTime -End $JobSession.EndTime).TotalSeconds + } + + # Calculate AVG TimeDuration of job sessions + $StrdDevDuration = Get-StandardDeviation -value $TimeDurationObj + + return $StrdDevDuration +} + + +function Get-StandardDeviation { + <# + .Synopsis + This script will find the standard deviation, given a set of numbers. + .DESCRIPTION + This script will find the standard deviation, given a set of numbers. + + Written by Mike Roberts (Ginger Ninja) + Version: 0.5 + .EXAMPLE + .\Get-StandardDeviation.ps1 + + Using this method you will need to input numbers one line at a time, and then hit enter twice when done. + -------------------------------------------------------------------------------------------------------- + PS > .\Get-StandardDeviation.ps1 + + cmdlet Get-StandardDeviation at command pipeline position 1 + Supply values for the following parameters: + value[0]: 12345 + value[1]: 0 + value[2]: + + + Original Numbers : 12345,0 + Standard Deviation : 8729.23321374793 + Rounded Number (2 decimal) : 8729.23 + Rounded Number (3 decimal) : 8729.233 + -------------------------------------------------------------------------------------------------------- + .EXAMPLE + .\Get-StandardDeviation.ps1 -value 12345,0 + .LINK + http://www.gngrninja.com/script-ninja/2016/5/1/powershell-calculating-standard-deviation + .NOTES + Be sure to enter at least 2 numbers, separated by a comma if using the -value parameter. + #> + #Begin function Get-StandardDeviation + [cmdletbinding()] + param( + [Parameter(Mandatory = $true)] + [decimal[]] $value + ) + + #Simple if to see if the value matches digits, and also that there is more than one number. + if ($value -match '\d+' -and $value.Count -gt 1) { + + #Variables used later + [decimal]$newNumbers = $Null + [decimal]$stdDev = $null + + #Get the average and count via Measure-Object + $avgCount = $value | Measure-Object -Average | Select-Object Average, Count + + #Iterate through each of the numbers and get part of the variance via some PowerShell math. + ForEach ($number in $value) { + + $newNumbers += [Math]::Pow(($number - $avgCount.Average), 2) + + } + + #Finish the variance calculation, and get the square root to finally get the standard deviation. + $stdDev = [math]::Sqrt($($newNumbers / ($avgCount.Count - 1))) + + #Create an array so we can add the object we create to it. This is incase we want to perhaps add some more math functions later. + [System.Collections.ArrayList]$formattedObjectArray = @() + + #Create a hashtable collection for the properties of the object + $formattedProperty = @{'StandardDeviation' = [Math]::Round($stdDev, 2) } + + #Create the object we'll add to the array, with the properties set above + $fpO = New-Object psobject -Property $formattedProperty + + #Add that object to this array + $formattedObjectArray.Add($fpO) | Out-Null + + #Return the array object with the selected objects defined, as well as formatting. + Return $formattedObjectArray + + } else { + + #Display an error if there are not enough numbers + Write-PScriboMessage "You did not enter enough numbers!" + } +} #End function Get-StandardDeviation \ No newline at end of file diff --git a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 index 8ddcea6..e2387b6 100644 --- a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 +++ b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 @@ -423,6 +423,22 @@ function Invoke-AsBuiltReport.Veeam.VBR { } } } + + #---------------------------------------------------------------------------------------------# + # Backup Jobs Restore Points Section # + #---------------------------------------------------------------------------------------------# + if ($InfoLevel.Jobs.PSObject.Properties.Value -ne 0) { + if (((Get-VBRJob -WarningAction SilentlyContinue).count -gt 0) -or ((Get-VBRTapeJob).count -gt 0) -or ((Get-VBRSureBackupJob).count -gt 0)) { + Section -Style Heading2 'Restores Points' { + Paragraph "The following section provides information about the jobs restore points in Veeam Server: $(((Get-VBRServerSession).Server))." + BlankLine + if ($InfoLevel.Jobs.Restores -gt 0) { + Get-AbrVbrBackupJobsRP + Get-AbrVbrTapeBackupJobsRP + } + } + } + } } if ((Get-VBRServerSession).Server) { Write-PScriboMessage "Disconecting section from $((Get-VBRServerSession).Server)" From 7a502dd43bad48bd1d523b19634fe360b004f4a1 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Fri, 15 Mar 2024 12:14:21 -0400 Subject: [PATCH 14/17] Added Automatically disable Windows Firewall option to the SureBackup verification option section --- CHANGELOG.md | 1 + Src/Private/Get-AbrVbrSureBackupjobconf.ps1 | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04dda73..5645b0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added HealthCheck conditions - Added Backup Time Duration table to the Jobs Summary. Close [#144](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/144) - Added Restore point reporting. Close [#143](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/143) +- Added Automatically disable Windows Firewall option to the SureBackup verification option section ### Changed diff --git a/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 b/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 index c1db9c8..cd08639 100644 --- a/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 +++ b/Src/Private/Get-AbrVbrSureBackupjobconf.ps1 @@ -178,6 +178,7 @@ function Get-AbrVbrSureBackupjobconf { 'Application Initialization Timeout' = "$($LinkedJob.StartupOptions.ApplicationInitializationTimeout) sec" 'VM heartbeat is present' = ConvertTo-TextYN $LinkedJob.StartupOptions.VMHeartBeatCheckEnabled 'VM respond to ping on any interface' = ConvertTo-TextYN $LinkedJob.StartupOptions.VMPingCheckEnabled + 'Automatically disable Windows Firewall' = ConvertTo-TextYN $LinkedJob.StartupOptions.WindowsFirewallDisabled 'VM Role' = ConvertTo-EmptyToFiller ($LinkedJob.ScriptOptions.PredefinedApplication -join ", ") 'VM Test Script' = Switch ([string]::IsNullOrEmpty(($LinkedJob.ScriptOptions | ForEach-Object { if ($_.Name) { $_.Name } }))) { $true { '--' } From 5df4c44aed50d6535d18a40fe2858b62a5073699 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Fri, 15 Mar 2024 16:29:14 -0400 Subject: [PATCH 15/17] Add a section about Restore Points #143 --- Src/Private/Get-AbrVbrBackupJobsRP.ps1 | 9 +- Src/Private/Get-AbrVbrBackupjob.ps1 | 4 +- Src/Private/Get-AbrVbrBackupsRPSummary.ps1 | 92 +++++++++++++++++++ Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 | 8 +- Src/Private/SharedUtilsFunctions.ps1 | 56 +++++++++-- Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 | 5 +- 6 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 Src/Private/Get-AbrVbrBackupsRPSummary.ps1 diff --git a/Src/Private/Get-AbrVbrBackupJobsRP.ps1 b/Src/Private/Get-AbrVbrBackupJobsRP.ps1 index ff664ad..59c7048 100644 --- a/Src/Private/Get-AbrVbrBackupJobsRP.ps1 +++ b/Src/Private/Get-AbrVbrBackupJobsRP.ps1 @@ -27,13 +27,13 @@ function Get-AbrVbrBackupJobsRP { $BackupJobs = Get-VBRBackup | Sort-Object -Property Name if ($BackupJobs) { Write-PScriboMessage "Collecting Veeam VBR Restore Point." - Section -Style Heading3 'Backup Jobs' { - Paragraph "The following section summarizes the backup jobs restore points." + Section -Style Heading3 'Backup Restore Points' { + Paragraph "The following section details per Backup Job restore points." BlankLine foreach ($BackupJob in $BackupJobs) { $BackupJobRestorePoints = Get-VBRRestorePoint -Backup $BackupJob | Sort-Object -Property VMName, CreationTimeUt, Type if ($BackupJobRestorePoints) { - Section -Style Heading4 $BackupJob.Name { + Section -ExcludeFromTOC -Style NOTOCHeading4 $BackupJob.Name { $RestorePointInfo = @() foreach ($RestorePoint in $BackupJobRestorePoints) { try { @@ -41,6 +41,7 @@ function Get-AbrVbrBackupJobsRP { $CompressRatio = $RestorePoint.GetStorage().stats.CompressRatio if ($DedupRatio -gt 1) { $DedupRatio = 100 / $DedupRatio } else { $DedupRatio = 1 } if ($CompressRatio -gt 1) { $CompressRatio = 100 / $CompressRatio } else { $CompressRatio = 1 } + $inObj = [ordered] @{ 'VM Name' = $RestorePoint.VMName 'Backup Type' = $RestorePoint.Algorithm @@ -58,7 +59,7 @@ function Get-AbrVbrBackupJobsRP { $TableParams = @{ Name = "Restore Points - $($BackupJob.Name)" List = $false - ColumnWidths = 20, 16, 16, 16, 16, 16 + ColumnWidths = 40, 12, 12, 12, 12, 12 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" diff --git a/Src/Private/Get-AbrVbrBackupjob.ps1 b/Src/Private/Get-AbrVbrBackupjob.ps1 index 354f6f9..d85cfbe 100644 --- a/Src/Private/Get-AbrVbrBackupjob.ps1 +++ b/Src/Private/Get-AbrVbrBackupjob.ps1 @@ -113,7 +113,7 @@ function Get-AbrVbrBackupjob { $StandardDeviation = $Null if ($BKJobSession) { try { - $Duration = Get-AvgTimeDuration -JobSessions $BKJobSession + $Duration = Get-AvgTimeDuration -InputObject $BKJobSession -StartTime 'CreationTime' -EndTime 'EndTime' } catch { Out-Null } @@ -129,7 +129,7 @@ function Get-AbrVbrBackupjob { 'Name' = $Bkjob.Name 'Last Backup Duration' = Switch ([string]::IsNullOrEmpty($BKJobSession)) { $true { '--' } - $false { Get-TimeDuration -JobTimeSpan (New-TimeSpan -Start $BKJobSession[0].CreationTime -End $BKJobSession[0].EndTime) } + $false { Get-TimeDuration -TimeSpan (New-TimeSpan -Start $BKJobSession[0].CreationTime -End $BKJobSession[0].EndTime) } } 'Last 10 Backup AVG Duration' = Switch ([string]::IsNullOrEmpty($Duration)) { $true { '--' } diff --git a/Src/Private/Get-AbrVbrBackupsRPSummary.ps1 b/Src/Private/Get-AbrVbrBackupsRPSummary.ps1 new file mode 100644 index 0000000..0daad59 --- /dev/null +++ b/Src/Private/Get-AbrVbrBackupsRPSummary.ps1 @@ -0,0 +1,92 @@ +function Get-AbrVbrBackupsRPSummary { + <# + .SYNOPSIS + Used by As Built Report to retrieve Veeam VBR Backups Restore Point Summary + .DESCRIPTION + Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo. + .NOTES + Version: 0.8.5 + Author: Jonathan Colon + Twitter: @jcolonfzenpr + Github: rebelinux + Credits: Iain Brighton (@iainbrighton) - PScribo module + + .LINK + https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR + #> + [CmdletBinding()] + param ( + ) + + begin { + Write-PScriboMessage "RestorePoint InfoLevel set at $($InfoLevel.Restore.RestorePoint)." + } + + process { + try { + $BackupJobs = Get-VBRBackup | Sort-Object -Property Name + $BackupJobs += Get-VBRTapeBackup -WarningAction SilentlyContinue | Sort-Object -Property Name + + if ($BackupJobs) { + Write-PScriboMessage "Collecting Veeam VBR Restore Point." + $RestorePointInfo = @() + foreach ($BackupJob in $BackupJobs) { + $BackupJobRestorePoints = Get-VBRRestorePoint -Backup $BackupJob + if ($BackupJobRestorePoints) { + try { + if ($FullRP = $BackupJobRestorePoints | Where-Object { $_.Type -eq 'Full' -and -Not $_.IsCorrupted -and $_.CompletionTimeUtc -gt $_.CreationTimeUTC }) { + try { + $FullDuration = Get-TimeDurationSum -InputObject $FullRP -StartTime 'CreationTimeUTC' -EndTime 'CompletionTimeUtc' + $FullDurationAvg = Get-TimeDuration -TimeSpan ([timespan]::fromseconds(($FullDuration / $FullRP.Count))) + } catch { + $FullDurationAvg = '--' + } + } else { + $FullDurationAvg = '--' + } + + if ($IncrementRP = $BackupJobRestorePoints | Where-Object { $_.Type -eq 'Increment' -and -Not $_.IsCorrupted -and $_.CompletionTimeUtc -gt $_.CreationTimeUTC } ) { + try { + $IncrementDuration = Get-TimeDurationSum -InputObject $IncrementRP -StartTime 'CreationTimeUTC' -EndTime 'CompletionTimeUtc' + $IncrementDurationAvg = Get-TimeDuration -TimeSpan ([timespan]::fromseconds(($IncrementDuration / $IncrementRP.Count))) + } catch { + $IncrementDurationAvg = '--' + } + } else { + $IncrementDurationAvg = '--' + } + + $inObj = [ordered] @{ + 'Job Name' = $BackupJob.Name + 'Oldest Backup' = $BackupJobRestorePoints[0].CreationTimeUTC + 'Newest Backup' = $BackupJobRestorePoints[-1].CreationTimeUTC + 'Full Count' = ($BackupJobRestorePoints | Where-Object { $_.Type -eq 'Full' }).Count + 'Increment Count ' = ($BackupJobRestorePoints | Where-Object { $_.Type -eq 'Increment' }).Count + 'Average Full Duration' = $FullDurationAvg + 'Average Increment Duration ' = $IncrementDurationAvg + } + $RestorePointInfo += [PSCustomObject]$InObj + + } catch { + Write-PScriboMessage -IsWarning "Restore Point table: $($_.Exception.Message)" + } + } + } + + $TableParams = @{ + Name = "Restore Points - $VeeamBackupServer" + List = $false + ColumnWidths = 22, 14, 14, 12, 12, 14, 12 + } + if ($Report.ShowTableCaptions) { + $TableParams['Caption'] = "- $($TableParams.Name)" + } + $RestorePointInfo | Sort-Object -Property 'Job Name' | Table @TableParams + + } + } catch { + Write-PScriboMessage -IsWarning "Restore Point Section: $($_.Exception.Message)" + } + } + end {} +} \ No newline at end of file diff --git a/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 b/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 index 8e43f2c..acc2779 100644 --- a/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 +++ b/Src/Private/Get-AbrVbrTapeBackupJobsRP.ps1 @@ -27,13 +27,13 @@ function Get-AbrVbrTapeBackupJobsRP { $BackupJobs = Get-VBRTapeBackup -WarningAction SilentlyContinue | Sort-Object -Property Name if ($BackupJobs) { Write-PScriboMessage "Collecting Veeam VBR Tape Restore Point." - Section -Style Heading3 'Tape Backup Jobs ' { - Paragraph "The following section summarizes the tape backup jobs restore points." + Section -Style Heading3 'Tape Backup Restore Points ' { + Paragraph "The following section details per Tape Backup Job restore points." BlankLine foreach ($BackupJob in $BackupJobs) { $BackupJobRestorePoints = Get-VBRRestorePoint -Backup $BackupJob | Sort-Object -Property VMName, CreationTimeUt, Type if ($BackupJobRestorePoints) { - Section -Style Heading4 $BackupJob.Name { + Section -ExcludeFromTOC -Style NOTOCHeading4 $BackupJob.Name { $RestorePointInfo = @() foreach ($RestorePoint in $BackupJobRestorePoints) { try { @@ -58,7 +58,7 @@ function Get-AbrVbrTapeBackupJobsRP { $TableParams = @{ Name = "Tape Restore Points - $($BackupJob.Name)" List = $false - ColumnWidths = 20, 16, 16, 16, 16, 16 + ColumnWidths = 40, 12, 12, 12, 12, 12 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" diff --git a/Src/Private/SharedUtilsFunctions.ps1 b/Src/Private/SharedUtilsFunctions.ps1 index d2c4326..a3d40e8 100644 --- a/Src/Private/SharedUtilsFunctions.ps1 +++ b/Src/Private/SharedUtilsFunctions.ps1 @@ -515,7 +515,7 @@ function Get-TimeDuration { Version: 0.1.0 Author: Jonathan Colon .EXAMPLE - Get-TimeDuration -$JobTimeSpan + Get-TimeDuration -$TimeSpan .LINK #> @@ -526,16 +526,49 @@ function Get-TimeDuration { Position = 0, Mandatory )] - [TimeSpan] $JobTimeSpan + [TimeSpan] $TimeSpan ) - if ($JobTimeSpan.Days -gt 0) { - $JobTimeSpan.ToString("dd\.hh\:mm\:ss") + if ($TimeSpan.Days -gt 0) { + $TimeSpan.ToString("dd\.hh\:mm\:ss") } else { - $JobTimeSpan.ToString("hh\:mm\:ss") + $TimeSpan.ToString("hh\:mm\:ss") } } +function Get-TimeDurationSum { + <# + .SYNOPSIS + Used by As Built Report to convert inputobject Duration time to TimeFormat. + .DESCRIPTION + .NOTES + Version: 0.1.0 + Author: Jonathan Colon + .EXAMPLE + Get-TimeDurationSum -$InputObject $Variable -StartTime $StartObjct -EndTime $EndObject + .LINK + #> + + [CmdletBinding()] + Param + ( + [Parameter ( + Position = 0, + Mandatory + )] + [Object[]] $InputObject, + [String] $StartTime, + [String] $EndTime + + ) + + $TimeDurationObj = @() + foreach ($Object in $InputObject) { + $TimeDurationObj += (New-TimeSpan -Start $Object.$StartTime -End $Object.$EndTime).TotalSeconds + } + + return ($TimeDurationObj | Measure-Object -Sum).Sum +} function Get-AvgTimeDuration { <# .SYNOPSIS @@ -545,7 +578,7 @@ function Get-AvgTimeDuration { Version: 0.1.0 Author: Jonathan Colon .EXAMPLE - Get-TimeDuration -$JobTimeSpan + Get-AvgTimeDuration -$InputObject $Variable -StartTime $StartObjct -EndTime $EndObject .LINK #> @@ -556,18 +589,21 @@ function Get-AvgTimeDuration { Position = 0, Mandatory )] - $JobSessions + [Object[]] $InputObject, + [String] $StartTime, + [String] $EndTime + ) $TimeDurationObj = @() - foreach ($JobSession in $JobSessions) { - $TimeDurationObj += New-TimeSpan -Start $JobSession.CreationTime -End $JobSession.EndTime + foreach ($Object in $InputObject) { + $TimeDurationObj += New-TimeSpan -Start $Object.$StartTime -End $Object.$EndTime } # Calculate AVG TimeDuration of job sessions $AverageTimeSpan = New-TimeSpan -Seconds (($TimeDurationObj.TotalSeconds | Measure-Object -Average).Average) - return (Get-TimeDuration -JobTimeSpan $AverageTimeSpan) + return (Get-TimeDuration -TimeSpan $AverageTimeSpan) } function Get-StrdDevDuration { diff --git a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 index e2387b6..cc61e28 100644 --- a/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 +++ b/Src/Public/Invoke-AsBuiltReport.Veeam.VBR.ps1 @@ -425,13 +425,14 @@ function Invoke-AsBuiltReport.Veeam.VBR { } #---------------------------------------------------------------------------------------------# - # Backup Jobs Restore Points Section # + # Backup Restore Points Section # #---------------------------------------------------------------------------------------------# if ($InfoLevel.Jobs.PSObject.Properties.Value -ne 0) { if (((Get-VBRJob -WarningAction SilentlyContinue).count -gt 0) -or ((Get-VBRTapeJob).count -gt 0) -or ((Get-VBRSureBackupJob).count -gt 0)) { - Section -Style Heading2 'Restores Points' { + Section -Style Heading2 'Backups Summary' { Paragraph "The following section provides information about the jobs restore points in Veeam Server: $(((Get-VBRServerSession).Server))." BlankLine + Get-AbrVbrBackupsRPSummary if ($InfoLevel.Jobs.Restores -gt 0) { Get-AbrVbrBackupJobsRP Get-AbrVbrTapeBackupJobsRP From d4bed63e63c8edac56189456fdb1c6a61b34610a Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Fri, 15 Mar 2024 16:29:54 -0400 Subject: [PATCH 16/17] Add a section about Restore Points #143 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5645b0f..2921610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added HealthCheck conditions - Added Backup Time Duration table to the Jobs Summary. Close [#144](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/144) - Added Restore point reporting. Close [#143](https://github.com/AsBuiltReport/AsBuiltReport.Veeam.VBR/issues/143) -- Added Automatically disable Windows Firewall option to the SureBackup verification option section +- Added Automatically disable Windows Firewall option to the SureBackup verification section ### Changed From d510add9e270f9001459de62c202c29e54759ad5 Mon Sep 17 00:00:00 2001 From: Jonathan Colon Date: Fri, 15 Mar 2024 17:10:47 -0400 Subject: [PATCH 17/17] Added Restores to ReadMe file --- README.md | 143 +++++++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 70474ee..f9f6b43 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ The Veeam VBR As Built Report supports the following Veeam Backup & Replication ## :no_entry_sign: Unsupported Versions -The versions 10 and 11 are no longer supported by Veeam. So I will not be performing compatibility tests with those versions. The report may work on previous versions but I do not guarantee and will not address issues related to pre-v12. +The versions 10 and 11 are no longer supported by Veeam. So I will not be performing compatibility tests with those versions. The report may work on previous versions but I do not guarantee and will not address issues related to pre-v12. [Veeam Product Lifecycle Policy](https://www.veeam.com/product-lifecycle.html) @@ -65,9 +65,9 @@ The versions 10 and 11 are no longer supported by Veeam. So I will not be perfor This report is compatible with the following PowerShell versions; -| Windows PowerShell 5.1 | PowerShell 7 | -|:----------------------:|:--------------------:| -| :white_check_mark: | :x: | +| Windows PowerShell 5.1 | PowerShell 7 | +| :--------------------: | :----------: | +| :white_check_mark: | :x: | ## :wrench: System Requirements @@ -127,27 +127,27 @@ The following provides information of how to configure each schema within the re The **Report** schema provides configuration of the Veeam VBR report information. -| Sub-Schema | Setting | Default | Description | -|---------------------|--------------|--------------------------------|--------------------------------------------------------------| +| Sub-Schema | Setting | Default | Description | +| ------------------- | ------------ | ------------------------- | ------------------------------------------------------------ | | Name | User defined | Veeam VBR As Built Report | The name of the As Built Report | -| Version | User defined | 1.0 | The report version | -| Status | User defined | Released | The report release status | -| ShowCoverPageImage | true / false | true | Toggle to enable/disable the display of the cover page image | -| ShowTableOfContents | true / false | true | Toggle to enable/disable table of contents | -| ShowHeaderFooter | true / false | true | Toggle to enable/disable document headers & footers | -| ShowTableCaptions | true / false | true | Toggle to enable/disable table captions/numbering | +| Version | User defined | 1.0 | The report version | +| Status | User defined | Released | The report release status | +| ShowCoverPageImage | true / false | true | Toggle to enable/disable the display of the cover page image | +| ShowTableOfContents | true / false | true | Toggle to enable/disable table of contents | +| ShowHeaderFooter | true / false | true | Toggle to enable/disable document headers & footers | +| ShowTableCaptions | true / false | true | Toggle to enable/disable table captions/numbering | ### Options The **Options** schema allows certain options within the report to be toggled on or off. -| Sub-Schema | Setting | Default | Description | -|-------------------------|--------------------|--------------------------------|----------------------------------------------------| -| BackupServerPort | TCP Port | 9392 | Set the backup server service's custom port. | -| PSDefaultAuthentication | Negotiate/Kerberos | Default | Set the PSRemoting authentication method | -| EnableCharts | true/false | false | Toggle to enable/disable creation of charts. | -| EnableHardwareInventory | true / false | false | Toggle to enable/disable of Hardware information | -| EnableDiagrams | true / false | false | Toggle to enable/disable of Infrastructure Diagrams| +| Sub-Schema | Setting | Default | Description | +| ----------------------- | ------------------ | ------- | --------------------------------------------------- | +| BackupServerPort | TCP Port | 9392 | Set the backup server service's custom port. | +| PSDefaultAuthentication | Negotiate/Kerberos | Default | Set the PSRemoting authentication method | +| EnableCharts | true/false | false | Toggle to enable/disable creation of charts. | +| EnableHardwareInventory | true / false | false | Toggle to enable/disable of Hardware information | +| EnableDiagrams | true / false | false | Toggle to enable/disable of Infrastructure Diagrams | ###### * Note: In order to generate the infrastructure diagram, the Veeam.Diagrammer module requires the following windows application [Graphviz](https://graphviz.org/download/#windows) >= v9.0 @@ -157,82 +157,83 @@ The **InfoLevel** schema allows configuration of each section of the report at a There are 4 levels (0-3) of detail granularity for each section as follows; -| Setting | InfoLevel | Description | -|:-------:|-------------------|-------------------------------------------------------------------------------------------------------| -| 0 | Disabled | Does not collect or display any information | -| 1 | Enabled | Provides summarised information for a collection of objects | -| 2 | Adv Summary | Provides condensed, detailed information for a collection of objects | -| 3 | Detailed | Provides detailed information for individual objects | +| Setting | InfoLevel | Description | +| :-----: | ----------- | -------------------------------------------------------------------- | +| 0 | Disabled | Does not collect or display any information | +| 1 | Enabled | Provides summarised information for a collection of objects | +| 2 | Adv Summary | Provides condensed, detailed information for a collection of objects | +| 3 | Detailed | Provides detailed information for individual objects | The table below outlines the default and maximum **InfoLevel** settings for each Backup Infrastructure section. -| Sub-Schema | Default Setting | Maximum Setting | -|----------------|:---------------:|:---------------:| -| BackupServe | 1 | 3 | -| Proxy | 1 | 3 | -| Settings | 1 | 2 | -| BR | 1 | 2 | -| Licenses | 1 | 1 | -| SOBR | 1 | 2 | -| WANAccel | 1 | 1 | -| ServiceProvider| 1 | 2 | -| SureBackup | 1 | 2 | +| Sub-Schema | Default Setting | Maximum Setting | +| --------------- | :-------------: | :-------------: | +| BackupServe | 1 | 3 | +| Proxy | 1 | 3 | +| Settings | 1 | 2 | +| BR | 1 | 2 | +| Licenses | 1 | 1 | +| SOBR | 1 | 2 | +| WANAccel | 1 | 1 | +| ServiceProvider | 1 | 2 | +| SureBackup | 1 | 2 | The table below outlines the default and maximum **InfoLevel** settings for each Tape Infrastructure section. -| Sub-Schema | Default Setting | Maximum Setting | -|--------------|:---------------:|:---------------:| -| Server | 1 | 1 | -| Library | 1 | 2 | -| MediaPool | 1 | 2 | -| Vault | 1 | 1 | -| NDMP | 1 | 1 | +| Sub-Schema | Default Setting | Maximum Setting | +| ---------- | :-------------: | :-------------: | +| Server | 1 | 1 | +| Library | 1 | 2 | +| MediaPool | 1 | 2 | +| Vault | 1 | 1 | +| NDMP | 1 | 1 | The table below outlines the default and maximum **InfoLevel** settings for each Inventory section. -| Sub-Schema | Default Setting | Maximum Setting | -|--------------|:---------------:|:---------------:| -| VI | 1 | 1 | -| PHY | 1 | 2 | -| FileShare | 1 | 1 | +| Sub-Schema | Default Setting | Maximum Setting | +| ---------- | :-------------: | :-------------: | +| VI | 1 | 1 | +| PHY | 1 | 2 | +| FileShare | 1 | 1 | The table below outlines the default and maximum **InfoLevel** settings for each Storage Infrastructure section. -| Sub-Schema | Default Setting | Maximum Setting | -|--------------|:---------------:|:---------------:| -| ONTAP | 1 | 2 | -| ISILON | 1 | 2 | +| Sub-Schema | Default Setting | Maximum Setting | +| ---------- | :-------------: | :-------------: | +| ONTAP | 1 | 2 | +| ISILON | 1 | 2 | The table below outlines the default and maximum **InfoLevel** settings for each Backup Jobs section. -| Sub-Schema | Default Setting | Maximum Setting | -|--------------|:---------------:|:---------------:| -| Backup | 1 | 2 | -| BackupCopy | 1 | 2 | -| Tape | 1 | 2 | -| Surebackup | 1 | 2 | -| Agent | 1 | 2 | -| FileShare | 1 | 2 | -| Replication | 1 | 2 | +| Sub-Schema | Default Setting | Maximum Setting | +| ----------- | :-------------: | :-------------: | +| Backup | 1 | 2 | +| BackupCopy | 1 | 2 | +| Tape | 1 | 2 | +| Surebackup | 1 | 2 | +| Agent | 1 | 2 | +| FileShare | 1 | 2 | +| Replication | 1 | 2 | +| Restores | 1 | 1 | The table below outlines the default and maximum **InfoLevel** settings for each Replication section. | Sub-Schema | Default Setting | Maximum Setting | -|--------------|:---------------:|:---------------:| +| ------------ | :-------------: | :-------------: | | Replica | 1 | 2 | | FailoverPlan | 1 | 1 | The table below outlines the default and maximum **InfoLevel** settings for each Cloud Connect section. -| Sub-Schema | Default Setting | Maximum Setting | -|-----------------|:---------------:|:---------------:| -| Certificate | 1 | 1 | -| PublicIP | 1 | 1 | -| CloudGateway | 1 | 2 | -| GatewayPools | 1 | 1 | -| Tenants | 1 | 2 | -| BackupStorage | 1 | 1 | -| ReplicaResources| 1 | 2 | +| Sub-Schema | Default Setting | Maximum Setting | +| ---------------- | :-------------: | :-------------: | +| Certificate | 1 | 1 | +| PublicIP | 1 | 1 | +| CloudGateway | 1 | 2 | +| GatewayPools | 1 | 1 | +| Tenants | 1 | 2 | +| BackupStorage | 1 | 1 | +| ReplicaResources | 1 | 2 | ### Healthcheck