Src/Public/Invoke-AsBuiltReport.VMware.vSphere.ps1

function Invoke-AsBuiltReport.VMware.vSphere {
    <#
    .SYNOPSIS
        PowerShell script to document the configuration of VMware vSphere infrastucture in Word/HTML/Text formats
    .DESCRIPTION
        Documents the configuration of VMware vSphere infrastucture in Word/HTML/Text formats using PScribo.
    .NOTES
        Version: 1.3.4-RC2
        Author: Tim Carman
        Twitter: @tpcarman
        Github: tpcarman
        Credits: Iain Brighton (@iainbrighton) - PScribo module
    .LINK
        https://github.com/AsBuiltReport/AsBuiltReport.VMware.vSphere
    #>


    param (
        [String[]] $Target,
        [PSCredential] $Credential
    )

    Write-PScriboMessage -Plugin "Module" -IsWarning "Please refer to www.asbuiltreport.com 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.VMware.vSphere"
    Write-PScriboMessage -Plugin "Module" -IsWarning "Issues or bug reporting: https://github.com/AsBuiltReport/AsBuiltReport.VMware.vSphere/issues"

    # Check the current AsBuiltReport.VMware.vSphere module
    Try {
        $InstalledVersion = Get-Module -ListAvailable -Name AsBuiltReport.VMware.vSphere -ErrorAction SilentlyContinue | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version

        if ($InstalledVersion) {
            Write-PScriboMessage -Plugin "Module" -IsWarning "AsBuiltReport.VMware.vSphere $($InstalledVersion.ToString()) is currently installed."
            $LatestVersion = Find-Module -Name AsBuiltReport.VMware.vSphere -Repository PSGallery -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Version
            if ($LatestVersion -gt $InstalledVersion) {
                Write-PScriboMessage -Plugin "Module" -IsWarning "AsBuiltReport.VMware.vSphere $($LatestVersion.ToString()) is available."
                Write-PScriboMessage -Plugin "Module" -IsWarning "Run 'Update-Module -Name AsBuiltReport.VMware.vSphere -Force' to install the latest version."
            }
        }
    } Catch {
        Write-PscriboMessage -Plugin "Module" -IsWarning $_.Exception.Message
    }
    # Check if the required version of VMware PowerCLI is installed
    Get-RequiredModule -Name 'VMware.PowerCLI' -Version '13.2'

    # Import Report Configuration
    $Report = $ReportConfig.Report
    $InfoLevel = $ReportConfig.InfoLevel
    $Options = $ReportConfig.Options
    # Used to set values to TitleCase where required
    $TextInfo = (Get-Culture).TextInfo

    #region Script Body
    #---------------------------------------------------------------------------------------------#
    # SCRIPT BODY #
    #---------------------------------------------------------------------------------------------#
    # Connect to vCenter Server using supplied credentials
    foreach ($VIServer in $Target) {
        try {
            Write-PScriboMessage "Connecting to vCenter Server '$VIServer'."
            $vCenter = Connect-VIServer $VIServer -Credential $Credential -ErrorAction Stop
        } catch {
            Write-Error $_
        }

        #region Generate vSphere report
        if ($vCenter) {
            # Check logged in user has sufficient privileges to generate an As Built Report
            Write-PScriboMessage 'Checking vCenter user privileges.'
            Try {
                $UserPermission = Get-VIPermission | Where-Object { $_.Principal -eq $vCenter.User }
                $AuthMgr = Get-View $($vCenter.ExtensionData.Content.AuthorizationManager)
                $UserRole = $AuthMgr.RoleList | Where-Object { $_.Name -eq $($UserPermission.Role) }
            } Catch {
                Write-PScriboMessage 'Unable to obtain vCenter user privileges.'
            }

            # Create a lookup hashtable to quickly link VM MoRefs to Names
            # Exclude VMware Site Recovery Manager placeholder VMs
            Write-PScriboMessage 'Creating VM lookup hashtable.'
            $VMs = Get-VM -Server $vCenter | Where-Object {
                $_.ExtensionData.Config.ManagedBy.ExtensionKey -notlike 'com.vmware.vcDr*'
            } | Sort-Object Name
            $VMLookup = @{ }
            foreach ($VM in $VMs) {
                $VMLookup.($VM.Id) = $VM.Name
            }

            # Create a lookup hashtable to link Host MoRefs to Names
            # Exclude VMware HCX hosts and ESX/ESXi versions prior to vSphere 5.0 from VMHost lookup
            Write-PScriboMessage 'Creating VMHost lookup hashtable.'
            $VMHosts = Get-VMHost -Server $vCenter | Where-Object { $_.Model -notlike "*VMware Mobility Platform" -and $_.Version -gt 5 } | Sort-Object Name
            $VMHostLookup = @{ }
            foreach ($VMHost in $VMHosts) {
                $VMHostLookup.($VMHost.Id) = $VMHost.Name
            }

            # Create a lookup hashtable to link Datastore MoRefs to Names
            Write-PScriboMessage 'Creating Datastore lookup hashtable.'
            $Datastores = Get-Datastore -Server $vCenter | Where-Object { ($_.State -eq 'Available') -and ($_.CapacityGB -gt 0) } | Sort-Object Name
            $DatastoreLookup = @{ }
            foreach ($Datastore in $Datastores) {
                $DatastoreLookup.($Datastore.Id) = $Datastore.Name
            }

            # Create a lookup hashtable to link VDS Portgroups MoRefs to Names
            Write-PScriboMessage 'Creating VDPortGroup lookup hashtable.'
            $VDPortGroups = Get-VDPortgroup -Server $vCenter | Sort-Object Name
            $VDPortGroupLookup = @{ }
            foreach ($VDPortGroup in $VDPortGroups) {
                $VDPortGroupLookup.($VDPortGroup.Key) = $VDPortGroup.Name
            }

            # Create a lookup hashtable to link EVC Modes to Names
            Write-PScriboMessage 'Creating EVC lookup hashtable.'
            $SupportedEvcModes = $vCenter.ExtensionData.Capability.SupportedEVCMode
            $EvcModeLookup = @{ }
            foreach ($EvcMode in $SupportedEvcModes) {
                $EvcModeLookup.($EvcMode.Key) = $EvcMode.Label
            }

            $si = Get-View ServiceInstance -Server $vCenter
            $extMgr = Get-View -Id $si.Content.ExtensionManager -Server $vCenter

            #region VMware Update Manager Server Name
            Write-PScriboMessage 'Checking for VMware Update Manager Server.'
            $VumServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vcIntegrity' } |
            Select-Object @{
                N = 'Name';
                E = { ($_.Server | Where-Object { $_.Type -eq 'SOAP' -and $_.Company -eq 'VMware, Inc.' } |
                        Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] }
            }
            #endregion VMware Update Manager Server Name

            #region VxRail Manager Server Name
            Write-PScriboMessage 'Checking for VxRail Manager Server.'
            $VxRailMgr = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vxrail' } |
            Select-Object @{
                N = 'Name';
                E = { ($_.Server | Where-Object { $_.Type -eq 'HTTPS' } |
                        Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] }
            }
            #endregion VxRail Manager Server Name

            #region Site Recovery Manager Server Name
            Write-PScriboMessage 'Checking for VMware Site Recovery Manager Server.'
            $SrmServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vcDr' } |
            Select-Object @{
                N = 'Name';
                E = { ($_.Server | Where-Object { $_.Company -eq 'VMware, Inc.' } |
                        Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] }
            }
            #endregion Site Recovery Manager Server Name

            #region NSX-T Manager Server Name
            Write-PScriboMessage 'Checking for VMware NSX-T Manager Server.'
            $NsxtServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.nsx.management.nsxt' } |
            Select-Object @{
                N = 'Name';
                E = { ($_.Server | Where-Object { ($_.Company -eq 'VMware') -and ($_.Type -eq 'VIP') } |
                        Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] }
            }
            #endregion NSX-T Manager Server Name

            #region Tag Information
            $TagAssignments = Get-TagAssignment -Server $vCenter
            $Tags = Get-Tag -Server $vCenter | Sort-Object Name, Category
            $TagCategories = Get-TagCategory -Server $vCenter | Sort-Object Name | Select-Object Name, Description, Cardinality -Unique
            #endregion Tag Information

            #region vCenter Advanced Settings
            Write-PScriboMessage "Collecting $vCenter advanced settings."
            $vCenterAdvSettings = Get-AdvancedSetting -Entity $vCenter
            $vCenterServerName = ($vCenterAdvSettings | Where-Object { $_.name -eq 'VirtualCenter.FQDN' }).Value
            $vCenterServerName = $vCenterServerName.ToString().ToLower()
            #endregion vCenter Advanced Settings

            #region vCenter Server Heading1 Section
            Section -Style Heading1 $vCenterServerName {
                #region vCenter Server Section
                Write-PScriboMessage "vCenter InfoLevel set at $($InfoLevel.vCenter)."
                if ($InfoLevel.vCenter -ge 1) {
                    Section -Style Heading2 'vCenter Server' {
                        Paragraph "The following sections detail the configuration of vCenter Server $vCenterServerName."
                        BlankLine
                        # Gather basic vCenter Server Information
                        $vCenterServerInfo = [PSCustomObject]@{
                            'vCenter Server' = $vCenterServerName
                            'IP Address' = ($vCenterAdvSettings | Where-Object { $_.name -like 'VirtualCenter.AutoManagedIPV4' }).Value
                            'Version' = $vCenter.Version
                            'Build' = $vCenter.Build
                        }
                        #region vCenter Server Summary & Advanced Summary
                        if ($InfoLevel.vCenter -le 2) {
                            $TableParams = @{
                                Name = "vCenter Server Summary - $vCenterServerName"
                                ColumnWidths = 25, 25, 25, 25
                            }
                            if ($Report.ShowTableCaptions) {
                                $TableParams['Caption'] = "- $($TableParams.Name)"
                            }
                            $vCenterServerInfo | Table @TableParams
                        }
                        #endregion vCenter Server Summary & Advanced Summary

                        #region vCenter Server Detailed Information
                        if ($InfoLevel.vCenter -ge 3) {
                            $MemberProps = @{
                                'InputObject' = $vCenterServerInfo
                                'MemberType' = 'NoteProperty'
                            }
                            #region vCenter Server Detail
                            if ($UserRole.Privilege -contains 'Global.Licenses') {
                                $vCenterLicense = Get-License -vCenter $vCenter
                                Add-Member @MemberProps -Name 'Product' -Value $vCenterLicense.Product
                                Add-Member @MemberProps -Name 'License Key' -Value $vCenterLicense.LicenseKey
                                Add-Member @MemberProps -Name 'License Expiration' -Value $vCenterLicense.Expiration
                            } else {
                                Write-PScriboMessage "Insufficient user privileges to report vCenter Server licensing. Please ensure the user account has the 'Global > Licenses' privilege assigned."
                            }

                            Add-Member @MemberProps -Name 'Instance ID' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'instance.id' }).Value

                            if ($vCenter.Version -ge 6) {
                                Add-Member @MemberProps -Name 'HTTP Port' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.rhttpproxy.httpport' }).Value
                                Add-Member @MemberProps -Name 'HTTPS Port' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.rhttpproxy.httpsport' }).Value
                                Add-Member @MemberProps -Name 'Platform Services Controller' -Value ((($vCenterAdvSettings).Where{ $_.name -eq 'config.vpxd.sso.admin.uri' }).Value).Split('/')[2]
                            }
                            if ($VumServer.Name) {
                                Add-Member @MemberProps -Name 'Update Manager Server' -Value $VumServer.Name
                            }
                            if ($SrmServer.Name) {
                                Add-Member @MemberProps -Name 'Site Recovery Manager Server' -Value $SrmServer.Name
                            }
                            if ($NsxtServer.Name) {
                                Add-Member @MemberProps -Name 'NSX-T Manager Server' -Value $NsxtServer.Name
                            }
                            if ($VxRailMgr.Name) {
                                Add-Member @MemberProps -Name 'VxRail Manager Server' -Value $VxRailMgr.Name
                            }
                            if ($Healthcheck.vCenter.Licensing) {
                                $vCenterServerInfo | Where-Object { $_.'Product' -like '*Evaluation*' } | Set-Style -Style Warning -Property 'Product'
                                $vCenterServerInfo | Where-Object { $null -eq $_.'Product' } | Set-Style -Style Warning -Property 'Product'
                                $vCenterServerInfo | Where-Object { $_.'License Key' -like '*-00000-00000' } | Set-Style -Style Warning -Property 'License Key'
                                $vCenterServerInfo | Where-Object { $_.'License Expiration' -eq 'Expired' } | Set-Style -Style Critical -Property 'License Expiration'
                            }
                            $TableParams = @{
                                Name = "vCenter Server Configuration - $vCenterServerName"
                                List = $true
                                ColumnWidths = 50, 50
                            }
                            if ($Report.ShowTableCaptions) {
                                $TableParams['Caption'] = "- $($TableParams.Name)"
                            }
                            $vCenterServerInfo | Table @TableParams
                            #endregion vCenter Server Detail

                            #region vCenter Server Database Settings
                            Section -Style Heading3 'Database Settings' {
                                $vCenterDbInfo = [PSCustomObject]@{
                                    'Database Type' = $TextInfo.ToTitleCase(($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.odbc.dbtype' }).Value)
                                    'Data Source Name' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.odbc.dsn' }).Value
                                    'Maximum Database Connection' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'VirtualCenter.MaxDBConnection' }).Value
                                }
                                $TableParams = @{
                                    Name = "Database Settings - $vCenterServerName"
                                    List = $true
                                    ColumnWidths = 50, 50
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $vCenterDbInfo | Table @TableParams
                            }
                            #endregion vCenter Server Database Settings

                            #region vCenter Server Mail Settings
                            Section -Style Heading3 'Mail Settings' {
                                $vCenterMailInfo = [PSCustomObject]@{
                                    'SMTP Server' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.smtp.server' }).Value
                                    'SMTP Port' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.smtp.port' }).Value
                                    'Mail Sender' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.sender' }).Value
                                }
                                if ($Healthcheck.vCenter.Mail) {
                                    $vCenterMailInfo | Where-Object { !($_.'SMTP Server') } | Set-Style -Style Critical -Property 'SMTP Server'
                                    $vCenterMailInfo | Where-Object { !($_.'SMTP Port') } | Set-Style -Style Critical -Property 'SMTP Port'
                                    $vCenterMailInfo | Where-Object { !($_.'Mail Sender') } | Set-Style -Style Critical -Property 'Mail Sender'
                                }
                                $TableParams = @{
                                    Name = "Mail Settings - $vCenterServerName"
                                    List = $true
                                    ColumnWidths = 50, 50
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $vCenterMailInfo | Table @TableParams
                            }
                            #endregion vCenter Server Mail Settings

                            #region vCenter Server Historical Statistics
                            Section -Style Heading3 'Historical Statistics' {
                                $vCenterHistoricalStats = Get-vCenterStats | Select-Object @{L = 'Interval Duration'; E = { $_.IntervalDuration } }, @{L = 'Interval Enabled'; E = { $_.IntervalEnabled } },
                                @{L = 'Save Duration'; E = { $_.SaveDuration } }, @{L = 'Statistics Level'; E = { $_.StatsLevel } } -Unique
                                $TableParams = @{
                                    Name = "Historical Statistics - $vCenterServerName"
                                    ColumnWidths = 25, 25, 25, 25
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $vCenterHistoricalStats | Table @TableParams
                            }
                            #endregion vCenter Server Historical Statistics

                            #region vCenter Server Licensing
                            if ($UserRole.Privilege -contains 'Global.Licenses') {
                                Section -Style Heading3 'Licensing' {
                                    $Licenses = Get-License -Licenses | Select-Object Product, @{L = 'License Key'; E = { ($_.LicenseKey) } }, Total, Used, @{L = 'Available'; E = { ($_.total) - ($_.Used) } }, Expiration -Unique
                                    if ($Healthcheck.vCenter.Licensing) {
                                        $Licenses | Where-Object { $_.Product -eq 'Product Evaluation' } | Set-Style -Style Warning
                                        $Licenses | Where-Object { $_.Expiration -eq 'Expired' } | Set-Style -Style Critical
                                    }
                                    $TableParams = @{
                                        Name = "Licensing - $vCenterServerName"
                                        ColumnWidths = 25, 25, 12, 12, 12, 14
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $Licenses | Sort-Object 'Product', 'License Key' | Table @TableParams
                                }
                            } else {
                                Write-PScriboMessage "Insufficient user privileges to report vCenter Server licensing. Please ensure the user account has the 'Global > Licenses' privilege assigned."
                            }
                            #endregion vCenter Server Licensing

                            #region vCenter Server Certificate
                            if ($vCenter.Version -ge 6) {
                                Section -Style Heading3 'Certificate' {
                                    $VcenterCertMgmt = [PSCustomObject]@{
                                        'Country' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.country' }).Value
                                        'Email' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.email' }).Value
                                        'Locality' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.localityName' }).Value
                                        'State' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.state' }).Value
                                        'Organization' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.organizationName' }).Value
                                        'Organization Unit' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.organizationalUnitName' }).Value
                                        'Validity' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.daysValid'}).Value / 365) years"
                                        'Mode' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.mode' }).Value
                                        'Soft Threshold' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.softThreshold'}).Value) days"
                                        'Hard Threshold' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.hardThreshold'}).Value) days"
                                        'Minutes Before' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.minutesBefore' }).Value
                                        'Poll Interval' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.pollIntervalDays'}).Value) days"
                                    }
                                    $TableParams = @{
                                        Name = "Certificate - $vCenterServerName"
                                        List = $true
                                        ColumnWidths = 50, 50
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $VcenterCertMgmt | Table @TableParams
                                }
                            }
                            #endregion vCenter Server Certificate

                            #region vCenter Server Roles
                            Section -Style Heading3 'Roles' {
                                $VIRoles = Get-VIRole -Server $vCenter | Where-Object { $null -ne $_.PrivilegeList } | Sort-Object Name
                                $VIRoleInfo = foreach ($VIRole in $VIRoles) {
                                    [PSCustomObject]@{
                                        'Role' = $VIRole.Name
                                        'System Role' = Switch ($VIRole.IsSystem) {
                                            $true { 'Yes' }
                                            $false { 'No' }
                                        }
                                        'Privilege List' = ($VIRole.PrivilegeList).Replace(".", " > ") | Select-Object -Unique
                                    }
                                }
                                if ($InfoLevel.vCenter -ge 4) {
                                    $VIRoleInfo | ForEach-Object {
                                        Section -Style NOTOCHeading5 -ExcludeFromTOC $($_.Role) {
                                            $TableParams = @{
                                                Name = "Role $($_.Role) - $vCenterServerName"
                                                ColumnWidths = 35, 15, 50
                                            }
                                            if ($Report.ShowTableCaptions) {
                                                $TableParams['Caption'] = "- $($TableParams.Name)"
                                            }
                                            $_ | Table @TableParams
                                        }
                                    }
                                } else {
                                    $TableParams = @{
                                        Name = "Roles - $vCenterServerName"
                                        Columns = 'Role', 'System Role'
                                        ColumnWidths = 50, 50
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $VIRoleInfo | Table @TableParams
                                }
                            }
                            #endregion vCenter Server Roles

                            #region vCenter Server Tags
                            if ($Tags) {
                                Section -Style Heading3 'Tags' {
                                    $TagInfo = foreach ($Tag in $Tags) {
                                        [PSCustomObject] @{
                                            'Tag' = $Tag.Name
                                            'Description' = Switch ($Tag.Description) {
                                                '' { 'None' }
                                                default { $Tag.Description }
                                            }
                                            'Category' = Switch ($Tag.Category) {
                                                '' { 'None' }
                                                default { $Tag.Category }
                                            }
                                        }
                                    }
                                    $TableParams = @{
                                        Name = "Tags - $vCenterServerName"
                                        ColumnWidths = 30, 40, 30
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $TagInfo | Table @TableParams
                                }
                            }
                            #endregion vCenter Server Tags

                            #region vCenter Server Tag Categories
                            if ($TagCategories) {
                                Section -Style Heading3 'Tag Categories' {
                                    $TagCategoryInfo = foreach ($TagCategory in $TagCategories) {
                                        [PSCustomObject] @{
                                            'Category' = $TagCategory.Name
                                            'Description' = Switch ($TagCategory.Description) {
                                                '' { 'None' }
                                                default { $TagCategory.Description }
                                            }
                                            'Cardinality' = Switch ($TagCategory.Cardinality) {
                                                '' { 'None' }
                                                default { $TagCategory.Cardinality }
                                            }
                                        }
                                    }
                                    $TableParams = @{
                                        Name = "Tag Categories - $vCenterServerName"
                                        ColumnWidths = 30, 40, 30
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $TagCategoryInfo | Table @TableParams
                                }
                            }
                            #endregion vCenter Server Tag Categories

                            #region vCenter Server Tag Assignments
                            if ($TagAssignments) {
                                Section -Style Heading3 'Tag Assignments' {
                                    $TagAssignmentInfo = foreach ($TagAssignment in $TagAssignments) {
                                        [PSCustomObject]@{
                                            'Entity' = $TagAssignment.Entity.Name
                                            'Tag' = $TagAssignment.Tag.Name
                                            'Category' = $TagAssignment.Tag.Category
                                        }
                                    }
                                    $TableParams = @{
                                        Name = "Tag Assignments - $vCenterServerName"
                                        ColumnWidths = 30, 40, 30
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $TagAssignmentInfo | Sort-Object Entity | Table @TableParams
                                }
                            }
                            #endregion vCenter Server Tag Assignments

                            #region VM Storage Policies
                            if ($UserRole.Privilege -contains 'StorageProfile.View') {
                                $SpbmStoragePolicies = Get-SpbmStoragePolicy | Sort-Object Name
                                if ($SpbmStoragePolicies) {
                                    Section -Style Heading3 'VM Storage Policies' {
                                        $VmStoragePolicies = foreach ($SpbmStoragePolicy in $SpbmStoragePolicies) {
                                            [PSCustomObject]@{
                                                'VM Storage Policy' = $SpbmStoragePolicy.Name
                                                'Description' = $SpbmStoragePolicy.Description
                                            }
                                        }
                                        $TableParams = @{
                                            Name = "VM Storage Policies - $vCenterServerName"
                                            ColumnWidths = 50, 50
                                        }
                                        if ($Report.ShowTableCaptions) {
                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                        }
                                        $VmStoragePolicies | Table @TableParams
                                    }
                                }
                            } else {
                                Write-PScriboMessage "Insufficient user privileges to report VM storage policies. Please ensure the user account has the 'Storage Profile > View' privilege assigned."
                            }
                            #endregion VM Storage Policies
                        }
                        #endregion vCenter Server Detailed Information

                        #region vCenter Server Advanced Detail Information
                        if ($InfoLevel.vCenter -ge 4) {
                            #region vCenter Alarms
                            Section -Style Heading3 'Alarms' {
                                $Alarms = Get-AlarmDefinition -PipelineVariable alarm | ForEach-Object -Process {
                                    Get-AlarmAction -AlarmDefinition $_ -PipelineVariable action | ForEach-Object -Process {
                                        Get-AlarmActionTrigger -AlarmAction $_ |
                                        Select-Object @{N = 'Alarm'; E = { $alarm.Name } },
                                        @{N = 'Description'; E = { $alarm.Description } },
                                        @{N = 'Enabled'; E = { Switch ($alarm.Enabled) {
                                                    $true { 'Enabled' }
                                                    $false { 'Disabled' }
                                                } }
                                        },
                                        @{N = 'Entity'; E = { $alarm.Entity.Type } },
                                        @{N = 'Trigger'; E = {
                                                "{0}:{1}->{2} (Repeat={3})" -f $action.ActionType,
                                                $_.StartStatus,
                                                $_.EndStatus,
                                                $_.Repeat
                                            }
                                        },
                                        @{N = 'Trigger Info'; E = { Switch ($action.ActionType) {
                                                    'SendEmail' {
                                                        "To: $($action.To -join ', ') `
                                                        Cc: $($action.Cc -join ', ') `
                                                        Subject: $($action.Subject) `
                                                        Body: $($action.Body)"

                                                    }
                                                    'ExecuteScript' {
                                                        "$($action.ScriptFilePath)"
                                                    }
                                                    default { '--' }
                                                }
                                            }
                                        }
                                    }
                                }
                                $Alarms = ($Alarms).Where{ $_.alarm -ne "" } | Sort-Object 'Alarm', 'Trigger'
                                if ($Healthcheck.vCenter.Alarms) {
                                    $Alarms | Where-Object { $_.'Enabled' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Enabled'
                                }
                                if ($InfoLevel.vCenter -ge 5) {
                                    foreach ($Alarm in $Alarms) {
                                        Section -Style NOTOCHeading5 -ExcludeFromTOC $($Alarm.Alarm) {
                                            $TableParams = @{
                                                Name = "$($Alarm.Alarm) - $vCenterServerName"
                                                List = $true
                                                ColumnWidths = 25, 75
                                            }
                                            if ($Report.ShowTableCaptions) {
                                                $TableParams['Caption'] = "- $($TableParams.Name)"
                                            }
                                            $Alarm | Table @TableParams
                                        }
                                    }
                                } else {
                                    $TableParams = @{
                                        Name = "Alarms - $vCenterServerName"
                                        Columns = 'Alarm', 'Description', 'Enabled', 'Entity', 'Trigger'
                                        ColumnWidths = 20, 20, 20, 20, 20
                                    }
                                    if ($Report.ShowTableCaptions) {
                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                    }
                                    $Alarms | Table @TableParams
                                }
                            }
                            #endregion vCenter Alarms
                        }
                        #endregion vCenter Server Advanced Detail Information

                        #region vCenter Server Comprehensive Information
                        if ($InfoLevel.vCenter -ge 5) {
                            #region vCenter Advanced System Settings
                            Section -Style Heading3 'Advanced System Settings' {
                                $TableParams = @{
                                    Name = "vCenter Advanced System Settings - $vCenterServerName"
                                    Columns = 'Name', 'Value'
                                    ColumnWidths = 50, 50
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $vCenterAdvSettings | Sort-Object Name | Table @TableParams
                            }
                            #endregion vCenter Advanced System Settings
                        }
                        #endregion vCenter Server Comprehensive Information
                    }
                }
                #endregion vCenter Server Section

                #region Clusters
                Write-PScriboMessage "Cluster InfoLevel set at $($InfoLevel.Cluster)."
                if ($InfoLevel.Cluster -ge 1) {
                    $Clusters = Get-Cluster -Server $vCenter | Sort-Object Name
                    if ($Clusters) {
                        #region Cluster Section
                        Section -Style Heading2 'Clusters' {
                            Paragraph "The following sections detail the configuration of vSphere HA/DRS clusters managed by vCenter Server $vCenterServerName."
                            #region Cluster Advanced Summary
                            if ($InfoLevel.Cluster -le 2) {
                                BlankLine
                                $ClusterInfo = foreach ($Cluster in $Clusters) {
                                    [PSCustomObject]@{
                                        'Cluster' = $Cluster.Name
                                        'Datacenter' = $Cluster | Get-Datacenter
                                        '# of Hosts' = $Cluster.ExtensionData.Host.Count
                                        '# of VMs' = $Cluster.ExtensionData.VM.Count
                                        'vSphere HA' = Switch ($Cluster.HAEnabled) {
                                            $true { 'Enabled' }
                                            $false { 'Disabled' }
                                        }
                                        'vSphere DRS' = Switch ($Cluster.DrsEnabled) {
                                            $true { 'Enabled' }
                                            $false { 'Disabled' }
                                        }
                                        'Virtual SAN' = Switch ($Cluster.VsanEnabled) {
                                            $true { 'Enabled' }
                                            $false { 'Disabled' }
                                        }
                                        'EVC Mode' = Switch ($Cluster.EVCMode) {
                                            $null { 'Disabled' }
                                            default { $EvcModeLookup."$($Cluster.EVCMode)" }
                                        }
                                        'VM Swap File Policy' = Switch ($Cluster.VMSwapfilePolicy) {
                                            'WithVM' { 'With VM' }
                                            'InHostDatastore' { 'In Host Datastore' }
                                            default { $Cluster.VMSwapfilePolicy }
                                        }
                                    }
                                }
                                if ($Healthcheck.Cluster.HAEnabled) {
                                    $ClusterInfo | Where-Object { $_.'vSphere HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere HA'
                                }
                                if ($Healthcheck.Cluster.DrsEnabled) {
                                    $ClusterInfo | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS'
                                }
                                if ($Healthcheck.Cluster.VsanEnabled) {
                                    $ClusterInfo | Where-Object { $_.'Virtual SAN' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Virtual SAN'
                                }
                                if ($Healthcheck.Cluster.EvcEnabled) {
                                    $ClusterInfo | Where-Object { $_.'EVC Mode' -eq 'Disabled' } | Set-Style -Style Warning -Property 'EVC Mode'
                                }
                                $TableParams = @{
                                    Name = "Cluster Summary - $vCenterServerName"
                                    ColumnWidths = 15, 15, 7, 7, 11, 11, 11, 15, 8
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $ClusterInfo | Table @TableParams
                            }
                            #endregion Cluster Advanced Summary

                            #region Cluster Detailed Information
                            # TODO: Test Tags
                            if ($InfoLevel.Cluster -ge 3) {
                                foreach ($Cluster in $Clusters) {
                                    $ClusterDasConfig = $Cluster.ExtensionData.Configuration.DasConfig
                                    $ClusterDrsConfig = $Cluster.ExtensionData.Configuration.DrsConfig
                                    $ClusterConfigEx = $Cluster.ExtensionData.ConfigurationEx
                                    #region Cluster Section
                                    Section -Style Heading3 $Cluster {
                                        Paragraph "The following table details the configuration for cluster $Cluster."
                                        BlankLine
                                        #region Cluster Configuration
                                        $ClusterDetail = [PSCustomObject]@{
                                            'Cluster' = $Cluster.Name
                                            'ID' = $Cluster.Id
                                            'Datacenter' = $Cluster | Get-Datacenter
                                            'Number of Hosts' = $Cluster.ExtensionData.Host.Count
                                            'Number of VMs' = ($Cluster | Get-VM).Count
                                            'vSphere HA' = Switch ($Cluster.HAEnabled) {
                                                $true { 'Enabled' }
                                                $false { 'Disabled' }
                                            }
                                            'vSphere DRS' = Switch ($Cluster.DrsEnabled) {
                                                $true { 'Enabled' }
                                                $false { 'Disabled' }
                                            }
                                            'Virtual SAN' = Switch ($Cluster.VsanEnabled) {
                                                $true { 'Enabled' }
                                                $false { 'Disabled' }
                                            }
                                            'EVC Mode' = Switch ($Cluster.EVCMode) {
                                                $null { 'Disabled' }
                                                default { $EvcModeLookup."$($Cluster.EVCMode)" }
                                            }
                                            'VM Swap File Policy' = Switch ($Cluster.VMSwapfilePolicy) {
                                                'WithVM' { 'Virtual machine directory' }
                                                'InHostDatastore' { 'Datastore specified by host' }
                                                default { $Cluster.VMSwapfilePolicy }
                                            }
                                        }
                                        $MemberProps = @{
                                            'InputObject' = $ClusterDetail
                                            'MemberType' = 'NoteProperty'
                                        }
                                        <#
                                        if ($TagAssignments | Where-Object {$_.entity -eq $Cluster}) {
                                            Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $Cluster}).Tag -join ',')
                                        }
                                        #>

                                        if ($Healthcheck.Cluster.HAEnabled) {
                                            $ClusterDetail | Where-Object { $_.'vSphere HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere HA'
                                        }
                                        if ($Healthcheck.Cluster.DrsEnabled) {
                                            $ClusterDetail | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS'
                                        }
                                        if ($Healthcheck.Cluster.VsanEnabled) {
                                            $ClusterDetail | Where-Object { $_.'Virtual SAN' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Virtual SAN'
                                        }
                                        if ($Healthcheck.Cluster.EvcEnabled) {
                                            $ClusterDetail | Where-Object { $_.'EVC Mode' -eq 'Disabled' } | Set-Style -Style Warning -Property 'EVC Mode'
                                        }
                                        #region Cluster Advanced Detailed Information
                                        if ($InfoLevel.Cluster -ge 4) {
                                            $ClusterDetail | ForEach-Object {
                                                $ClusterHosts = $Cluster | Get-VMHost | Sort-Object Name
                                                Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Hosts' -Value ($ClusterHosts.Name -join ', ')
                                                $ClusterVMs = $Cluster | Get-VM | Sort-Object Name
                                                Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Virtual Machines' -Value ($ClusterVMs.Name -join ', ')
                                            }
                                        }
                                        #endregion Cluster Advanced Detailed Information
                                        $TableParams = @{
                                            Name = "Cluster Configuration - $Cluster"
                                            List = $true
                                            ColumnWidths = 50, 50
                                        }
                                        if ($Report.ShowTableCaptions) {
                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                        }
                                        $ClusterDetail | Table @TableParams
                                        #endregion Cluster Configuration

                                        #region vSphere HA Cluster Configuration
                                        if ($Cluster.HAEnabled) {
                                            Section -Style Heading4 'vSphere HA Configuration' {
                                                Paragraph "The following section details the vSphere HA configuration for $Cluster cluster."
                                                #region vSphere HA Cluster Failures and Responses
                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'Failures and Responses' {
                                                    $HAClusterResponses = [PSCustomObject]@{
                                                        'Host Monitoring' = $TextInfo.ToTitleCase($ClusterDasConfig.HostMonitoring)
                                                    }
                                                    if ($ClusterDasConfig.HostMonitoring -eq 'Enabled') {
                                                        $MemberProps = @{
                                                            'InputObject' = $HAClusterResponses
                                                            'MemberType' = 'NoteProperty'
                                                        }
                                                        if ($ClusterDasConfig.DefaultVmSettings.RestartPriority -eq 'Disabled') {
                                                            Add-Member @MemberProps -Name 'Host Failure Response' -Value 'Disabled'
                                                        } else {
                                                            Add-Member @MemberProps -Name 'Host Failure Response' -Value 'Restart VMs'
                                                            Switch ($Cluster.HAIsolationResponse) {
                                                                'DoNothing' {
                                                                    Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Disabled'
                                                                }
                                                                'Shutdown' {
                                                                    Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Shutdown and restart VMs'
                                                                }
                                                                'PowerOff' {
                                                                    Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Power off and restart VMs'
                                                                }
                                                            }
                                                            Add-Member @MemberProps -Name 'VM Restart Priority' -Value $Cluster.HARestartPriority
                                                            Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL) {
                                                                'disabled' {
                                                                    Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Disabled'
                                                                }
                                                                'warning' {
                                                                    Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Issue events'
                                                                }
                                                                'restartAggressive' {
                                                                    Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Power off and restart VMs'
                                                                }
                                                            }
                                                            Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD) {
                                                                'disabled' {
                                                                    Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Disabled'
                                                                }
                                                                'warning' {
                                                                    Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Issue events'
                                                                }
                                                                'restartConservative' {
                                                                    Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Power off and restart VMs (conservative)'
                                                                }
                                                                'restartAggressive' {
                                                                    Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Power off and restart VMs (aggressive)'
                                                                }
                                                            }
                                                            Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared) {
                                                                'none' {
                                                                    Add-Member @MemberProps -Name 'APD recovery after APD timeout' -Value 'Disabled'
                                                                }
                                                                'reset' {
                                                                    Add-Member @MemberProps -Name 'APD recovery after APD timeout' -Value 'Reset VMs'
                                                                }
                                                            }
                                                        }
                                                        Switch ($ClusterDasConfig.VmMonitoring) {
                                                            'vmMonitoringDisabled' {
                                                                Add-Member @MemberProps -Name 'VM Monitoring' -Value 'Disabled'
                                                            }
                                                            'vmMonitoringOnly' {
                                                                Add-Member @MemberProps -Name 'VM Monitoring' -Value 'VM monitoring only'
                                                            }
                                                            'vmAndAppMonitoring' {
                                                                Add-Member @MemberProps -Name 'VM Monitoring' -Value 'VM and application monitoring'
                                                            }
                                                        }
                                                    }
                                                    if ($Healthcheck.Cluster.HostFailureResponse) {
                                                        $HAClusterResponses | Where-Object { $_.'Host Failure Response' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Host Failure Response'
                                                    }
                                                    if ($Healthcheck.Cluster.HostMonitoring) {
                                                        $HAClusterResponses | Where-Object { $_.'Host Monitoring' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Host Monitoring'
                                                    }
                                                    if ($Healthcheck.Cluster.DatastoreOnPDL) {
                                                        $HAClusterResponses | Where-Object { $_.'Datastore with Permanent Device Loss' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Datastore with Permanent Device Loss'
                                                    }
                                                    if ($Healthcheck.Cluster.DatastoreOnAPD) {
                                                        $HAClusterResponses | Where-Object { $_.'Datastore with All Paths Down' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Datastore with All Paths Down'
                                                    }
                                                    if ($Healthcheck.Cluster.APDTimeout) {
                                                        $HAClusterResponses | Where-Object { $_.'APD recovery after APD timeout' -eq 'Disabled' } | Set-Style -Style Warning -Property 'APD recovery after APD timeout'
                                                    }
                                                    if ($Healthcheck.Cluster.vmMonitoring) {
                                                        $HAClusterResponses | Where-Object { $_.'VM Monitoring' -eq 'Disabled' } | Set-Style -Style Warning -Property 'VM Monitoring'
                                                    }
                                                    $TableParams = @{
                                                        Name = "vSphere HA Failures and Responses - $Cluster"
                                                        List = $true
                                                        ColumnWidths = 50, 50
                                                    }
                                                    if ($Report.ShowTableCaptions) {
                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                    }
                                                    $HAClusterResponses | Table @TableParams
                                                }
                                                #endregion vSphere HA Cluster Failures and Responses

                                                #region vSphere HA Cluster Admission Control
                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'Admission Control' {
                                                    $HAAdmissionControl = [PSCustomObject]@{
                                                        'Admission Control' = Switch ($Cluster.HAAdmissionControlEnabled) {
                                                            $true { 'Enabled' }
                                                            $false { 'Disabled' }
                                                        }
                                                    }
                                                    if ($Cluster.HAAdmissionControlEnabled) {
                                                        $MemberProps = @{
                                                            'InputObject' = $HAAdmissionControl
                                                            'MemberType' = 'NoteProperty'
                                                        }
                                                        Add-Member @MemberProps -Name 'Host Failures Cluster Tolerates' -Value $Cluster.HAFailoverLevel
                                                        Switch ($ClusterDasConfig.AdmissionControlPolicy.GetType().Name) {
                                                            'ClusterFailoverHostAdmissionControlPolicy' {
                                                                Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Dedicated failover hosts'
                                                            }
                                                            'ClusterFailoverResourcesAdmissionControlPolicy' {
                                                                Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Cluster resource percentage'
                                                            }
                                                            'ClusterFailoverLevelAdmissionControlPolicy' {
                                                                Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Slot policy'
                                                            }
                                                        }
                                                        Switch ($ClusterDasConfig.AdmissionControlPolicy.AutoComputePercentages) {
                                                            $true {
                                                                Add-Member @MemberProps -Name 'Override Calculated Failover Capacity' -Value 'No'
                                                            }
                                                            $false {
                                                                Add-Member @MemberProps -Name 'Override Calculated Failover Capacity' -Value 'Yes'
                                                                Add-Member @MemberProps -Name 'CPU %' -Value $ClusterDasConfig.AdmissionControlPolicy.CpuFailoverResourcesPercent
                                                                Add-Member @MemberProps -Name 'Memory %' -Value $ClusterDasConfig.AdmissionControlPolicy.MemoryFailoverResourcesPercent
                                                            }
                                                        }
                                                        if ($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy) {
                                                            Add-Member @MemberProps -Name 'Slot Policy' -Value 'Fixed slot size'
                                                            Add-Member @MemberProps -Name 'CPU Slot Size' -Value "$($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy.Cpu) MHz"
                                                            Add-Member @MemberProps -Name 'Memory Slot Size' -Value "$($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy.Memory) MB"
                                                        } else {
                                                            Add-Member @MemberProps -Name 'Slot Policy' -Value 'Cover all powered-on virtual machines'
                                                        }
                                                        if ($ClusterDasConfig.AdmissionControlPolicy.ResourceReductionToToleratePercent) {
                                                            Add-Member @MemberProps -Name 'Performance Degradation VMs Tolerate' -Value "$($ClusterDasConfig.AdmissionControlPolicy.ResourceReductionToToleratePercent)%"
                                                        }
                                                    }
                                                    if ($Healthcheck.Cluster.HAAdmissionControl) {
                                                        $HAAdmissionControl | Where-Object { $_.'Admission Control' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Admission Control'
                                                    }
                                                    $TableParams = @{
                                                        Name = "vSphere HA Admission Control - $Cluster"
                                                        List = $true
                                                        ColumnWidths = 50, 50
                                                    }
                                                    if ($Report.ShowTableCaptions) {
                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                    }
                                                    $HAAdmissionControl | Table @TableParams
                                                }
                                                #endregion vSphere HA Cluster Admission Control

                                                #region vSphere HA Cluster Heartbeat Datastores
                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'Heartbeat Datastores' {
                                                    $HeartbeatDatastores = [PSCustomObject]@{
                                                        'Heartbeat Selection Policy' = Switch ($ClusterDasConfig.HBDatastoreCandidatePolicy) {
                                                            'allFeasibleDsWithUserPreference' { 'Use datastores from the specified list and complement automatically if needed' }
                                                            'allFeasibleDs' { 'Automatically select datastores accessible from the host' }
                                                            'userSelectedDs' { 'Use datastores only from the specified list' }
                                                            default { $ClusterDasConfig.HBDatastoreCandidatePolicy }
                                                        }
                                                        'Heartbeat Datastores' = try {
                                                            (((Get-View -Id $ClusterDasConfig.HeartbeatDatastore -Property Name).Name | Sort-Object) -join ', ')
                                                        } catch {
                                                            'None specified'
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "vSphere HA Heartbeat Datastores - $Cluster"
                                                        List = $true
                                                        ColumnWidths = 50, 50
                                                    }
                                                    if ($Report.ShowTableCaptions) {
                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                    }
                                                    $HeartbeatDatastores | Table @TableParams
                                                }
                                                #endregion vSphere HA Cluster Heartbeat Datastores

                                                #region vSphere HA Cluster Advanced Options
                                                $HAAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterHA' }
                                                if ($HAAdvancedSettings) {
                                                    Section -Style NOTOCHeading5 -ExcludeFromTOC 'vSphere HA Advanced Options' {
                                                        $HAAdvancedOptions = @()
                                                        foreach ($HAAdvancedSetting in $HAAdvancedSettings) {
                                                            $HAAdvancedOption = [PSCustomObject]@{
                                                                'Option' = $HAAdvancedSetting.Name
                                                                'Value' = $HAAdvancedSetting.Value
                                                            }
                                                            $HAAdvancedOptions += $HAAdvancedOption
                                                        }
                                                        $TableParams = @{
                                                            Name = "vSphere HA Advanced Options - $Cluster"
                                                            ColumnWidths = 50, 50
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $HAAdvancedOptions | Sort-Object Option | Table @TableParams
                                                    }
                                                }
                                                #endregion vSphere HA Cluster Advanced Options
                                            }
                                        }
                                        #endregion vSphere HA Cluster Configuration

                                        #region Proactive HA Configuration
                                        # TODO: Proactive HA Providers
                                        # Proactive HA is only available in vSphere 6.5 and above
                                        if ($ClusterConfigEx.InfraUpdateHaConfig.Enabled -and $vCenter.Version -ge 6.5) {
                                            Section -Style Heading4 'Proactive HA' {
                                                Paragraph "The following section details the Proactive HA configuration for $Cluster cluster."
                                                #region Proactive HA Failures and Responses Section
                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'Failures and Responses' {
                                                    $ProactiveHa = [PSCustomObject]@{
                                                        'Proactive HA' = Switch ($ClusterConfigEx.InfraUpdateHaConfig.Enabled) {
                                                            $true { 'Enabled' }
                                                            $false { 'Disabled' }
                                                        }
                                                    }
                                                    if ($ClusterConfigEx.InfraUpdateHaConfig.Enabled) {
                                                        $ProactiveHaModerateRemediation = Switch ($ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation) {
                                                            'MaintenanceMode' { 'Maintenance Mode' }
                                                            'QuarantineMode' { 'Quarantine Mode' }
                                                            default { $ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation }
                                                        }
                                                        $ProactiveHaSevereRemediation = Switch ($ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation) {
                                                            'MaintenanceMode' { 'Maintenance Mode' }
                                                            'QuarantineMode' { 'Quarantine Mode' }
                                                            default { $ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation }
                                                        }
                                                        $MemberProps = @{
                                                            'InputObject' = $ProactiveHa
                                                            'MemberType' = 'NoteProperty'
                                                        }
                                                        Add-Member @MemberProps -Name 'Automation Level' -Value $ClusterConfigEx.InfraUpdateHaConfig.Behavior
                                                        if ($ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation -eq $ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation) {
                                                            Add-Member @MemberProps -Name 'Remediation' -Value $ProactiveHaModerateRemediation
                                                        } else {
                                                            Add-Member @MemberProps -Name 'Remediation' -Value 'Mixed Mode'
                                                            Add-Member @MemberProps -Name 'Moderate Remediation' -Value $ProactiveHaModerateRemediation
                                                            Add-Member @MemberProps -Name 'Severe Remediation' -Value $ProactiveHaSevereRemediation
                                                        }
                                                    }
                                                    if ($Healthcheck.Cluster.ProactiveHA) {
                                                        $ProactiveHa | Where-Object { $_.'Proactive HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Proactive HA'
                                                    }
                                                    $TableParams = @{
                                                        Name = "Proactive HA - $Cluster"
                                                        List = $true
                                                        ColumnWidths = 50, 50
                                                    }
                                                    if ($Report.ShowTableCaptions) {
                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                    }
                                                    $ProactiveHa | Table @TableParams
                                                }
                                                #endregion Proactive HA Failures and Responses Section
                                            }
                                        }
                                        #endregion Proactive HA Configuration

                                        #region vSphere DRS Cluster Configuration
                                        if ($Cluster.DrsEnabled) {
                                            Section -Style Heading4 'vSphere DRS Configuration' {
                                                Paragraph ("The following table details the vSphere DRS configuration " +
                                                    "for cluster $Cluster.")
                                                BlankLine

                                                #region vSphere DRS Cluster Specifications
                                                $DrsCluster = [PSCustomObject]@{
                                                    'vSphere DRS' = Switch ($Cluster.DrsEnabled) {
                                                        $true { 'Enabled' }
                                                        $false { 'Disabled' }
                                                    }
                                                }
                                                $MemberProps = @{
                                                    'InputObject' = $DrsCluster
                                                    'MemberType' = 'NoteProperty'
                                                }
                                                Switch ($Cluster.DrsAutomationLevel) {
                                                    'Manual' {
                                                        Add-Member @MemberProps -Name 'Automation Level' -Value 'Manual'
                                                    }
                                                    'PartiallyAutomated' {
                                                        Add-Member @MemberProps -Name 'Automation Level' -Value 'Partially Automated'
                                                    }
                                                    'FullyAutomated' {
                                                        Add-Member @MemberProps -Name 'Automation Level' -Value 'Fully Automated'
                                                    }
                                                }
                                                Add-Member @MemberProps -Name 'Migration Threshold' -Value $ClusterDrsConfig.VmotionRate
                                                Switch ($ClusterConfigEx.ProactiveDrsConfig.Enabled) {
                                                    $false {
                                                        Add-Member @MemberProps -Name 'Predictive DRS' -Value 'Disabled'
                                                    }
                                                    $true {
                                                        Add-Member @MemberProps -Name 'Predictive DRS' -Value 'Enabled'
                                                    }
                                                }
                                                Switch ($ClusterDrsConfig.EnableVmBehaviorOverrides) {
                                                    $true {
                                                        Add-Member @MemberProps -Name 'Virtual Machine Automation' -Value 'Enabled'
                                                    }
                                                    $false {
                                                        Add-Member @MemberProps -Name 'Virtual Machine Automation' -Value 'Disabled'
                                                    }
                                                }
                                                if ($Healthcheck.Cluster.DrsEnabled) {
                                                    $DrsCluster | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS'
                                                }
                                                if ($Healthcheck.Cluster.DrsAutomationLevelFullyAuto) {
                                                    $DrsCluster | Where-Object { $_.'Automation Level' -ne 'Fully Automated' } | Set-Style -Style Warning -Property 'Automation Level'
                                                }
                                                $TableParams = @{
                                                    Name = "vSphere DRS Configuration - $Cluster"
                                                    List = $true
                                                    ColumnWidths = 50, 50
                                                }
                                                if ($Report.ShowTableCaptions) {
                                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                                }
                                                $DrsCluster | Table @TableParams
                                                #endregion vSphere DRS Cluster Specfications

                                                #region DRS Cluster Additional Options
                                                $DrsAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterDRS' }
                                                if ($DrsAdvancedSettings) {
                                                    Section -Style NOTOCHeading5 -ExcludeFromTOC 'Additional Options' {
                                                        $DrsAdditionalOptions = [PSCustomObject] @{
                                                            'VM Distribution' = Switch (($DrsAdvancedSettings | Where-Object { $_.name -eq 'TryBalanceVmsPerHost' }).Value) {
                                                                '1' { 'Enabled' }
                                                                $null { 'Disabled' }
                                                            }
                                                            'Memory Metric for Load Balancing' = Switch (($DrsAdvancedSettings | Where-Object { $_.name -eq 'PercentIdleMBInMemDemand' }).Value) {
                                                                '100' { 'Enabled' }
                                                                $null { 'Disabled' }
                                                            }
                                                            'CPU Over-Commitment' = if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerCore' }).Value) {
                                                                'Enabled'
                                                            } else {
                                                                'Disabled'
                                                            }
                                                        }
                                                        $MemberProps = @{
                                                            'InputObject' = $DrsAdditionalOptions
                                                            'MemberType' = 'NoteProperty'
                                                        }
                                                        if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerCore' }).Value) {
                                                            Add-Member @MemberProps -Name 'Over-Commitment Ratio' -Value "$(($DrsAdvancedSettings | Where-Object {$_.name -eq 'MaxVcpusPerCore'}).Value):1 (vCPU:pCPU)"
                                                        }
                                                        if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerClusterPct' }).Value) {
                                                            Add-Member @MemberProps -Name 'Over-Commitment Ratio (% of cluster capacity)' -Value "$(($DrsAdvancedSettings | Where-Object {$_.name -eq 'MaxVcpusPerClusterPct'}).Value) %"
                                                        }
                                                        $TableParams = @{
                                                            Name = "DRS Additional Options - $Cluster"
                                                            List = $true
                                                            ColumnWidths = 50, 50
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $DrsAdditionalOptions | Table @TableParams
                                                    }
                                                }
                                                #endregion DRS Cluster Additional Options

                                                #region vSphere DPM Configuration
                                                if ($ClusterConfigEx.DpmConfigInfo.Enabled) {
                                                    Section -Style NOTOCHeading5 -ExcludeFromTOC 'Power Management' {
                                                        $DpmConfig = [PSCustomObject]@{
                                                            'DPM' = Switch ($ClusterConfigEx.DpmConfigInfo.Enabled) {
                                                                $true { 'Enabled' }
                                                                $false { 'Disabled' }
                                                            }
                                                        }
                                                        $MemberProps = @{
                                                            'InputObject' = $DpmConfig
                                                            'MemberType' = 'NoteProperty'
                                                        }
                                                        Switch ($ClusterConfigEx.DpmConfigInfo.DefaultDpmBehavior) {
                                                            'manual' {
                                                                Add-Member @MemberProps -Name 'Automation Level' -Value 'Manual'
                                                            }
                                                            'automated' {
                                                                Add-Member @MemberProps -Name 'Automation Level' -Value 'Automated'
                                                            }
                                                        }
                                                        if ($ClusterConfigEx.DpmConfigInfo.DefaultDpmBehavior -eq 'automated') {
                                                            Add-Member @MemberProps -Name 'DPM Threshold' -Value $ClusterConfigEx.DpmConfigInfo.HostPowerActionRate
                                                        }
                                                        $TableParams = @{
                                                            Name = "vSphere DPM - $Cluster"
                                                            List = $true
                                                            ColumnWidths = 50, 50
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $DpmConfig | Table @TableParams
                                                    }
                                                }
                                                #endregion vSphere DPM Configuration

                                                #region vSphere DRS Cluster Advanced Options
                                                $DrsAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterDRS' }
                                                if ($DrsAdvancedSettings) {
                                                    Section -Style NOTOCHeading5 -ExcludeFromTOC 'Advanced Options' {
                                                        $DrsAdvancedOptions = @()
                                                        foreach ($DrsAdvancedSetting in $DrsAdvancedSettings) {
                                                            $DrsAdvancedOption = [PSCustomObject]@{
                                                                'Option' = $DrsAdvancedSetting.Name
                                                                'Value' = $DrsAdvancedSetting.Value
                                                            }
                                                            $DrsAdvancedOptions += $DrsAdvancedOption
                                                        }
                                                        $TableParams = @{
                                                            Name = "vSphere DRS Advanced Options - $Cluster"
                                                            ColumnWidths = 50, 50
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $DrsAdvancedOptions | Sort-Object Option | Table @TableParams
                                                    }
                                                }
                                                #endregion vSphere DRS Cluster Advanced Options

                                                #region vSphere DRS Cluster Group
                                                $DrsClusterGroups = $Cluster | Get-DrsClusterGroup
                                                if ($DrsClusterGroups) {
                                                    #region vSphere DRS Cluster Group Section
                                                    Section -Style NOTOCHeading5 -ExcludeFromTOC 'DRS Cluster Groups' {
                                                        $DrsGroups = foreach ($DrsClusterGroup in $DrsClusterGroups) {
                                                            [PSCustomObject]@{
                                                                'DRS Cluster Group' = $DrsClusterGroup.Name
                                                                'Type' = Switch ($DrsClusterGroup.GroupType) {
                                                                    'VMGroup' { 'VM Group' }
                                                                    'VMHostGroup' { 'Host Group' }
                                                                    default { $DrsClusterGroup.GroupType }
                                                                }
                                                                'Members' = Switch (($DrsClusterGroup.Member).Count -gt 0) {
                                                                    $true { ($DrsClusterGroup.Member | Sort-Object) -join ', ' }
                                                                    $false { "None" }
                                                                }
                                                            }
                                                        }
                                                        $TableParams = @{
                                                            Name = "DRS Cluster Groups - $Cluster"
                                                            ColumnWidths = 42, 16, 42
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $DrsGroups | Sort-Object 'DRS Cluster Group', 'Type' | Table @TableParams
                                                    }
                                                    #endregion vSphere DRS Cluster Group Section

                                                    #region vSphere DRS Cluster VM/Host Rules
                                                    $DrsVMHostRules = $Cluster | Get-DrsVMHostRule
                                                    if ($DrsVMHostRules) {
                                                        Section -Style NOTOCHeading5 -ExcludeFromTOC 'DRS VM/Host Rules' {
                                                            $DrsVMHostRuleDetail = foreach ($DrsVMHostRule in $DrsVMHostRules) {
                                                                [PSCustomObject]@{
                                                                    'DRS VM/Host Rule' = $DrsVMHostRule.Name
                                                                    'Type' = Switch ($DrsVMHostRule.Type) {
                                                                        'MustRunOn' { 'Must run on hosts in group' }
                                                                        'ShouldRunOn' { 'Should run on hosts in group' }
                                                                        'MustNotRunOn' { 'Must not run on hosts in group' }
                                                                        'ShouldNotRunOn' { 'Should not run on hosts in group' }
                                                                        default { $DrsVMHostRule.Type }
                                                                    }
                                                                    'Enabled' = Switch ($DrsVMHostRule.Enabled) {
                                                                        $true { 'Yes' }
                                                                        $False { 'No' }
                                                                    }
                                                                    'VM Group' = $DrsVMHostRule.VMGroup
                                                                    'Host Group' = $DrsVMHostRule.VMHostGroup
                                                                }
                                                            }
                                                            if ($Healthcheck.Cluster.DrsVMHostRules) {
                                                                $DrsVMHostRuleDetail | Where-Object { $_.Enabled -eq 'No' } | Set-Style -Style Warning -Property 'Enabled'
                                                            }
                                                            $TableParams = @{
                                                                Name = "DRS VM/Host Rules - $Cluster"
                                                                ColumnWidths = 22, 22, 12, 22, 22
                                                            }
                                                            if ($Report.ShowTableCaptions) {
                                                                $TableParams['Caption'] = "- $($TableParams.Name)"
                                                            }
                                                            $DrsVMHostRuleDetail | Sort-Object 'DRS VM/Host Rule' | Table @TableParams
                                                        }
                                                    }
                                                    #endregion vSphere DRS Cluster VM/Host Rules

                                                    #region vSphere DRS Cluster Rules
                                                    $DrsRules = $Cluster | Get-DrsRule
                                                    if ($DrsRules) {
                                                        #region vSphere DRS Cluster Rules Section
                                                        Section -Style NOTOCHeading5 -ExcludeFromTOC 'DRS Rules' {
                                                            $DrsRuleDetail = foreach ($DrsRule in $DrsRules) {
                                                                [PSCustomObject]@{
                                                                    'DRS Rule' = $DrsRule.Name
                                                                    'Type' = Switch ($DrsRule.Type) {
                                                                        'VMAffinity' { 'Keep Vitrual Machines Together' }
                                                                        'VMAntiAffinity' { 'Separate Virtual Machines' }
                                                                    }
                                                                    'Enabled' = Switch ($DrsRule.Enabled) {
                                                                        $true { 'Yes' }
                                                                        $False { 'No' }
                                                                    }
                                                                    'Mandatory' = $DrsRule.Mandatory
                                                                    'Virtual Machines' = ($DrsRule.VMIds | ForEach-Object { (Get-View -Id $_).name }) -join ', '
                                                                }
                                                                if ($Healthcheck.Cluster.DrsRules) {
                                                                    $DrsRuleDetail | Where-Object { $_.Enabled -eq 'No' } | Set-Style -Style Warning -Property 'Enabled'
                                                                }
                                                            }
                                                            $TableParams = @{
                                                                Name = "DRS Rules - $Cluster"
                                                                ColumnWidths = 26, 25, 12, 12, 25
                                                            }
                                                            if ($Report.ShowTableCaptions) {
                                                                $TableParams['Caption'] = "- $($TableParams.Name)"
                                                            }
                                                            $DrsRuleDetail | Sort-Object Type | Table @TableParams
                                                        }
                                                        #endregion vSphere DRS Cluster Rules Section
                                                    }
                                                    #endregion vSphere DRS Cluster Rules
                                                }
                                                #endregion vSphere DRS Cluster Group

                                                #region Cluster VM Overrides
                                                $DrsVmOverrides = $Cluster.ExtensionData.Configuration.DrsVmConfig
                                                $DasVmOverrides = $Cluster.ExtensionData.Configuration.DasVmConfig
                                                if ($DrsVmOverrides -or $DasVmOverrides) {
                                                    #region VM Overrides Section
                                                    Section -Style NOTOCHeading4 -ExcludeFromTOC 'VM Overrides' {
                                                        #region vSphere DRS VM Overrides
                                                        if ($DrsVmOverrides) {
                                                            Section -Style NOTOCHeading5 -ExcludeFromTOC 'vSphere DRS' {
                                                                $DrsVmOverrideDetails = foreach ($DrsVmOverride in $DrsVmOverrides) {
                                                                    [PSCustomObject]@{
                                                                        'Virtual Machine' = $VMLookup."$($DrsVmOverride.Key.Type)-$($DrsVmOverride.Key.Value)"
                                                                        'vSphere DRS Automation Level' = if ($DrsVmOverride.Enabled -eq $false) {
                                                                            'Disabled'
                                                                        } else {
                                                                            Switch ($DrsVmOverride.Behavior) {
                                                                                'manual' { 'Manual' }
                                                                                'partiallyAutomated' { 'Partially Automated' }
                                                                                'fullyAutomated' { 'Fully Automated' }
                                                                                default { $DrsVmOverride.Behavior }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                                $TableParams = @{
                                                                    Name = "DRS VM Overrides - $Cluster"
                                                                    ColumnWidths = 50, 50
                                                                }
                                                                if ($Report.ShowTableCaptions) {
                                                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                }
                                                                $DrsVmOverrideDetails | Sort-Object 'Virtual Machine' | Table @TableParams
                                                            }
                                                        }
                                                        #endregion vSphere DRS VM Overrides

                                                        #region vSphere HA VM Overrides
                                                        if ($DasVmOverrides) {
                                                            Section -Style NOTOCHeading5 -ExcludeFromTOC 'vSphere HA' {
                                                                $DasVmOverrideDetails = foreach ($DasVmOverride in $DasVmOverrides) {
                                                                    [PSCustomObject]@{
                                                                        'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)"
                                                                        'VM Restart Priority' = Switch ($DasVmOverride.DasSettings.RestartPriority) {
                                                                            $null { '--' }
                                                                            'lowest' { 'Lowest' }
                                                                            'low' { 'Low' }
                                                                            'medium' { 'Medium' }
                                                                            'high' { 'High' }
                                                                            'highest' { 'Highest' }
                                                                            'disabled' { 'Disabled' }
                                                                            'clusterRestartPriority' { 'Cluster default' }
                                                                        }
                                                                        'VM Dependency Restart Condition Timeout' = Switch ($DasVmOverride.DasSettings.RestartPriorityTimeout) {
                                                                            $null { '--' }
                                                                            '-1' { 'Disabled' }
                                                                            default { "$($DasVmOverride.DasSettings.RestartPriorityTimeout) seconds" }
                                                                        }
                                                                        'Host Isolation Response' = Switch ($DasVmOverride.DasSettings.IsolationResponse) {
                                                                            $null { '--' }
                                                                            'none' { 'Disabled' }
                                                                            'powerOff' { 'Power off and restart VMs' }
                                                                            'shutdown' { 'Shutdown and restart VMs' }
                                                                            'clusterIsolationResponse' { 'Cluster default' }
                                                                        }
                                                                    }
                                                                }
                                                                $TableParams = @{
                                                                    Name = "HA VM Overrides - $Cluster"
                                                                    ColumnWidths = 25, 25, 25, 25
                                                                }
                                                                if ($Report.ShowTableCaptions) {
                                                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                }
                                                                $DasVmOverrideDetails | Sort-Object 'Virtual Machine' | Table @TableParams

                                                                #region PDL/APD Protection Settings Section
                                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'PDL/APD Protection Settings' {
                                                                    $DasVmOverridePdlApd = foreach ($DasVmOverride in $DasVmOverrides) {
                                                                        $DasVmComponentProtection = $DasVmOverride.DasSettings.VmComponentProtectionSettings
                                                                        [PSCustomObject]@{
                                                                            'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)"
                                                                            'PDL Failure Response' = Switch ($DasVmComponentProtection.VmStorageProtectionForPDL) {
                                                                                $null { '--' }
                                                                                'clusterDefault' { 'Cluster default' }
                                                                                'warning' { 'Issue events' }
                                                                                'restartAggressive' { 'Power off and restart VMs' }
                                                                                'disabled' { 'Disabled' }
                                                                            }
                                                                            'APD Failure Response' = Switch ($DasVmComponentProtection.VmStorageProtectionForAPD) {
                                                                                $null { '--' }
                                                                                'clusterDefault' { 'Cluster default' }
                                                                                'warning' { 'Issue events' }
                                                                                'restartConservative' { 'Power off and restart VMs - Conservative restart policy' }
                                                                                'restartAggressive' { 'Power off and restart VMs - Aggressive restart policy' }
                                                                                'disabled' { 'Disabled' }
                                                                            }
                                                                            'VM Failover Delay' = Switch ($DasVmComponentProtection.VmTerminateDelayForAPDSec) {
                                                                                $null { '--' }
                                                                                '-1' { 'Disabled' }
                                                                                default { "$(($DasVmComponentProtection.VmTerminateDelayForAPDSec)/60) minutes" }
                                                                            }
                                                                            'Response Recovery' = Switch ($DasVmComponentProtection.VmReactionOnAPDCleared) {
                                                                                $null { '--' }
                                                                                'reset' { 'Reset VMs' }
                                                                                'disabled' { 'Disabled' }
                                                                                'useClusterDefault' { 'Cluster default' }
                                                                            }
                                                                        }
                                                                    }
                                                                    $TableParams = @{
                                                                        Name = "HA VM Overrides PDL/APD Settings - $Cluster"
                                                                        ColumnWidths = 20, 20, 20, 20, 20
                                                                    }
                                                                    if ($Report.ShowTableCaptions) {
                                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                    }
                                                                    $DasVmOverridePdlApd | Sort-Object 'Virtual Machine' | Table @TableParams
                                                                }
                                                                #endregion PDL/APD Protection Settings Section

                                                                #region VM Monitoring Section
                                                                Section -Style NOTOCHeading5 -ExcludeFromTOC 'VM Monitoring' {
                                                                    $DasVmOverrideVmMonitoring = foreach ($DasVmOverride in $DasVmOverrides) {
                                                                        $DasVmMonitoring = $DasVmOverride.DasSettings.VmToolsMonitoringSettings
                                                                        [PSCustomObject]@{
                                                                            'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)"
                                                                            'VM Monitoring' = Switch ($DasVmMonitoring.VmMonitoring) {
                                                                                $null { '--' }
                                                                                'vmMonitoringDisabled' { 'Disabled' }
                                                                                'vmMonitoringOnly' { 'VM Monitoring Only' }
                                                                                'vmAndAppMonitoring' { 'VM and App Monitoring' }
                                                                            }
                                                                            'Failure Interval' = Switch ($DasVmMonitoring.FailureInterval) {
                                                                                $null { '--' }
                                                                                default {
                                                                                    if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') {
                                                                                        '--'
                                                                                    } else {
                                                                                        "$($DasVmMonitoring.FailureInterval) seconds"
                                                                                    }
                                                                                }
                                                                            }
                                                                            'Minimum Uptime' = Switch ($DasVmMonitoring.MinUptime) {
                                                                                $null { '--' }
                                                                                default {
                                                                                    if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') {
                                                                                        '--'
                                                                                    } else {
                                                                                        "$($DasVmMonitoring.MinUptime) seconds"
                                                                                    }
                                                                                }
                                                                            }
                                                                            'Maximum Per-VM Resets' = Switch ($DasVmMonitoring.MaxFailures) {
                                                                                $null { '--' }
                                                                                default {
                                                                                    if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') {
                                                                                        '--'
                                                                                    } else {
                                                                                        $DasVmMonitoring.MaxFailures
                                                                                    }
                                                                                }
                                                                            }
                                                                            'Maximum Resets Time Window' = Switch ($DasVmMonitoring.MaxFailureWindow) {
                                                                                $null { '--' }
                                                                                '-1' { 'No window' }
                                                                                default {
                                                                                    if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') {
                                                                                        '--'
                                                                                    } else {
                                                                                        "Within $(($DasVmMonitoring.MaxFailureWindow)/3600) hrs"
                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                    $TableParams = @{
                                                                        Name = "HA VM Overrides VM Monitoring - $Cluster"
                                                                        ColumnWidths = 40, 12, 12, 12, 12, 12
                                                                    }
                                                                    if ($Report.ShowTableCaptions) {
                                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                    }
                                                                    $DasVmOverrideVmMonitoring | Sort-Object 'Virtual Machine' | Table @TableParams
                                                                }
                                                                #endregion VM Monitoring Section
                                                            }
                                                        }
                                                        #endregion vSphere HA VM Overrides
                                                    }
                                                    #endregion VM Overrides Section
                                                }
                                                #endregion Cluster VM Overrides

                                                #region Cluster VUM Baselines
                                                if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') {
                                                    if ($VUMConnection) {
                                                        if ("Desktop" -eq $PSVersionTable.PsEdition) {
                                                            $ClusterPatchBaselines = $Cluster | Get-PatchBaseline
                                                        } else {
                                                            Write-PScriboMessage 'Cluster VUM baseline information is not currently available with your version of PowerShell.'
                                                        }
                                                        if ($ClusterPatchBaselines) {
                                                            Section -Style Heading4 'Update Manager Baselines' {
                                                                $ClusterBaselines = foreach ($ClusterBaseline in $ClusterPatchBaselines) {
                                                                    [PSCustomObject]@{
                                                                        'Baseline' = $ClusterBaseline.Name
                                                                        'Description' = $ClusterBaseline.Description
                                                                        'Type' = $ClusterBaseline.BaselineType
                                                                        'Target Type' = $ClusterBaseline.TargetType
                                                                        'Last Update Time' = ($ClusterBaseline.LastUpdateTime).ToLocalTime()
                                                                        '# of Patches' = $ClusterBaseline.CurrentPatches.Count
                                                                    }
                                                                }
                                                                $TableParams = @{
                                                                    Name = "Update Manager Baselines - $Cluster"
                                                                    ColumnWidths = 25, 25, 10, 10, 20, 10
                                                                }
                                                                if ($Report.ShowTableCaptions) {
                                                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                }
                                                                $ClusterBaselines | Sort-Object 'Baseline' | Table @TableParams
                                                            }
                                                        }
                                                        if ($Healthcheck.Cluster.VUMCompliance) {
                                                            $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Unknown' } | Set-Style -Style Warning
                                                            $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Not Compliant' -or $_.Status -eq 'Incompatible' } | Set-Style -Style Critical
                                                        }
                                                        $TableParams = @{
                                                            Name = "Update Manager Compliance - $Cluster"
                                                            ColumnWidths = 25, 50, 25
                                                        }
                                                        if ($Report.ShowTableCaptions) {
                                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                                        }
                                                        $ClusterComplianceInfo | Sort-Object Name, Baseline | Table @TableParams
                                                    }
                                                } else {
                                                    Write-PScriboMessage "Insufficient user privileges to report Cluster baselines. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned."
                                                }
                                                #endregion Cluster VUM Baselines

                                                #region Cluster VUM Compliance (Advanced Detail Information)
                                                if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') {
                                                    if ($InfoLevel.Cluster -ge 4 -and $VumServer.Name) {
                                                        if ("Desktop" -eq $PSVersionTable.PsEdition) {
                                                            $ClusterCompliances = $Cluster | Get-Compliance
                                                        } else {
                                                            Write-PScriboMessage 'Cluster VUM compliance information is not currently available with your version of PowerShell.'
                                                        }
                                                        if ($ClusterCompliances) {
                                                            Section -Style Heading4 'Update Manager Compliance' {
                                                                $ClusterComplianceInfo = foreach ($ClusterCompliance in $ClusterCompliances) {
                                                                    [PSCustomObject]@{
                                                                        'Entity' = $ClusterCompliance.Entity
                                                                        'Baseline' = $ClusterCompliance.Baseline.Name
                                                                        'Status' = Switch ($ClusterCompliance.Status) {
                                                                            'NotCompliant' { 'Not Compliant' }
                                                                            default { $ClusterCompliance.Status }
                                                                        }
                                                                    }
                                                                }
                                                                if ($Healthcheck.Cluster.VUMCompliance) {
                                                                    $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Unknown' } | Set-Style -Style Warning
                                                                    $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Not Compliant' -or $_.Status -eq 'Incompatible' } | Set-Style -Style Critical
                                                                }
                                                                $TableParams = @{
                                                                    Name = "Update Manager Compliance - $Cluster"
                                                                    ColumnWidths = 25, 50, 25
                                                                }
                                                                if ($Report.ShowTableCaptions) {
                                                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                                                }
                                                                $ClusterComplianceInfo | Sort-Object Entity, Baseline | Table @TableParams
                                                            }
                                                        }
                                                    }
                                                } else {
                                                    Write-PScriboMessage "Insufficient user privileges to report Cluster compliance. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned."
                                                }

                                                #endregion Cluster VUM Compliance (Advanced Detail Information)

                                                #region Cluster Permissions
                                                Section -Style NOTOCHeading4 -ExcludeFromTOC 'Permissions' {
                                                    Paragraph "The following table details the permissions assigned to cluster $Cluster."
                                                    BlankLine
                                                    $VIPermissions = $Cluster | Get-VIPermission
                                                    $ClusterVIPermissions = foreach ($VIPermission in $VIPermissions) {
                                                        [PSCustomObject]@{
                                                            'User/Group' = $VIPermission.Principal
                                                            'Is Group?' = Switch ($VIPermission.IsGroup) {
                                                                $true { 'Yes' }
                                                                $false { 'No' }
                                                            }
                                                            'Role' = $VIPermission.Role
                                                            'Defined In' = $VIPermission.Entity
                                                            'Propagate' = Switch ($VIPermission.Propagate) {
                                                                $true { 'Yes' }
                                                                $false { 'No' }
                                                            }
                                                        }
                                                    }
                                                    $TableParams = @{
                                                        Name = "Permissions - $Cluster"
                                                        ColumnWidths = 42, 12, 20, 14, 12
                                                    }
                                                    if ($Report.ShowTableCaptions) {
                                                        $TableParams['Caption'] = "- $($TableParams.Name)"
                                                    }
                                                    $ClusterVIPermissions | Sort-Object 'User/Group' | Table @TableParams
                                                }
                                                #endregion Cluster Permissions
                                            }
                                        }
                                        #endregion vSphere DRS Cluster Configuration
                                    }
                                    #endregion Cluster Section
                                }
                            }
                            #endregion Cluster Detailed Information
                        }
                        #endregion Cluster Section
                    }
                }
                #endregion Clusters

                #region Resource Pool Section
                Write-PScriboMessage "ResourcePool InfoLevel set at $($InfoLevel.ResourcePool)."
                if ($InfoLevel.ResourcePool -ge 1) {
                    $ResourcePools = Get-ResourcePool -Server $vCenter | Sort-Object Parent, Name
                    if ($ResourcePools) {
                        #region Resource Pools Section
                        Section -Style Heading2 'Resource Pools' {
                            Paragraph "The following sections detail the configuration of resource pools managed by vCenter Server $vCenterServerName."
                            #region Resource Pool Advanced Summary
                            if ($InfoLevel.ResourcePool -le 2) {
                                BlankLine
                                $ResourcePoolInfo = foreach ($ResourcePool in $ResourcePools) {
                                    [PSCustomObject]@{
                                        'Resource Pool' = $ResourcePool.Name
                                        'Parent' = $ResourcePool.Parent
                                        'CPU Shares Level' = $ResourcePool.CpuSharesLevel
                                        'CPU Reservation MHz' = $ResourcePool.CpuReservationMHz
                                        'CPU Limit MHz' = Switch ($ResourcePool.CpuLimitMHz) {
                                            '-1' { 'Unlimited' }
                                            default { $ResourcePool.CpuLimitMHz }
                                        }
                                        'Memory Shares Level' = $ResourcePool.MemSharesLevel
                                        'Memory Reservation' = [math]::Round($ResourcePool.MemReservationGB, 2)
                                        'Memory Limit GB' = Switch ($ResourcePool.MemLimitGB) {
                                            '-1' { 'Unlimited' }
                                            default { [math]::Round($ResourcePool.MemLimitGB, 2) }
                                        }
                                    }
                                }
                                $TableParams = @{
                                    Name = "Resource Pool Summary - $($vCenterServerName)"
                                    ColumnWidths = 20, 20, 10, 10, 10, 10, 10, 10
                                }
                                if ($Report.ShowTableCaptions) {
                                    $TableParams['Caption'] = "- $($TableParams.Name)"
                                }
                                $ResourcePoolInfo | Sort-Object Name | Table @TableParams
                            }
                            #endregion Resource Pool Advanced Summary

                            #region Resource Pool Detailed Information
                            # TODO: Test Tags
                            if ($InfoLevel.ResourcePool -ge 3) {
                                foreach ($ResourcePool in $ResourcePools) {
                                    Section -Style Heading3 $ResourcePool.Name {
                                        $ResourcePoolDetail = [PSCustomObject]@{
                                            'Resource Pool' = $ResourcePool.Name
                                            'ID' = $ResourcePool.Id
                                            'Parent' = $ResourcePool.Parent
                                            'CPU Shares Level' = $ResourcePool.CpuSharesLevel
                                            'Number of CPU Shares' = $ResourcePool.NumCpuShares
                                            'CPU Reservation' = "$($ResourcePool.CpuReservationMHz) MHz"
                                            'CPU Expandable Reservation' = Switch ($ResourcePool.CpuExpandableReservation) {
                                                $true { 'Enabled' }
                                                $false { 'Disabled' }
                                            }
                                            'CPU Limit MHz' = Switch ($ResourcePool.CpuLimitMHz) {
                                                '-1' { 'Unlimited' }
                                                default { "$($ResourcePool.CpuLimitMHz) MHz" }
                                            }
                                            'Memory Shares Level' = $ResourcePool.MemSharesLevel
                                            'Number of Memory Shares' = $ResourcePool.NumMemShares
                                            'Memory Reservation' = "$([math]::Round($ResourcePool.MemReservationGB, 2)) GB"
                                            'Memory Expandable Reservation' = Switch ($ResourcePool.MemExpandableReservation) {
                                                $true { 'Enabled' }
                                                $false { 'Disabled' }
                                            }
                                            'Memory Limit' = Switch ($ResourcePool.MemLimitGB) {
                                                '-1' { 'Unlimited' }
                                                default { "$([math]::Round($ResourcePool.MemLimitGB, 2)) GB" }
                                            }
                                            'Number of VMs' = $ResourcePool.ExtensionData.VM.Count
                                        }
                                        <#
                                        $MemberProps = @{
                                            'InputObject' = $ResourcePoolDetail
                                            'MemberType' = 'NoteProperty'
                                        }
 
                                        if ($TagAssignments | Where-Object {$_.entity -eq $ResourcePool}) {
                                            Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $ResourcePool}).Tag -join ',')
                                        }
                                        #>

                                        #region Resource Pool Advanced Detail Information
                                        if ($InfoLevel.ResourcePool -ge 4) {
                                            $ResourcePoolDetail | ForEach-Object {
                                                # Query for VMs by resource pool Id
                                                $ResourcePoolId = $_.Id
                                                $ResourcePoolVMs = $VMs | Where-Object { $_.ResourcePoolId -eq $ResourcePoolId } | Sort-Object Name
                                                Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Virtual Machines' -Value ($ResourcePoolVMs.Name -join ', ')
                                            }
                                        }
                                        #endregion Resource Pool Advanced Detail Information
                                        $TableParams = @{
                                            Name = "Resource Pool Configuration - $($ResourcePool.Name)"
                                            List = $true
                                            ColumnWidths = 50, 50
                                        }
                                        if ($Report.ShowTableCaptions) {
                                            $TableParams['Caption'] = "- $($TableParams.Name)"
                                        }
                                        $ResourcePoolDetail | Table @TableParams
                                    }
                                }
                            }
                            #endregion Resource Pool Detailed Information
                        }
                        #endregion Resource Pools Section
                    }
                }
                #endregion Resource Pool Section

                #region ESXi VMHost Section
                Write-PScriboMessage "VMHost InfoLevel set at $($InfoLevel.VMHost)."
                if ($InfoLevel.VMHost -ge 1) {
                    if ($VMHosts) {
                        #region Hosts Section
                        Section -Style Heading2 'Hosts' {
                            Paragraph "The following sections detail the configuration of VMware ESXi hosts managed by vCenter Server $vCenterServerName."
                            #region ESXi Host Advanced Summary
                            if ($InfoLevel.VMHost -le 2) {
                                BlankLine
                                $VMHostInfo = foreach ($VMHost in $VMHosts) {
                                    [PSCustomObject]@{
                                        'Host' = $VMHost.Name
                                        'Version' = $VMHost.Version
                                        'Build' =<