Src/Private/Get-AbrVbrBackupServerInfo.ps1


function Get-AbrVbrBackupServerInfo {
    <#
    .SYNOPSIS
    Used by As Built Report to retrieve Veeam VBR Backup Server Information
    .DESCRIPTION
        Documents the configuration of Veeam VBR in Word/HTML/Text formats using PScribo.
    .NOTES
        Version: 0.8.9
        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 "Discovering Veeam V&R Server information from $System."
    }

    process {
        try {
            if ($script:BackupServers = Get-VBRServer -Type Local) {
                Section -Style Heading3 'Backup Server' {
                    $OutObj = @()
                    try {
                        foreach ($BackupServer in $BackupServers) {
                            $CimSession = try { New-CimSession $BackupServer.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -Name 'CIMBackupServer' -ErrorAction Stop } catch { Write-PScriboMessage -IsWarning "Backup Server Section: New-CimSession: Unable to connect to $($BackupServer.Name): $($_.Exception.MessageId)" }

                            $PssSession = try { New-PSSession $BackupServer.Name -Credential $Credential -Authentication $Options.PSDefaultAuthentication -ErrorAction Stop -Name 'PSSBackupServer' } catch {
                                if (-Not $_.Exception.MessageId) {
                                    $ErrorMessage = $_.FullyQualifiedErrorId
                                } else { $ErrorMessage = $_.Exception.MessageId }
                                Write-PScriboMessage -IsWarning "Backup Server Section: New-PSSession: Unable to connect to $($BackupServer.Name): $ErrorMessage"
                            }
                            $SecurityOptions = Get-VBRSecurityOptions
                            try { $DomainJoined = Get-CimInstance -Class Win32_ComputerSystem -Property PartOfDomain -CimSession $CimSession } catch { 'Unknown' }
                            Write-PScriboMessage "Collecting Backup Server information from $($BackupServer.Name)."
                            try {
                                $script:VeeamVersion = Invoke-Command -Session $PssSession -ErrorAction SilentlyContinue -ScriptBlock { Get-ChildItem -Recurse HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall | Get-ItemProperty | Where-Object { $_.DisplayName -match 'Veeam Backup & Replication Server' } | Select-Object -Property DisplayVersion }
                            } catch { Write-PScriboMessage -IsWarning "Backup Server Inkoke-Command Section: $($_.Exception.Message)" }
                            try {
                                $VeeamInfo = Invoke-Command -Session $PssSession -ErrorAction SilentlyContinue -ScriptBlock { Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication' }
                            } catch { Write-PScriboMessage -IsWarning "Backup Server Invoke-Command Section: $($_.Exception.Message)" }
                            try {
                                $VeeamDBFlavor = Invoke-Command -Session $PssSession -ErrorAction SilentlyContinue -ScriptBlock { Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\DatabaseConfigurations' }
                            } catch { Write-PScriboMessage -IsWarning "Backup Server Invoke-Command Section: $($_.Exception.Message)" }
                            try {
                                $VeeamDBInfo = Invoke-Command -Session $PssSession -ErrorAction SilentlyContinue -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication\DatabaseConfigurations\$(($Using:VeeamDBFlavor).SqlActiveConfiguration)" }
                            } catch { Write-PScriboMessage -IsWarning "Backup Server Invoke-Command Section: $($_.Exception.Message)" }
                            Write-PScriboMessage "Discovered $BackupServer Server."
                            $inObj = [ordered] @{
                                'Server Name' = $BackupServer.Name
                                'Is Domain Joined?' = ConvertTo-TextYN $DomainJoined.PartOfDomain
                                'Version' = Switch (($VeeamVersion).count) {
                                    0 { "--" }
                                    default { $VeeamVersion.DisplayVersion }
                                }
                                'Database Server' = Switch ([string]::IsNullOrEmpty($VeeamDBInfo.SqlServerName)) {
                                    $true { "--" }
                                    $false { $VeeamDBInfo.SqlServerName }
                                    default { 'Unknown' }
                                }
                                'Database Instance' = Switch ([string]::IsNullOrEmpty($VeeamDBInfo.SqlInstanceName)) {
                                    $true { "--" }
                                    $false { $VeeamDBInfo.SqlInstanceName }
                                    default { 'Unknown' }
                                }
                                'Database Name' = Switch ([string]::IsNullOrEmpty($VeeamDBInfo.SqlDatabaseName)) {
                                    $true { "--" }
                                    $false { $VeeamDBInfo.SqlDatabaseName }
                                    default { 'Unknown' }
                                }
                                'Connection Ports' = Switch (($VeeamInfo.BackupServerPort).count) {
                                    0 { "--" }
                                    default { "Backup Server Port: $($VeeamInfo.BackupServerPort)`r`nSecure Connections Port: $($VeeamInfo.SecureConnectionsPort)`r`nCloud Server Port: $($VeeamInfo.CloudServerPort)`r`nCloud Service Port: $($VeeamInfo.CloudSvcPort)" }
                                }
                                'Install Path' = Switch (($VeeamInfo.CorePath).count) {
                                    0 { "--" }
                                    default { $VeeamInfo.CorePath }
                                }
                                'Audit Logs Path' = $SecurityOptions.AuditLogsPath
                                'Compress Old Audit Logs' = ConvertTo-TextYN $SecurityOptions.CompressOldAuditLogs
                                'Fips Compliant Mode' = Switch ($SecurityOptions.FipsCompliantModeEnabled) {
                                    'True' { "Enabled" }
                                    'False' { "Disabled" }
                                }
                                'Linux host authentication' = Switch ($SecurityOptions.HostPolicy.Type) {
                                    'All' { "Add all discovered host to the list automatically" }
                                    'KnownHosts' { "Add unknown host to the list manually" }
                                }
                                'Logging Level' = $VeeamInfo.LoggingLevel

                            }

                            if ($Null -notlike $VeeamInfo.LogDirectory) {
                                $inObj.add('Log Directory', ($VeeamInfo.LogDirectory))
                            }

                            $OutObj += [pscustomobject]$inobj
                        }
                    } catch {
                        Write-PScriboMessage -IsWarning "Backup Server Section: $($_.Exception.Message)"
                    }

                    if ($HealthCheck.Infrastructure.BackupServer) {
                        $OutObj | Where-Object { $_.'Logging Level' -gt 4 } | Set-Style -Style Warning -Property 'Logging Level'
                        $OutObj | Where-Object { $_.'Is Domain Joined?' -eq 'Yes' } | Set-Style -Style Warning -Property 'Is Domain Joined?'
                    }

                    $TableParams = @{
                        Name = "Backup Server - $($BackupServer.Name.Split(".")[0])"
                        List = $true
                        ColumnWidths = 40, 60
                    }
                    if ($Report.ShowTableCaptions) {
                        $TableParams['Caption'] = "- $($TableParams.Name)"
                    }
                    $OutObj | Table @TableParams
                    if ($HealthCheck.Infrastructure.BestPractice) {
                        if ($OutObj | Where-Object { $_.'Is Domain Joined?' -eq 'Yes' }) {
                            Paragraph "Health Check:" -Bold -Underline
                            BlankLine
                            Paragraph {
                                Text "Best Practice:" -Bold
                                Text "When setting up the Veeam Availability infrastructure keep in mind the principle that a data protection system should not rely on the environment it is meant to protect in any way! This is because when your production environment goes down along with its domain controllers, it will impact your ability to perform actual restores due to the backup server's dependency on those domain controllers for backup console authentication, DNS for name resolution, etc."
                            }
                            BlankLine
                            Paragraph {
                                Text 'Reference:' -Bold
                                Text 'https://bp.veeam.com/vbr/Security/Security_domains.html'
                            }
                            BlankLine
                        }
                    }
                    #---------------------------------------------------------------------------------------------#
                    # Backup Server Inventory & Software Summary Section #
                    #---------------------------------------------------------------------------------------------#
                    try {
                        Write-PScriboMessage "Hardware Inventory Status set as $($Options.EnableHardwareInventory)."
                        if ($Options.EnableHardwareInventory) {
                            $BackupServer = Get-VBRServer -Type Local
                            Write-PScriboMessage "Collecting Backup Server Inventory Summary from $($BackupServer.Name)."
                            $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
                            if ($HW = Invoke-Command -Session $PssSession -ScriptBlock { Get-ComputerInfo }) {
                                Section -Style Heading4 'Hardware & Software Inventory' {
                                    $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.NumberOfCores | Measure-Object -Sum).Sum
                                        'Number of Logical Cores' = ($HWCPU.NumberOfLogicalProcessors | Measure-Object -Sum).Sum
                                        'Physical Memory (GB)' = ConvertTo-FileSizeString $HW.CsTotalPhysicalMemory
                                    }
                                    $OutObj += [pscustomobject]$inobj

                                    if ($HealthCheck.Infrastructure.Server) {
                                        $OutObj | Where-Object { $_.'Number of CPU Cores' -lt 2 } | 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 Server Inventory - $($BackupServer.Name.Split(".")[0])"
                                        List = $true
                                        ColumnWidths = 40, 60
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $OutObj | Table @TableParams
                                    if ($HealthCheck.Infrastructure.BestPractice) {
                                        if (([int]([regex]::Matches($OutObj.'Physical Memory (GB)', "\d+(?!.*\d+)").value) -lt 8) -or ($OutObj | Where-Object { $_.'Number of CPU Cores' -lt 2 })) {
                                            Paragraph "Health Check:" -Bold -Underline
                                            BlankLine
                                            Paragraph {
                                                Text "Best Practice:" -Bold
                                                Text "Recommended Veeam Backup Server minimum configuration is two CPU cores and 8GB of RAM."
                                            }
                                            BlankLine
                                        }
                                    }
                                    #---------------------------------------------------------------------------------------------#
                                    # Backup Server Local Disk Inventory Section #
                                    #---------------------------------------------------------------------------------------------#
                                    if ($InfoLevel.Infrastructure.BackupServer -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 NOTOCHeading5 -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 "Backup Server Local Disk $($Disk.Number) Section: $($_.Exception.Message)"
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "Backup Server - Local Disks"
                                                        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 "Backup Server Local Disk Section: $($_.Exception.Message)"
                                        }
                                        #---------------------------------------------------------------------------------------------#
                                        # Backup Server 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 NOTOCHeading5 -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 "Backup Server SAN Disk $($Disk.Number) Section: $($_.Exception.Message)"
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "Backup Server - SAN Disks"
                                                        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 "Backup Server SAN Disk Section: $($_.Exception.Message)"
                                        }
                                    }
                                    #---------------------------------------------------------------------------------------------#
                                    # Backup Server 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 NOTOCHeading5 -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 "Backup Server Host Volume $($HostVolume.DriveLetter) Section: $($_.Exception.Message)"
                                                    }
                                                }
                                                $TableParams = @{
                                                    Name = "Backup Server - Volumes"
                                                    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 "Backup Server Host Volume Section: $($_.Exception.Message)"
                                    }
                                    #---------------------------------------------------------------------------------------------#
                                    # Backup Server Network Inventory Section #
                                    #---------------------------------------------------------------------------------------------#
                                    if ($InfoLevel.Infrastructure.BackupServer -ge 2) {
                                        try {
                                            $HostAdapters = Invoke-Command -Session $PssSession { Get-NetAdapter }
                                            if ($HostAdapters) {
                                                Section -Style NOTOCHeading5 -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 "Backup Server Host Volume $($HostAdapter.Name) Section: $($_.Exception.Message)"
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "Backup Server - Network Adapters"
                                                        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 "Backup Server Host Volume Section: $($_.Exception.Message)"
                                        }
                                        try {
                                            $NetIPs = Invoke-Command -Session $PssSession { Get-NetIPConfiguration | Where-Object -FilterScript { ($_.NetAdapter.Status -Eq "Up") } }
                                            if ($NetIPs) {
                                                Section -Style NOTOCHeading5 -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 "Backup Server Host Volume $($NetIp.InterfaceAlias) Section: $($_.Exception.Message)"
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "Backup Server - IP Address"
                                                        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 "Backup Server Host Volume Section: $($_.Exception.Message)"
                                        }
                                    }
                                }
                            }
                        }
                    } catch {
                        Write-PScriboMessage -IsWarning "Backup Server Inventory Summary Section: $($_.Exception.Message)"
                    }
                    try {
                        Write-PScriboMessage "Infrastructure Backup Server InfoLevel set at $($InfoLevel.Infrastructure.BackupServer)."
                        if ($InfoLevel.Infrastructure.BackupServer -ge 3) {
                            $VeeamInfo = Invoke-Command -Session $PssSession -ErrorAction SilentlyContinue -ScriptBlock { Get-ItemProperty -Path 'HKLM:\SOFTWARE\Veeam\Veeam Backup and Replication' }
                            $DefaultRegistryHash = @{
                                "AgentLogging" = "1"
                                "AgentLogOptions" = "flush"
                                "LoggingLevel" = "4"
                                "VNXBlockNaviSECCliPath" = "C:\Program Files\Veeam\Backup and Replication\Backup\EMC Navisphere CLI\NaviSECCli.exe"
                                "VNXeUemcliPath" = "C:\Program Files\Veeam\Backup and Replication\Backup\EMC Unisphere CLI\3.0.1\uemcli.exe"
                                "SqlLockInfo" = ""
                                "CloudServerPort" = "10003"
                                "SqlDatabaseName" = "VeeamBackup"
                                "SqlInstanceName" = "VEEAMSQL2016"
                                "SqlServerName" = ""
                                "SqlLogin" = ""
                                "CorePath" = "C:\Program Files\Veeam\Backup and Replication\Backup\"
                                "BackupServerPort" = "9392"
                                "SecureConnectionsPort" = "9401"
                                "VddkReadBufferSize" = "0"
                                "EndPointServerPort" = "10001"
                                "SqlSecuredPassword" = ""
                                "IsComponentsUpdateRequired" = "0"
                                "LicenseAutoUpdate" = "1"
                                "CloudSvcPort" = "6169"
                                "VBRServiceRestartNeeded" = "0"
                                "ImportServers" = "0"
                                "MaxLogCount" = "10"
                                "MaxLogSize" = "10240"
                                "RunspaceId" = "0000"
                                "ProviderCredentialsId" = ""
                                "ProviderInfo" = ""
                                "ProviderId" = ""
                            }
                            if ($VeeamInfo) {
                                $OutObj = @()
                                $Hashtable = $VeeamInfo | ForEach-Object {
                                    foreach ($prop in $_.psobject.Properties.Where({ $_.Name -notlike 'PS*' })) {
                                        [pscustomobject] @{
                                            Key = $prop.Name
                                            Value = $prop.Value
                                        }
                                    }
                                }
                                foreach ($Registry in $Hashtable) {
                                    if ($Registry.Key -notin $DefaultRegistryHash.Keys) {
                                        $inObj = [ordered] @{
                                            'Registry Key' = $Registry.Key
                                            'Registry Value' = Switch (($Registry.Value).count) {
                                                0 { '--' }
                                                1 { $Registry.Value }
                                                default { $Registry.Value -Join ', ' }

                                            }
                                        }
                                        $OutObj += [pscustomobject]$inobj
                                    }
                                }

                                $TableParams = @{
                                    Name = "Non-Default Registry Keys - $($BackupServer.Name.Split(".")[0])"
                                    List = $false
                                    ColumnWidths = 50, 50
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                            }
                            if ($OutObj) {
                                Section -Style Heading4 "Non-Default Registry Keys" {
                                    $OutObj | Sort-Object -Property 'Registry Key' | Table @TableParams
                                }
                            }
                        }
                    } catch {
                        Write-PScriboMessage -IsWarning "Backup Server Non-Default Registry Keys Section: $($_.Exception.Message)"
                    }
                    #---------------------------------------------------------------------------------------------#
                    # Backup Server Services Information Section #
                    #---------------------------------------------------------------------------------------------#
                    if ($HealthCheck.Infrastructure.Server) {
                        $BackupServer = Get-VBRServer -Type Local
                        try {
                            Write-PScriboMessage "Infrastructure Backup Server InfoLevel set at $($InfoLevel.Infrastructure.BackupServer)."
                            if ($InfoLevel.Infrastructure.BackupServer -ge 2) {
                                Write-PScriboMessage "Collecting Backup Server Service Summary from $($BackupServer.Name)."
                                $Available = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service "W32Time" | Select-Object DisplayName, Name, Status }
                                if ($Available) {
                                    $Services = Invoke-Command -Session $PssSession -ScriptBlock { Get-Service Veeam* }
                                    Section -Style Heading4 "HealthCheck - Services Status" {
                                        $OutObj = @()
                                        foreach ($Service in $Services) {
                                            Write-PScriboMessage "Collecting '$($Service.DisplayName)' status on $($BackupServer.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 - $($BackupServer.Name.Split(".")[0])"
                                            List = $false
                                            ColumnWidths = 45, 35, 20
                                        }
                                        if ($Report.ShowTableCaptions) {
                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                        }
                                        $OutObj | Table @TableParams
                                    }
                                }
                            }
                        } catch {
                            Write-PScriboMessage -IsWarning "Backup Server Service Status Section: $($_.Exception.Message)"
                        }
                    }
                    if ($HealthCheck.Infrastructure.BestPractice) {
                        try {
                            $UpdObj = @()
                            $Updates = Invoke-Command -Session $PssSession -ScriptBlock { (New-Object -ComObject Microsoft.Update.Session).CreateupdateSearcher().Search("IsHidden=0 and IsInstalled=0").Updates | Select-Object Title, KBArticleIDs }
                            $UpdObj += if ($Updates) {
                                $OutObj = @()
                                foreach ($Update in $Updates) {
                                    try {
                                        $inObj = [ordered] @{
                                            'KB Article' = "KB$($Update.KBArticleIDs)"
                                            'Name' = $Update.Title
                                        }
                                        $OutObj += [pscustomobject]$inobj

                                        if ($HealthCheck.OperatingSystem.Updates) {
                                            $OutObj | Set-Style -Style Warning
                                        }
                                    } catch {
                                        Write-PScriboMessage -IsWarning $_.Exception.Message
                                    }
                                }

                                $OutObj | Set-Style -Style Warning

                                $TableParams = @{
                                    Name = "Missing Windows Updates - $($BackupServer.Name.Split(".")[0])"
                                    List = $false
                                    ColumnWidths = 40, 60
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $OutObj | Sort-Object -Property 'Name' | Table @TableParams
                            }
                            if ($UpdObj) {
                                Section -Style Heading4 'Missing Windows Updates' {
                                    Paragraph "The following table provides a summary of the backup server pending/missing windows updates."
                                    BlankLine
                                    $UpdObj
                                }
                                Paragraph "Health Check:" -Bold -Underline
                                BlankLine
                                Paragraph {
                                    Text "Security Best Practices:" -Bold
                                    Text "Patch operating systems, software, and firmware on Veeam components. Most hacks succeed because there is already vulnerable software in use which is not up-to-date with current patch levels. So make sure all software and hardware where Veeam components are running are up-to-date. One of the most possible causes of a credential theft are missing guest OS updates and use of outdated authentication protocols."
                                }
                            }
                        } catch {
                            Write-PScriboMessage -IsWarning $_.Exception.Message
                        }
                    }
                }
            }
        } catch {
            Write-PScriboMessage -IsWarning "Backup Server Section: $($_.Exception.Message)"
        }
    }
    end {
        if ($PssSession) { Remove-PSSession -Session $PssSession }
        if ($CimSession) { Remove-CimSession $CimSession }
    }

}