VMware.CloudFoundation.Reporting.psm1
# Copyright 2023-2024 Broadcom. All Rights Reserved. # SPDX-License-Identifier: BSD-2 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Enable communication with self-signed certificates when using Powershell Core. If you require all communications # to be secure and do not wish to allow communication with self-signed certificates, remove lines 19-39 before # importing the module. if ($PSEdition -eq 'Core') { $PSDefaultParameterValues.Add("Invoke-RestMethod:SkipCertificateCheck", $true) [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null } if ($PSEdition -eq 'Desktop') { # Allow communication with self-signed certificates when using Windows PowerShell [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null if ("TrustAllCertificatePolicy" -as [type]) {} else { Add-Type @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertificatePolicy : ICertificatePolicy { public TrustAllCertificatePolicy() {} public bool CheckValidationResult( ServicePoint sPoint, X509Certificate certificate, WebRequest wRequest, int certificateProblem) { return true; } } "@ [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertificatePolicy } } ########################################################################## #Region Non Exported Functions ###### Function Get-Password { param ( [string]$user, [string]$password ) if ([string]::IsNullOrEmpty($password)) { $secureString = Read-Host -Prompt "Enter the password for $user" -AsSecureString $password = ConvertFrom-SecureString $secureString -AsPlainText } return $password } #EndRegion Non Exported Functions ###### ########################################################################## ####################################################################################################################### ##################################### J S O N O U T P U T V A R I A B L E S ##################################### Set-Variable -Name "backupJsonSuffix" -value "backup-status.json" -scope global Set-Variable -Name "nsxtTransportJsonSuffix" -value "nsxttransportnode-status.json" -scope global Set-Variable -Name "nsxttntunnelJsonSuffix" -value "nsxttntunnel-status.json" -scope global Set-Variable -Name "nsxttier0bgpJsonSuffix" -value "nsxttier0bgp-status.json" -scope global Set-Variable -Name "snapshotJsonSuffix" -value "snapshot-status.json" -scope global Set-Variable -Name "localuserexpiryJsonSuffix" -value "localuserexpiry-status.json" -scope global Set-Variable -Name "storageCapacityHealthJsonSuffix" -value "storagecapacityhealth-status.json" -scope global Set-Variable -Name "componentConnectivityHealthNonSOSJsonSuffix" -value "componentconnectivityhealthnonsos-status.json" -scope global Set-Variable -Name "nsxtCombinedHealthNonSOSJsonSuffix" -value "nsxtcombinedhealthnonsos-status.json" -scope global Set-Variable -Name "cdRomJsonSuffix" -value "cdrom-status.json" -scope global Set-Variable -Name "esxiConnectionHealthJsonSuffix" -value "esxi-connection-status.json" -scope global Set-Variable -Name "sddcFreePoolJsonSuffix" -value "sddc-manager-free-pool-status.json" -scope global ############################## E N D O F J S O N O U T P U T V A R I A B L E S ############################## ####################################################################################################################### ####################################################################################################################### ################################## C O M P O N E N T S I Z E V A R I A B L E S ################################## Set-Variable -Name "vcsaSize" -Value '[ { "name": "tiny", "resources": { "cpu": 2, "memory": 12 }, "maximum": { "hosts": 10, "vms": 100 } }, { "name": "small", "resources": { "cpu": 4, "memory": 19 }, "maximum": { "hosts": 100, "vms": 1000 } }, { "name": "medium", "resources": { "cpu": 8, "memory": 28 }, "maximum": { "hosts": 400, "vms": 4000 } }, { "name": "large", "resources": { "cpu": 16, "memory": 37 }, "maximum": { "hosts": 1000, "vms": 10000 } }, { "name": "extra-large", "resources": { "cpu": 24, "memory": 56 }, "maximum": { "hosts": 3000, "vms": 30000 } } ]' -Scope Global Set-Variable -Name 'nsxLocalManagerSize' -Value '[ { "name": "extra-small", "resources": { "cpu": 2, "memory": 8 } }, { "name": "small", "resources": { "cpu": 4, "memory": 16 } }, { "name": "medium", "resources": { "cpu": 6, "memory": 24 } }, { "name": "large", "resources": { "cpu": 12, "memory": 48 } } ]' -Scope Global Set-Variable -Name 'nsxEdgeSize' -Value '[ { "name": "small", "resources": { "cpu": 2, "memory": 4 } }, { "name": "medium", "resources": { "cpu": 4, "memory": 8 } }, { "name": "large", "resources": { "cpu": 8, "memory": 32 } }, { "name": "extra-large", "resources": { "cpu": 16, "memory": 64 } } ]' -Scope Global ########################### E N D O F C O M P O N E N T S I Z E V A R I A B L E S ########################### ####################################################################################################################### ####################################################################################################################### ############################# C O M B I N E D O P E R A T I O N S F U N C T I O N S ############################ Function Invoke-VcfHealthReport { <# .SYNOPSIS Perform health checks .DESCRIPTION The Invoke-VcfHealthReport provides a single cmdlet to perform health checks across a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfHealthReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -sddcManagerLocalUser vcf -sddcManagerLocalPass VMw@re1! -reportPath F:\Reporting -allDomains This example runs a health check across a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfHealthReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -sddcManagerLocalUser vcf -sddcManagerLocalPass VMw@re1! -reportPath F:\Reporting -workloadDomain sfo-w01 This example runs a health check for a specific workload domain within a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfHealthReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -sddcManagerLocalUser vcf -sddcManagerLocalPass VMw@re1! -reportPath F:\Reporting -allDomains -failureOnly This example runs a health check across a VMware Cloud Foundation instance but only ouputs issues to the HTML report. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. .PARAMETER sddcManagerLocalUser The username to authenticate to the SDDC Manager appliance. .PARAMETER sddcManagerLocalPass The password to authenticate to the SDDC Manager appliance. .PARAMETER reportPath The path to save the policy report. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER darkMode Switch to enable dark mode for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerLocalUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerLocalPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$darkMode ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass $sddcManagerLocalPass = Get-Password -user $sddcManagerLocalUser -password $sddcManagerLocalPass Try { Clear-Host; Write-Host "" if ($PSVersionTable.PSEdition -eq "Desktop" -or $PSVersionTable.OS -like "Microsoft Windows*") { $tarPath = (Get-Command tar -ErrorAction SilentlyContinue).Source if (!($tarPath)) { Write-Warning "The tar utility is required to run this cmdlet. Please check the module system requirements and try again." return } } if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $defaultReport = Start-CreateReportDirectory -path $reportPath -reportType health # Setup Report Location and Report File if (!(Test-Path -Path $reportPath)) {Write-Warning "Unable to locate report path $reportPath, enter a valid path and try again"; Write-Host ""; Break } if ($PsBoundParameters.ContainsKey("allDomains")) { $reportname = $defaultReport.Split('-health')[0] + "-health-" + $sddcManagerFqdn.Split(".")[0] + ".htm" $reportData = "<h1>SDDC Manager: $sddcManagerFqdn</h1>" $workflowMessage = "VMware Cloud Foundation instance ($sddcManagerFqdn)" $commandSwitch = "-allDomains" } else { $reportname = $defaultReport.Split('.')[0] + "-" + $workloadDomain + ".htm" $reportData = "<h1>Workload Domain: $workloadDomain</h1>" $workflowMessage = "Workload Domain ($workloadDomain)" $commandSwitch = "-workloadDomain $workloadDomain" } if ($PsBoundParameters.ContainsKey('failureOnly')) { $failureOnlySwitch = " -failureOnly" } $vcfVersion = ((Get-VCFManager).version).Split('-')[0] Start-SetupLogFile -Path $reportPath -ScriptName $MyInvocation.MyCommand.Name # Setup Log Location and Log File Write-LogMessage -Type INFO -Message "Starting the process of creating a Health Report for $workflowMessage." -Colour Yellow Write-LogMessage -Type INFO -Message "Setting up the log file to path $logfile." Write-LogMessage -Type INFO -Message "Setting up report folder and report $reportName." Write-LogMessage -Type INFO -Message "Running an SoS Health Check for $workflowMessage, process takes time." $jsonFilePath = Invoke-Expression "Request-SoSHealthJson -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass -reportPath $reportFolder $($commandSwitch)" # Generating the Service Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the Service Health Report using the SoS output for $workflowMessage." $serviceHtml = Invoke-Expression "Publish-ServiceHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $serviceHtml # Generating the Version Health Data Using the SoS Data if ($vcfVersion -ge "4.5.0") { Write-LogMessage -Type INFO -Message "Generating the Version Health Report using the SoS output for $workflowMessage." $versionHtml = Invoke-Expression "Publish-VersionHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $versionHtml } # Generating the Hardware Compatibility Health Data Using the SoS Data Write-LogMessage -type INFO -Message "Generating the Hardware Compatibility Health Report using the SoS output for $workflowMessage." $hardwareCompatibilityHtml = Invoke-Expression "Publish-HardwareCompatibilityHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $hardwareCompatibilityHtml # Generating the Connectivity Health Data Using SoS Data and Supplemental PowerShell Request Functions Write-LogMessage -Type INFO -Message "Generating the Connectivity Health Report using the SoS output for $workflowMessage." $componentConnectivityHtml = Invoke-Expression "Publish-ComponentConnectivityHealth -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass -json $jsonFilePath $($commandSwitch) $($failureOnlySwitch)"; $reportData += $componentConnectivityHtml # Generating the Password Expiry Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the Password Expiry Report for $workflowMessage." $passwordHtml = Invoke-Expression "Publish-PasswordHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $passwordHtml # Generating the Certificate Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the Certificate Health Report using the SoS output for $workflowMessage." $certificateHtml = Invoke-Expression "Publish-CertificateHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $certificateHtml # Generating the Backup Status Health Data Using PowerShell Request Functions Write-LogMessage -Type INFO -Message "Generating the Backup Status Report for $workflowMessage." $backupStatusHtml = Invoke-Expression "Publish-BackupStatus -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $backupStatusHtml # Generating the Snapshot Status Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the Snapshots Report for $workflowMessage." $snapshotStatusHtml = Invoke-Expression "Publish-SnapshotStatus -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $snapshotStatusHtml # Generating the DNS Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the DNS Health Report using the SoS output for $workflowMessage." $dnsHtml = Invoke-Expression "Publish-DnsHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $dnsHtml # Generating the NTP Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the NTP Health Report using the SoS output for $workflowMessage." $ntpHtml = Invoke-Expression "Publish-NtpHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $ntpHtml # Generating the vCenter Server Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the vCenter Server Health Report using the SoS output for $workflowMessage." $vcenterHtml = Invoke-Expression "Publish-VcenterHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $vcenterHtml # Generating the ESXi Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the ESXi Health Report using the SoS output for $workflowMessage." $esxiHtml = Invoke-Expression "Publish-EsxiHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $esxiHtml # Generating the ESXi Connection Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the ESXi Connection Health Data report for $workflowMessage." $esxiConnectionHtml = Invoke-Expression "Publish-EsxiConnectionHealth -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $esxiConnectionHtml # Generating the Free Pool Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the SDDC Manager Free Pool Health for $workflowMessage." $freePoolHtml = Invoke-Expression "Publish-SddcManagerFreePool -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($failureOnlySwitch)"; $reportData += $freePoolHtml # Generating the vSAN Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the vSAN Health Report using the SoS output for $workflowMessage." $vsanHtml = Invoke-Expression "Publish-VsanHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $vsanHtml # Generating the vSAN Storage Policy Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the vSAN Storage Policy Health Report using the SoS output for $workflowMessage." $vsanPolicyHtml = Invoke-Expression "Publish-VsanStoragePolicy -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $vsanPolicyHtml # Generating the NSX Manager Health Data Using SoS output and Supplemental PowerShell Request Functions Write-LogMessage -Type INFO -Message "Generating the NSX Health Report using the SoS output for $workflowMessage." $nsxtHtml = Invoke-Expression "Publish-NsxtCombinedHealth -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass -json $jsonFilePath $($commandSwitch) $($failureOnlySwitch)"; $reportData += $nsxtHtml # Generating the NSX Edge Cluster Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the NSX Edge Cluster Health Report using the SoS output for $workflowMessage." $nsxtEdgeClusterHtml = Invoke-Expression "Publish-NsxtEdgeClusterHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $nsxtEdgeClusterHtml # Generating the NSX Edge Node Health Data Using the SoS Data Write-LogMessage -Type INFO -Message "Generating the NSX Edge Node Health Report using the SoS output for $workflowMessage." $nsxtEdgeNodeHtml = Invoke-Expression "Publish-NsxtEdgeNodeHealth -json $jsonFilePath -html $($failureOnlySwitch)"; $reportData += $nsxtEdgeNodeHtml # Generating the NSX Transport Node Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the NSX Transport Node Report for $workflowMessage." $nsxTransportNodeHtml = Invoke-Expression "Publish-NsxtTransportNodeStatus -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $nsxTransportNodeHtml # Generating the NSX Transport Node Tunnel Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the NSX Transport Node Tunnel Report for $workflowMessage." $nsxTransportNodeTunnelHtml = Invoke-Expression "Publish-NsxtTransportNodeTunnelStatus -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $nsxTransportNodeTunnelHtml # Generating the NSX Tier-0 Gateway BGP Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the NSX Tier-0 Gateway BGP Report for $workflowMessage." $nsxTier0BgpHtml = Invoke-Expression "Publish-NsxtTier0BgpStatus -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $nsxTier0BgpHtml # Generating the Disk Capacity Health Data Using PowerShell Request Functions Write-LogMessage -Type INFO -Message "Generating the Disk Capacity Report for $workflowMessage.'" $storageCapacityHealthHtml = Invoke-Expression "Publish-StorageCapacityHealth -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass -localUser $sddcManagerLocalUser -localPass $sddcManagerLocalPass $($commandSwitch) $($failureOnlySwitch)"; $reportData += $storageCapacityHealthHtml # Generating the Virtual Machines with Connected CD-ROM Health Data Using PowerShell Request Functions Write-LogMessage -type INFO -Message "Generating the Virtual Machines with Connected CD-ROM Report for $workflowMessage." $vmConnectedCdromHtml = Invoke-Expression "Publish-VmConnectedCdrom -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $vmConnectedCdromHtml if ($PsBoundParameters.ContainsKey("darkMode")) { $reportHeader = Get-ClarityReportHeader -dark } else { $reportHeader = Get-ClarityReportHeader } $reportNavigation = Get-ClarityReportNavigation -reportType health $reportFooter = Get-ClarityReportFooter $report = $reportHeader $report += $reportNavigation $report += $reportData $report += $reportFooter # Generate the report to an HTML file and then open it in the default browser Write-LogMessage -Type INFO -Message "Generating the final report and saving to ($reportName)." $report | Out-File $reportName if ($PSEdition -eq "Core" -and ($PSVersionTable.OS).Split(' ')[0] -ne "Linux") { Invoke-Item $reportName } elseif ($PSEdition -eq "Desktop") { Invoke-Item $reportName } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Invoke-VcfHealthReport Function Invoke-VcfAlertReport { <# .SYNOPSIS Generates the alert report for a VMware Cloud Foundation instance. .DESCRIPTION The Invoke-VcfAlertReport provides a single cmdlet to generates the alert report for a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfAlertReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -allDomains This example generates the alert report across a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfAlertReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -allDomains -failureOnly This example generates the alert report across a VMware Cloud Foundation instance but for only failed items. .EXAMPLE Invoke-VcfAlertReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -workloadDomain sfo-w01 This example generates the alert report for a specific workload domain in a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfAlertReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -workloadDomain sfo-w01 -failureOnly This example generates the alert report for a specific workload domain in a VMware Cloud Foundation instance but for only failed items. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. .PARAMETER reportPath The path to save the policy report. .PARAMETER allDomains Switch to run against all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER darkMode Switch to enable dark mode for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$darkMode ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass Try { Clear-Host; Write-Host "" if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $defaultReport = Start-CreateReportDirectory -path $reportPath -reportType alert # Setup Report Location and Report File if (!(Test-Path -Path $reportPath)) {Write-Warning "Unable to locate report path $reportPath, enter a valid path and try again"; Write-Host ""; Break } if ($PsBoundParameters.ContainsKey("allDomains")) { $reportname = $defaultReport.Split('-alert')[0] + "-alert-" + $sddcManagerFqdn.Split(".")[0] + ".htm" $reportData = "<h1>SDDC Manager: $sddcManagerFqdn</h1>" $workflowMessage = "VMware Cloud Foundation instance ($sddcManagerFqdn)" $commandSwitch = "-allDomains" } else { $reportname = $defaultReport.Split('.')[0] + "-" + $workloadDomain + ".htm" $reportData = "<h1>Workload Domain: $workloadDomain</h1>" $workflowMessage = "Workload Domain ($workloadDomain)" $commandSwitch = "-workloadDomain $workloadDomain" } if ($PsBoundParameters.ContainsKey('failureOnly')) { $commandSwitch = $commandSwitch + " -failureOnly" } Start-SetupLogFile -Path $reportPath -ScriptName $MyInvocation.MyCommand.Name # Setup Log Location and Log File Write-LogMessage -Type INFO -Message "Starting the process of creating an Alert Report for $workflowMessage." -Colour Yellow Write-LogMessage -Type INFO -Message "Setting up the log file to path $logfile." Write-LogMessage -Type INFO -Message "Setting up report folder and report $reportName." # Generate vCenter Server Alerts Using PowerShell Function Write-LogMessage -Type INFO -Message "Generating the vCenter Server alerts for $workflowMessage." $vCenterAlertHtml = Invoke-Expression "Publish-VcenterAlert -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $vCenterAlertHtml # Generate ESXi Alerts Using PowerShell Function Write-LogMessage -type INFO -Message "Generating the ESXi host alerts for $workflowMessage." $esxiAlertHtml = Invoke-Expression "Publish-EsxiAlert -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $esxiAlertHtml # Generate vSAN Alerts Using PowerShell Function Write-LogMessage -type INFO -Message "Generating the vSAN alerts for $workflowMessage." $vsanAlertHtml = Invoke-Expression "Publish-VsanAlert -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $vsanAlertHtml # Generate NSX Alerts Using PowerShell Function Write-LogMessage -type INFO -Message "Generating the NSX alerts for $workflowMessage." $nsxtAlertHtml = Invoke-Expression "Publish-NsxtAlert -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $nsxtAlertHtml if ($PsBoundParameters.ContainsKey("darkMode")) { $reportHeader = Get-ClarityReportHeader -dark } else { $reportHeader = Get-ClarityReportHeader } $reportNavigation = Get-ClarityReportNavigation -reportType alert $reportFooter = Get-ClarityReportFooter $report = $reportHeader $report += $reportNavigation $report += $reportData $report += $reportFooter # Generate the report to an HTML file and then open it in the default browser Write-LogMessage -Type INFO -Message "Generating the final report and saving to ($reportName)." $report | Out-File $reportName if ($PSEdition -eq "Core" -and ($PSVersionTable.OS).Split(' ')[0] -ne "Linux") { Invoke-Item $reportName } elseif ($PSEdition -eq "Desktop") { Invoke-Item $reportName } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Invoke-VcfAlertReport Function Invoke-VcfConfigReport { <# .SYNOPSIS Generates the configuration report .DESCRIPTION The Invoke-VcfConfigReport provides a single cmdlet to generates a configuration report for a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfConfigReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -allDomains This example generates the configuration report across a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfConfigReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -workloadDomain sfo-w01 This example generates the configuration report for a specific workload domain within a VMware Cloud Foundation instance. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. .PARAMETER reportPath The path to save the policy report. .PARAMETER allDomains Switch to run against all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER darkMode Switch to enable dark mode for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$darkMode ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass Try { Clear-Host; Write-Host "" if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $defaultReport = Start-CreateReportDirectory -path $reportPath -reportType config # Setup Report Location and Report File if (!(Test-Path -Path $reportPath)) {Write-Warning "Unable to locate report path $reportPath, enter a valid path and try again"; Write-Host ""; Break } if ($PsBoundParameters.ContainsKey("allDomains")) { $reportname = $defaultReport.Split('-config')[0] + "-config-" + $sddcManagerFqdn.Split(".")[0] + ".htm" $reportData = "<h1>SDDC Manager: $sddcManagerFqdn</h1>" $workflowMessage = "VMware Cloud Foundation instance ($sddcManagerFqdn)" $commandSwitch = "-allDomains" } else { $reportname = $defaultReport.Split('.')[0] + "-" + $workloadDomain + ".htm" $reportData = "<h1>Workload Domain: $workloadDomain</h1>" $workflowMessage = "Workload Domain ($workloadDomain)" $commandSwitch = "-workloadDomain $workloadDomain" } Start-SetupLogFile -Path $reportPath -ScriptName $MyInvocation.MyCommand.Name # Setup Log Location and Log File Write-LogMessage -Type INFO -Message "Starting the Process of Creating a Configuration Report for $workflowMessage." -Colour Yellow Write-LogMessage -Type INFO -Message "Setting up the log file to path $logfile." Write-LogMessage -Type INFO -Message "Setting up report folder and report $reportName." # Collecting Cluster Configuration Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the Cluster Configuration for $workflowMessage." $clusterConfigHtml = Invoke-Expression "Publish-ClusterConfiguration -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $clusterConfigHtml # Collecting Cluster DRS Rules Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the DRS Rule Configuration for $workflowMessage." $clusterDrsRuleHtml = Invoke-Expression "Publish-ClusterDrsRule -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $clusterDrsRuleHtml # Collecting Resource Pool Details Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the Resouce Pool Configuration for $workflowMessage." $resourcePoolsHtml = Invoke-Expression "Publish-ResourcePool -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $resourcePoolsHtml # Collecting VM Overrides Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the VM Override Configuration for $workflowMessage." $vmOverridesHtml = Invoke-Expression "Publish-VmOverride -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $vmOverridesHtml # Collecting Virtual Networking Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the Virtual Networking Configuration for $workflowMessage." $virtualNetworkHtml = Invoke-Expression "Publish-VirtualNetwork -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $virtualNetworkHtml # Collecting ESXi Security Configuration Using PowerShell Functions Write-LogMessage -Type INFO -Message "Generating the ESXi Security Configuration for $workflowMessage." $esxiSecurityHtml = Invoke-Expression "Publish-EsxiSecurityConfiguration -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass $($commandSwitch)"; $reportData += $esxiSecurityHtml if ($PsBoundParameters.ContainsKey("darkMode")) { $reportHeader = Get-ClarityReportHeader -dark } else { $reportHeader = Get-ClarityReportHeader } $reportNavigation = Get-ClarityReportNavigation -reportType config $reportFooter = Get-ClarityReportFooter $report = $reportHeader $report += $reportNavigation $report += $reportData $report += $reportFooter # Generate the report to an HTML file and then open it in the default browser Write-LogMessage -Type INFO -Message "Generating the Final Report and Saving to ($reportName)." $report | Out-File $reportName if ($PSEdition -eq "Core" -and ($PSVersionTable.OS).Split(' ')[0] -ne "Linux") { Invoke-Item $reportName } elseif ($PSEdition -eq "Desktop") { Invoke-Item $reportName } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Invoke-VcfConfigReport Function Invoke-VcfUpgradePrecheck { <# .SYNOPSIS Perform upgrade precheck .DESCRIPTION The Invoke-VcfUpgradePrecheck runs an upgrade precheck for a workload domain .EXAMPLE Invoke-VcfUpgradePrecheck -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -workloadDomain sfo-w01 This example runs a health check for a specific workload domain within an SDDC Manager instance. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. .PARAMETER reportPath The path to save the policy report. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER darkMode Switch to enable dark mode for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (ParameterSetName = 'Specific--WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$darkMode ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass Try { Clear-Host; Write-Host "" if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $defaultReport = Start-CreateReportDirectory -path $reportPath -reportType upgrade # Setup Report Location and Report File if (!(Test-Path -Path $reportPath)) {Write-Warning "Unable to locate report path $reportPath, enter a valid path and try again"; Write-Host ""; Break } $reportname = $defaultReport.Split('-upgrade')[0] + "-upgrade-" + $workloadDomain + ".htm" $workflowMessage = "Workload Domain ($workloadDomain)" Start-SetupLogFile -Path $reportPath -ScriptName $MyInvocation.MyCommand.Name # Setup Log Location and Log File Write-LogMessage -Type INFO -Message "Starting the Process of Running an Upgrade Precheck for $workflowMessage." -Colour Yellow Write-LogMessage -Type INFO -Message "Setting up the log file to path $logfile." Write-LogMessage -Type INFO -Message "Setting up report folder and report $reportName." if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $jsonSpec = '{ "resources" : [ { "resourceId" : "'+ (Get-VCFWorkloadDomain | Where-Object {$_.name -eq $workloadDomain}).id+'", "type" : "DOMAIN" } ] }' $task = Start-VCFSystemPrecheck -json $jsonSpec Write-LogMessage -Type INFO -Message "Waiting for Upgrade Precheck Task ($($task.name)) with Id ($($task.id)) to Complete." Do { $status = Get-VCFSystemPrecheckTask -id $task.id } While ($status.status -eq "IN_PROGRESS") Write-LogMessage -Type INFO -Message "Task ($($task.name)) with Task Id ($($task.id)) completed with status ($($status.status))." $allChecksObject = New-Object System.Collections.ArrayList foreach ($subTask in $status.subTasks) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $subTask.resources.type if ($subTask.resources.type -eq "ESX") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFHost -id $subTask.resources.resourceId).fqdn } elseif ($subTask.resources.type -eq "VCENTER") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFvCenter -id $subTask.resources.resourceId).fqdn } elseif ($subTask.resources.type -eq "CLUSTER") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFCluster -id $subTask.resources.resourceId).name } elseif ($subTask.resources.type -eq "VSAN") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFCluster -id $subTask.resources.resourceId).primaryDatastoreName } elseif ($subTask.resources.type -eq "DEPLOYMENT_CONFIGURATION") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFManager -id $subTask.resources.resourceId).fqdn } elseif ($subTask.resources.type -eq "VRSLCM") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFvRSLCM -id $subTask.resources.resourceId).fqdn } elseif ($subTask.resources.type -eq "VROPS") { $id = $subTask.resources.resourceId + ":vrops" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFvROPS | Where-Object {$_.id -eq $id}).loadBalancerFqdn } elseif ($subTask.resources.type -eq "VRLI") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFvRLI -id $subTask.resources.resourceId).loadBalancerFqdn } elseif ($subTask.resources.type -eq "VRA") { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue (Get-VCFvRA -id $subTask.resources.resourceId).loadBalancerFqdn } else { $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $subTask.resources.resourceId } $elementObject | Add-Member -NotePropertyName 'Precheck Task' -NotePropertyValue $subTask.name if ($subTask.status -eq "SUCCESSFUL") { $alert = "GREEN" } elseif ($subTask.status -eq "WARNING") { $alert = "YELLOW" } elseif ($subTask.status -eq "FAILED") { $alert = "RED" } $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $allChecksObject += $elementObject } $allChecksObject = $allChecksObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="upgrade-precheck"></a><h3>Upgrade Precheck</h3>' -As Table $allChecksObject = Convert-CssClass -htmldata $allChecksObject } } # Combine all information gathered into a single HTML report $reportData = "<h1>Workload Domain: $workloadDomain</h1>" $reportData += $allChecksObject if ($PsBoundParameters.ContainsKey("darkMode")) { $reportHeader = Get-ClarityReportHeader -dark } else { $reportHeader = Get-ClarityReportHeader } $reportNavigation = Get-ClarityReportNavigation -reportType upgrade $reportFooter = Get-ClarityReportFooter $report = $reportHeader $report += $reportNavigation $report += $reportData $report += $reportFooter # Generate the report to an HTML file and then open it in the default browser Write-LogMessage -Type INFO -Message "Generating the Final Report and Saving to ($reportName)." $report | Out-File $reportName if ($PSEdition -eq "Core" -and ($PSVersionTable.OS).Split(' ')[0] -ne "Linux") { Invoke-Item $reportName } elseif ($PSEdition -eq "Desktop") { Invoke-Item $reportName } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Invoke-VcfUpgradePrecheck Function Invoke-VcfOverviewReport { <# .SYNOPSIS Generates the system overview report .DESCRIPTION The Invoke-VcfOverviewReport provides a single cmdlet to generates a system overview report for a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfOverviewReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting This example generates the system overview report for a VMware Cloud Foundation instance. .EXAMPLE Invoke-VcfOverviewReport -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! -reportPath F:\Reporting -anonymized This example generates the system overview report for a VMware Cloud Foundation instance, but will anonymize the output. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. .PARAMETER reportPath The path to save the policy report. .PARAMETER darkMode Switch to enable dark mode for the report. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$darkMode, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass Try { Clear-Host; Write-Host "" if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $defaultReport = Start-CreateReportDirectory -path $reportPath -reportType overview # Setup Report Location and Report File if (!(Test-Path -Path $reportPath)) {Write-Warning "Unable to locate report path $reportPath, enter a valid path and try again"; Write-Host ""; Break } $reportname = $defaultReport.Split('-overview')[0] + "-overview-" + $sddcManagerFqdn.Split(".")[0] + ".htm" $workflowMessage = "VMware Cloud Foundation instance ($sddcManagerFqdn)" Start-SetupLogFile -Path $reportPath -ScriptName $MyInvocation.MyCommand.Name # Setup Log Location and Log File Write-LogMessage -Type INFO -Message "Starting the Process of Creating a System Overview Report for $workflowMessage." -Colour Yellow Write-LogMessage -Type INFO -Message "Setting up the log file to path $logfile." Write-LogMessage -Type INFO -Message "Setting up report folder and report $reportName." if ($PsBoundParameters.ContainsKey("anonymized")) { Write-LogMessage -Type INFO -Message "Generating Anonymized System Overview Report for $workflowMessage." $vcfOverviewHtml = Publish-VcfSystemOverview -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass -anonymized } else { Write-LogMessage -Type INFO -Message "Generating System Overview Report for $workflowMessage." $vcfOverviewHtml = Publish-VcfSystemOverview -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass } $reportData += $vcfOverviewHtml if ($PsBoundParameters.ContainsKey("darkMode")) { $reportHeader = Get-ClarityReportHeader -dark } else { $reportHeader = Get-ClarityReportHeader } $reportNavigation = Get-ClarityReportNavigation -reportType overview $reportFooter = Get-ClarityReportFooter $report = $reportHeader $report += $reportNavigation $report += $reportData $report += $reportFooter # Generate the report to an HTML file and then open it in the default browser Write-LogMessage -Type INFO -Message "Generating the Final Report and Saving to ($reportName)." $report | Out-File $reportName if ($PSEdition -eq "Core" -and ($PSVersionTable.OS).Split(' ')[0] -ne "Linux") { Invoke-Item $reportName } elseif ($PSEdition -eq "Desktop") { Invoke-Item $reportName } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Invoke-VcfOverviewReport ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ####################################################################################################################### ############################# S O S J S O N E X T R A C T I O N F U N C T I O N S ############################ Function Request-SoSHealthJson { <# .SYNOPSIS Run SoS and save the JSON output. .DESCRIPTION The Request-SoSHealthJson cmdlet connects to SDDC Manager, runs an SoS Health collection to JSON, and saves the JSON file to the local file system. .EXAMPLE Request-SoSHealthJson -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -reportPath F:\Reporting\HealthReports -allDomains This example runs an SoS Health collection for all domains in the SDDC and saves the JSON output to the local file system. .EXAMPLE Request-SoSHealthJson -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -reportPath F:\Reporting\HealthReports -workloadDomain sfo-w01 This example runs an SoS Health collection for a workload domain in the SDDC and saves the JSON output to the local file system. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER reportPath The path to save the policy report. .PARAMETER allDomains Switch to run against all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$reportPath, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -user $user -password $pass # Request VCF Token Request-VCFToken -fqdn $server -Username $user -Password $pass -skipCertificateCheck -ErrorAction SilentlyContinue -ErrorVariable ErrMsg | Out-Null $vcfVersion = ((Get-VCFManager).version).Split('-')[0] # Build Default Health Summary Check POST payload $healthChecksPayload = New-Object -TypeName psobject $healthChecksPayload | Add-Member -notepropertyname 'certificateHealth' -notepropertyvalue $true if ($vcfVersion -lt "5.2.0") {$healthChecksPayload | Add-Member -notepropertyname 'composabilityHealth' -notepropertyvalue $true } $healthChecksPayload | Add-Member -notepropertyname 'computeHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'connectivityHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'dnsHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'generalHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'hardwareCompatibilityHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'ntpHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'passwordHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'servicesHealth' -notepropertyvalue $true $healthChecksPayload | Add-Member -notepropertyname 'storageHealth' -notepropertyvalue $true if ($vcfVersion -ge "4.5.0") { $healthChecksPayload | Add-Member -notepropertyname 'versionHealth' -notepropertyvalue $true } $optionsConfigPayload = New-Object -TypeName psobject $optionsConfigPayload | Add-Member -notepropertyname 'force' -notepropertyvalue $false $optionsConfigPayload | Add-Member -notepropertyname 'skipKnownHostCheck' -notepropertyvalue $true $optionsIncludePayload = New-Object -TypeName psobject if ($vcfVersion -ge "4.5.0") { $optionsIncludePayload | Add-Member -notepropertyname 'precheckReport' -notepropertyvalue $false } $optionsIncludePayload | Add-Member -notepropertyname 'summaryReport' -notepropertyvalue $false $optionsPayload = New-Object -TypeName psobject $optionsPayload | Add-Member -notepropertyname 'config' -notepropertyvalue $optionsConfigPayload $optionsPayload | Add-Member -notepropertyname 'include' -notepropertyvalue $optionsIncludePayload $scopeDomainsPayload = New-Object -TypeName psobject $scopeDomainsPayload | Add-Member -notepropertyname 'clusterNames' -notepropertyvalue @() $scopeDomainsPayload | Add-Member -notepropertyname 'domainName' -notepropertyvalue "" $scopeDomainsPayload.clusterNames += @("") $domainsArray = @() $domainsArray += $scopeDomainsPayload $scopePayload = New-Object -TypeName psobject $scopePayload | Add-Member -notepropertyname 'domains' -notepropertyvalue @() $scopePayload | Add-Member -notepropertyname 'includeAllDomains' -notepropertyvalue $false $scopePayload | Add-Member -notepropertyname 'includeFreeHosts' -notepropertyvalue $false $scopePayload.domains += $domainsArray $healthSummarySpec = New-Object -TypeName psobject $healthSummarySpec | Add-Member -notepropertyname 'healthChecks' -notepropertyvalue $healthChecksPayload $healthSummarySpec | Add-Member -notepropertyname 'options' -notepropertyvalue $optionsPayload $healthSummarySpec | Add-Member -notepropertyname 'scope' -notepropertyvalue $scopePayload Try { # Set the suffix for the report name. $reportSuffix = "-all-health-results.json" # Set the report name and scope. if ($PsBoundParameters.ContainsKey("allDomains")) { $healthSummarySpec.scope.includeAllDomains = $true $reportName = $server.Split(".")[0] + $reportSuffix } elseif ($PsBoundParameters.ContainsKey("workloadDomain")) { $healthSummarySpec.scope.domains[0].domainName = $workloadDomain $reportName = $workloadDomain + $reportSuffix } # Set the report destination. $reportDestination = Join-Path -Path $reportPath -ChildPath $reportName if (Test-VCFConnection -server $server) { # Create a temporary directory under reportDirectory. $createPathCounter = 0 for ($createPathCounter -lt 4) { $randomOutput = -join (((48..57)+(65..90)+(97..122)) * 80 | Get-Random -Count 6 |%{[char]$_}) $outFilePath = Join-Path -Path $reportPath -ChildPath $randomOutput if (!(Test-Path -Path $outFilePath) -and (Test-Path -Path $reportPath)) { Break } else { if ($createPathCounter -eq 3) { Write-Error "Unable to write to $reportPath." } $createPathCounter++ } } New-Item -Path $outFilePath -ItemType Directory | Out-Null # Use REST API method to request the health summary. $healthSummaryPayloadJson = Join-Path -Path $outFilePath -ChildPath "healthSummaryPayload.json" ConvertTo-JSON $healthSummarySpec -Depth 10 | Out-File $healthSummaryPayloadJson $response = Start-VCFHealthSummary -json $healthSummaryPayloadJson if ($response.id -eq "") { Write-Error "The Health Summary request encountered an issue. Please try again." Return $false } $requestID = $response.id # Retrieve the request status. $response = Get-VCFHealthSummaryTask -id $requestID $escapeCounter = 0 while (($escapeCounter -lt 30) -and !($response.status -Match "COMPLETED")) { sleep(30) $escapeCounter++ $response = Get-VCFHealthSummaryTask -id $requestID } if ($escapeCounter -eq 20) { Write-Error " The Health Summary request is taking an unusual amount of time to complete." Return $false } # Download the health summary bundle file to a temporary directory. Request-VCFHealthSummaryBundle -id $requestID $prefix = "health-summary" $outFile = Join-Path -Path $outFilePath -ChildPath ($prefix + ".tar.gz") $savedFile = ($prefix + "-" + $requestID + ".tar") if (Test-Path -Path $savedFile) { # Copy the saved file to the output file. Copy-Item -Path $savedFile -Destination $outFile -Force | Out-Null # Remove the saved file. Remove-Item -Force $savedFile | Out-Null } else { Write-Error "An error was encountered downloading the health summary bundle." Return $false } # Extract health-results.json from tar.gz file. tar -xzf $outFile -C $outFilePath | Out-Null # Find health-results.json file and copy to report destination. $healthSummaryFile = "health-results.json" $healthSummaryPath = Get-ChildItem -Recurse -Filter $healthSummaryFile -Path $outFilePath $healthSummaryFile = Join-Path -Path $healthSummaryPath.DirectoryName -ChildPath $healthSummaryFile Copy-Item -Path $healthSummaryFile -Destination $reportDestination | Out-Null # Convert to JSON. $temp = Get-Content -Path $reportDestination; $temp = $temp -replace '""', '"-"'; $temp | Out-File $reportDestination Return $reportDestination } } Catch { Debug-CatchWriter -object $_ } Finally { # Always remove the temporary directory and its contents to free up disk space. Try { if (Test-Path -Path $outFilePath) { Remove-Item -Path $outFilePath -Recurse -Force | Out-Null } } Catch { Debug-CatchWriter -object $_ } } } Export-ModuleMember -Function Request-SoSHealthJson Function Publish-CertificateHealth { <# .SYNOPSIS Formats the Certificate Health data from the SoS JSON output. .DESCRIPTION The Publish-CertificateHealth cmdlet formats the Certificate Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-CertificateHealth -json <file-name> This example extracts and formats the Certificate Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-CertificateHealth -json <file-name> -html This example extracts and formats the Certificate Health data as an HTML object from the JSON file. .EXAMPLE Publish-CertificateHealth -json <file-name> -failureOnly This example extracts and formats the Certificate Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $customObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.'Certificates'.'Certificate Status' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'Certificate Status data not found in the JSON file: SKIPPED' } else { foreach ($component in $jsonInputData.PsObject.Properties.Value) { foreach ($element in $component.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() if ($element.title -and $element.title -notcontains "-") { $current = [DateTime]::Now $expiry = [DateTime]::ParseExact(($element.title[2] -replace '\s+', ' '), "MMM d HH:mm:ss yyyy 'GMT'", $null).ToUniversalTime() $expires_in = ($expiry - $current).Days $alert = "GREEN" if ([int]$expires_in -le 15) { $alert = "RED" } elseif ([int]$expires_in -le 30) { $alert = "YELLOW" } $elementObject | Add-Member -NotePropertyName 'Expires In (Days)' -NotePropertyValue $expires_in $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert } else { $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert } $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } $outputObject += $customObject } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="security-certificate"></a><h3>Certificate Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="security-certificate"></a><h3>Certificate Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmlData $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-ntp"></a><h3>NTP Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Certificate Status data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-CertificateHealth Function Publish-ConnectivityHealth { <# .SYNOPSIS Formats the Connectivity Health data from the SoS JSON output. .DESCRIPTION The Publish-ConnectivityHealth cmdlet formats the Connectivity Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-ConnectivityHealth -json <file-name> This example extracts and formats the Connectivity Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-ConnectivityHealth -json <file-name> -html This example extracts and formats the Connectivity Health data as an HTML object from the JSON file. .EXAMPLE Publish-ConnectivityHealth -json <file-name> -failureOnly This example extracts and formats the Connectivity Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $customObject = New-Object System.Collections.ArrayList $jsonInputCheck = $targetContent.Connectivity.'Connectivity Status' # Extract Data from the provided SOS JSON if (($jsonInputCheck | Measure-Object).Count -lt 1) { Write-Warning 'Connectivity Status data not found in the JSON file: SKIPPED' } else { # ESXi SSH Status $jsonInputData = $targetContent.Connectivity.'Connectivity Status'.'ESXi SSH Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # ESXi API Status $jsonInputData = $targetContent.Connectivity.'Connectivity Status'.'ESXi API Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Additional Items Status $jsonInputData = $targetContent.Connectivity.'Connectivity Status' # Extract Data from the provided SOS JSON $jsonInputData.PSObject.Properties.Remove('ESXi SSH Status') $jsonInputData.PSObject.Properties.Remove('ESXi API Status') if ($PsBoundParameters.ContainsKey('failureOnly')) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Connectivity Status data not found.</p>' -As Table } $customObject } else { $customObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ConnectivityHealth Function Publish-PingConnectivityHealth { <# .SYNOPSIS Formats the Ping Connectivity Health data from the SoS JSON output. .DESCRIPTION The Publish-PingConnectivityHealth cmdlet formats the Ping Connectivity Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-PingConnectivityHealth -json <file-name> This example extracts and formats the Ping Connectivity Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-PingConnectivityHealth -json <file-name> -html This example extracts and formats the Ping Connectivity Health data as an HTML object from the JSON file. .EXAMPLE Publish-PingConnectivityHealth -json <file-name> -failureOnly This example extracts and formats the Ping Connectivity Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $customObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.Connectivity.'Ping Status' $pingStatusProperties = @('area', 'title', 'state', 'timestamp', 'message', 'status', 'alert') if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'Ping Status data not found in the JSON file: SKIPPED' } else { $esxiHosts = Get-VCFHost foreach ($esxiHost in $esxiHosts) { $fqdn = $esxiHost.fqdn $propertiesName = $jsonInputData.$fqdn.PSObject.Properties.Name if (($pingStatusProperties.contains($propertiesName))) { # do nothing } else { $jsonInputData.$fqdn = $jsonInputData.$fqdn.$propertiesName } } if ($PsBoundParameters.ContainsKey('failureOnly')) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly } else { $outputObject = Read-JsonElement -inputData $jsonInputData } $customObject += $outputObject } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-ping-connectivity"></a><h3>Ping Connectivity Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-ping-connectivity"></a><h3>Ping Connectivity Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-ping-connectivity"></a><h3>Ping Connectivity Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Ping Status data not found.</p>' -As Table } $customObject } else { $customObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-PingConnectivityHealth Function Publish-DnsHealth { <# .SYNOPSIS Formats the DNS Health data from the SoS JSON output. .DESCRIPTION The Publish-DnsHealth cmdlet formats the DNS Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-DnsHealth -json <file-name> This example extracts and formats the DNS Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-DnsHealth -json <file-name> -html This example extracts and formats the DNS Health data as an HTML object from the JSON file. .EXAMPLE Publish-DnsHealth -json <file-name> -failureOnly This example extracts and formats the DNS Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # Forward Lookup Health Status $allForwardLookupObject = New-Object System.Collections.ArrayList $jsonForwardLookupInput = $targetContent.'DNS lookup Status'.'Forward lookup Status' # Extract Data from the provided SOS JSON if (($jsonForwardLookupInput | Measure-Object).Count -lt 1) { Write-Warning 'Forward Lookup Status not found in the JSON file: SKIPPED' } else { if ($PsBoundParameters.ContainsKey("failureOnly")) { $allForwardLookupObject = Read-JsonElement -inputData $jsonForwardLookupInput -failureOnly # Call Function to Structure the Data for Report Output } else { $allForwardLookupObject = Read-JsonElement -inputData $jsonForwardLookupInput # Call Function to Structure the Data for Report Output } } # Reverse Lookup Health Status $allReverseLookupObject = New-Object System.Collections.ArrayList $jsonReverseLookupInput = $targetContent.'DNS lookup Status'.'Reverse lookup Status' # Extract Data from the provided SOS JSON if (($jsonReverseLookupInput | Measure-Object).Count -lt 1) { Write-Warning 'Reverse Lookup Status not found in the JSON file: SKIPPED' } else { if ($PsBoundParameters.ContainsKey("failureOnly")) { $allReverseLookupObject = Read-JsonElement -inputData $jsonReverseLookupInput -failureOnly # Call Function to Structure the Data for Report Output } else { $allReverseLookupObject = Read-JsonElement -inputData $jsonReverseLookupInput # Call Function to Structure the Data for Report Output } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonForwardLookupInput | Measure-Object).Count -gt 0) { if ($allForwardLookupObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allForwardLookupObject = $allForwardLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-forward"></a><h3>DNS Forward Lookup Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allForwardLookupObject = $allForwardLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-forward"></a><h3>DNS Forward Lookup Health Status</h3>' -As Table } $allForwardLookupObject = Convert-CssClass -htmldata $allForwardLookupObject } else { $allForwardLookupObject = $allForwardLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-forward"></a><h3>DNS Forward Lookup Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Forward Lookup Status data not found.</p>' -As Table } $allForwardLookupObject if (($jsonReverseLookupInput | Measure-Object).Count -gt 0) { if ($allReverseLookupObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allReverseLookupObject = $allReverseLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-reverse"></a><h3>DNS Reverse Lookup Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allReverseLookupObject = $allReverseLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-reverse"></a><h3>DNS Reverse Lookup Health Status</h3>' -As Table } $allReverseLookupObject = Convert-CssClass -htmldata $allReverseLookupObject } else { $allReverseLookupObject = $allReverseLookupObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-dns-reverse"></a><h3>DNS Reverse Lookup Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Reverse Lookup Status data not found.</p>' -As Table } $allReverseLookupObject } else { $allForwardLookupObject | Sort-Object Component, Resource $allReverseLookupObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-DnsHealth Function Publish-EsxiHealth { <# .SYNOPSIS Formats the ESXi Health data from the SoS JSON output. .DESCRIPTION The Publish-EsxiHealth cmdlet formats the ESXi Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-EsxiHealth -json <file-name> This example extracts and formats the ESXi Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-EsxiHealth -json <file-name> -html This example extracts and formats the ESXi Health data as an HTML object from the JSON file. .EXAMPLE Publish-EsxiHealth -json <file-name> -failureOnly This example extracts and formats the ESXi Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # ESXi Overall Health Status $allOverallHealthObject = New-Object System.Collections.ArrayList $jsonGeneralCheck = $targetContent.General # Extract Data from the provided SOS JSON if (($jsonGeneralCheck | Measure-Object).Count -lt 1) { Write-Warning 'General data not found in the JSON file: SKIPPED' } else { # ESXi Core Dump Status $allCoreDumpObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.General.'ESXi Core Dump Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $allCoreDumpObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $allCoreDumpObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Rep } } $jsonComputeCheck = $targetContent.Compute # Extract Data from the provided SOS JSON if (($jsonComputeCheck | Measure-Object).Count -lt 1) { Write-Warning 'Compute data not found in the JSON file: SKIPPED' } else { # ESXi Overall Health Status $jsonInputData = $targetContent.Compute.'ESXi Overall Health' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $allOverallHealthObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $allOverallHealthObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } # ESXi License Status $allLicenseObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.Compute.'ESXi License Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $allLicenseObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $allLicenseObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } # ESXi Disk Status $allDiskObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.Compute.'ESXi Disk Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey('failureOnly')) { $allDiskObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $allDiskObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonGeneralCheck | Measure-Object).Count -gt 0) { if ($allCoreDumpObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allCoreDumpObject = $allCoreDumpObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-coredump"></a><h3>ESXi Core Dump Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allCoreDumpObject = $allCoreDumpObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-coredump"></a><h3>ESXi Core Dump Health Status</h3>' -As Table } $allCoreDumpObject = Convert-CssClass -htmldata $allCoreDumpObject } else { $allCoreDumpObject = $allCoreDumpObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-coredump"></a><h3>ESXi Core Dump Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: ESXi Core Dump data not found.</p>' -As Table } $allCoreDumpObject if (($jsonComputeCheck | Measure-Object).Count -gt 0) { if ($allOverallHealthObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allOverallHealthObject = $allOverallHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-overall"></a><h3>ESXi Overall Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allOverallHealthObject = $allOverallHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-overall"></a><h3>ESXi Overall Health Status</h3>' -As Table } $allOverallHealthObject = Convert-CssClass -htmldata $allOverallHealthObject } else { $allOverallHealthObject = $allOverallHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-overall"></a><h3>ESXi Overall Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: ESXi Overall Health data not found.</p>' -As Table } $allOverallHealthObject if (($jsonComputeCheck | Measure-Object).Count -gt 0) { if ($allLicenseObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allLicenseObject = $allLicenseObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-license"></a><h3>ESXi License Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allLicenseObject = $allLicenseObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-license"></a><h3>ESXi License Health Status</h3>' -As Table } $allLicenseObject = Convert-CssClass -htmldata $allLicenseObject } else { $allLicenseObject = $allLicenseObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-license"></a><h3>ESXi License Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: ESXi License data not found.</p>' -As Table } $allLicenseObject if (($jsonComputeCheck | Measure-Object).Count -gt 0) { if ($allDiskObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allDiskObject = $allDiskObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-disk"></a><h3>ESXi Disk Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allDiskObject = $allDiskObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-disk"></a><h3>ESXi Disk Health Status</h3>' -As Table } $allDiskObject = Convert-CssClass -htmldata $allDiskObject } else { $allDiskObject = $allDiskObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="esxi-disk"></a><h3>ESXi Disk Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: ESXi Disk data not found.</p>' -As Table } $allDiskObject } else { $allCoreDumpObject | Sort-Object Component, Resource $allOverallHealthbject | Sort-Object Component, Resource $allLicenseObject | Sort-Object Component, Resource $allDiskObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-EsxiHealth Function Publish-NsxtHealth { <# .SYNOPSIS Formats the NSX Health data from the SoS JSON output. .DESCRIPTION The Publish-NsxtHealth cmdlet formats the NSX Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-NsxtHealth -json <file-name> This example extracts and formats the NSX Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-NsxtHealth -json <file-name> -html This example extracts and formats the NSX Health data as an HTML object from the JSON file. .EXAMPLE Publish-NsxtHealth -json <file-name> -failureOnly This example extracts and formats the NSX Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $customObject = New-Object System.Collections.ArrayList $jsonInputCheck = $targetContent.General.'NSX Health' # Extract Data from the provided SOS JSON if (($jsonInputCheck | Measure-Object).Count -lt 1) { Write-Warning 'NSX Health data not found in the JSON file: SKIPPED' } else { # NSX Manager Health $component = 'NSX Manager' $inputData = $targetContent.General.'NSX Health'.'NSX Manager' foreach ($element in $inputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } # NSX Container Cluster Health Status $component = 'NSX Container Cluster' $inputData = $targetContent.General.'NSX Health'.'NSX Container Cluster Health Status' foreach ($element in $inputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } # NSX Cluster Status $component = 'NSX Cluster Status' $inputData = $targetContent.General.'NSX Health'.'NSX Cluster Status' foreach ($resource in $inputData.PsObject.Properties.Value) { foreach ($element in $resource.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } # NSX Controllers Health $component = 'NSX Controllers' $inputData = $targetContent.General.'NSX Health'.'NSX Controllers' foreach ($resource in $inputData.PsObject.Properties.Value) { foreach ($element in $resource.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($customObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Resource, Component | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Resource, Component | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: NSX Health data not found.</p>' -As Table } $customObject } else { $customObject | Sort-Object Resource, Component } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtHealth Function Publish-NsxtEdgeNodeHealth { <# .SYNOPSIS Formats the NSX Edge Node Health data from the SoS JSON output. .DESCRIPTION The Publish-NsxtEdgeNodeHealth cmdlet formats the NSX Edge Node Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-NsxtEdgeNodeHealth -json <file-name> This example extracts and formats the NSX Edge Node Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-NsxtEdgeNodeHealth -json <file-name> -html This example extracts and formats the NSX Edge Node Health data as an HTML object from the JSON file. .EXAMPLE Publish-NsxtEdgeNodeHealth -json <file-name> -failureOnly This example extracts and formats the NSX Edge Node Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # NSX Edge Node Health $customObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.General.'NSX Health'.'NSX Edge' if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning "NSX Health data for NSX Edges not found in the JSON file: SKIPPED" } else { $nsxtClusters = Get-VCFNsxtCluster foreach ($nsxtVip in $nsxtClusters.vipFqdn) { $jsonInputData.PSObject.Properties.Remove($nsxtVip) } $jsonInputData = $jsonInputData | Where-Object {$_ -ne ""} foreach ($element in $jsonInputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue 'NSX Edge' $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue ($element.message -Split ('Following are the individual health stats'))[0] if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($customObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge"></a><h3>NSX Edge Node Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge"></a><h3>NSX Edge Node Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge"></a><h3>NSX Edge Node Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: NSX Health data for NSX Edges not found. This warning is safe to ignore if NSX Edges are not managed by SDDC Manager.</p>' -As Table } $customObject } else { $customObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtEdgeNodeHealth Function Publish-NsxtEdgeClusterHealth { <# .SYNOPSIS Formats the NSX Edge Cluster Health data from the SoS JSON output. .DESCRIPTION The Publish-NsxtEdgeClusterHealth cmdlet formats the NSX Edge Cluster Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-NsxtEdgeClusterHealth -json <file-name> This example extracts and formats the NSX Edge Cluster Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-NsxtEdgeClusterHealth -json <file-name> -html This example extracts and formats the NSX Edge Cluster Health data as an HTML object from the JSON file. .EXAMPLE Publish-NsxtEdgeClusterHealth -json <file-name> -failureOnly This example extracts and formats the NSX Edge Cluster Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # NSX Edge Cluster Health $customObject = New-Object System.Collections.ArrayList $jsonInputData = $targetContent.General.'NSX Health'.'NSX Edge' if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning "NSX Health data for NSX Edge Cluster not found in the JSON file: SKIPPED" } else { $nsxtEdgeClusters = Get-VCFEdgeCluster foreach ($nsxtEdgeNodes in $nsxtEdgeClusters.edgeNodes.hostname) { $jsonInputData.PSObject.Properties.Remove($nsxtEdgeNodes) } if ($null -eq $nsxtEdgeClusters) { $nsxtClusters = Get-VCFNsxtCluster foreach ($nsxtCluster in $nsxtClusters) { $jsonInputData.PSObject.Properties.Remove($nsxtCluster.vipFqdn) } } $jsonInputData = $jsonInputData | Where-Object {$_ -ne ""} foreach ($element in $jsonInputData.PsObject.Properties.Value) { foreach ($cluster in $element.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue 'NSX Edge Cluster' $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($cluster.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $cluster.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $cluster.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($customObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge-cluster"></a><h3>NSX Edge Cluster Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge-cluster"></a><h3>NSX Edge Cluster Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-edge-cluster"></a><h3>NSX Edge Cluster Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: NSX Health data for Edge Cluster not found. This warning is safe to ignore if NSX Edges are not managed by SDDC Manager.</p>' -As Table } $customObject } else { $customObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtEdgeClusterHealth Function Publish-NtpHealth { <# .SYNOPSIS Formats the NTP Health data from the SoS JSON output. .DESCRIPTION The Publish-NtpHealth cmdlet formats the NTP Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-NtpHealth -json <file-name> This example extracts and formats the NTP Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-NtpHealth -json <file-name> -html This example extracts and formats the NTP Health data as an HTML object from the JSON file. .EXAMPLE Publish-NtpHealth -json <file-name> -failureOnly This example extracts and formats the NTP Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # NTP Health Status $jsonInputData = $targetContent.'NTP' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'NTP data not found in the JSON file: SKIPPED' } else { $jsonInputData.PSObject.Properties.Remove('ESXi HW Time') $jsonInputData.PSObject.Properties.Remove('ESXi Time') if ($PsBoundParameters.ContainsKey('failureOnly')) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-ntp"></a><h3>NTP Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-ntp"></a><h3>NTP Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmldata $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="infra-ntp"></a><h3>NTP Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: NTP data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NtpHealth Function Publish-PasswordHealth { <# .SYNOPSIS Formats the Password Health data from the SoS JSON output. .DESCRIPTION The Publish-PasswordHealth cmdlet formats the Password Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-PasswordHealth -json <file-name> This example extracts and formats the Password Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-PasswordHealth -json <file-name> -html This example extracts and formats the Password Health data as an HTML object from the JSON file. .EXAMPLE Publish-PasswordHealth -json <file-name> -failureOnly This example extracts and formats the Password Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # Password Expiry Health $jsonInputData = $targetContent.'Password Expiry Status' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'Password Expiry Status not found in the JSON file: SKIPPED' } else { $outputObject = New-Object System.Collections.ArrayList foreach ($element in $jsonInputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'User' -NotePropertyValue $element.title.User $elementObject | Add-Member -NotePropertyName 'Expires In (Days)' -NotePropertyValue $element.title.expires_in $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $outputObject += $elementObject } } else { $outputObject += $elementObject } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="security-password"></a><h3>Password Expiry Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="security-password"></a><h3>Password Expiry Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmldata $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="security-password"></a><h3>Password Expiry Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Password Expiry Status data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-PasswordHealth Function Publish-VersionHealth { <# .SYNOPSIS Formats the Version Health data from the SoS JSON output. .DESCRIPTION The Publish-VersionHealth cmdlet formats the Version Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-VersionHealth -json <file-name> This example extracts and formats the Version Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-VersionHealth -json <file-name> -html This example extracts and formats the Version Health data as an HTML object from the JSON file. .EXAMPLE Publish-VersionHealth -json <file-name> -failureOnly This example extracts and formats the Version Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # Version Health $jsonInputData = $targetContent.'Version Check Status' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'Version Check Status not found in the JSON file: SKIPPED' } else { $outputObject = New-Object System.Collections.ArrayList foreach ($element in $jsonInputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Version' -NotePropertyValue $element.title[0] $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $outputObject += $elementObject } } else { $outputObject += $elementObject } } } if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-version"></a><h3>Version Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-version"></a><h3>Version Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmldata $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-version"></a><h3>Version Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Version data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VersionHealth Function Publish-HardwareCompatibilityHealth { <# .SYNOPSIS Formats the Hardware Compatibility data from the SoS JSON output. .DESCRIPTION The Publish-HardwareCompatibilityHealth cmdlet formats the Hardware Compatibility data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-HardwareCompatibilityHealth -json <file-name> This example extracts and formats the Hardware Compatibility data as a PowerShell object from the JSON file. .EXAMPLE Publish-HardwareCompatibilityHealth -json <file-name> -html This example extracts and formats the Hardware Compatibility data as an HTML object from the JSON file. .EXAMPLE Publish-HardwareCompatibilityHealth -json <file-name> -failureOnly This example extracts and formats the Hardware Compatibility data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # Hardware Compatibility $jsonInputData = $targetContent.'Hardware Compatibility' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'Hardware Compatibility not found in the JSON file: SKIPPED' } else { $outputObject = New-Object System.Collections.ArrayList foreach ($component in $jsonInputData.PsObject.Properties.Value) { foreach ($element in $component.PsObject.Properties.Value) { if ($element.title -ne '-' -and $element.alert -ne '-') { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $message = $element.message $fixupMessage = $message.Replace('is Successul', 'is successful') $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $fixupMessage if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $outputObject += $elementObject } } else { $outputObject += $elementObject } } } } } if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-hardware"></a><h3>Hardware Compatibility Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-hardware"></a><h3>Hardware Compatibility Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmlData $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-hardware"></a><h3>Hardware Compatibility Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Hardware compatibility data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-HardwareCompatibilityHealth Function Publish-ServiceHealth { <# .SYNOPSIS Formats the Service Health data from the SoS JSON output. .DESCRIPTION The Publish-ServiceHealth cmdlet formats the Service Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-ServiceHealth -json <file-name> This example extracts and formats the Service Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-ServiceHealth -json <file-name> -html This example extracts and formats the Service Health data as an HTML object from the JSON file. .EXAMPLE Publish-ServiceHealth -json <file-name> -failureOnly This example extracts and formats the Service Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $outputObject = New-Object System.Collections.ArrayList $inputData = $targetContent.'Services' # Extract Data from the provided SOS JSON if (($inputData | Measure-Object).Count -lt 1) { Write-Warning 'Services data not found in the JSON file: SKIPPED' } else { foreach ($component in $inputData) { foreach ($element in $component.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $elementObject | Add-Member -NotePropertyName 'Service Name' -NotePropertyValue $element.title.ToUpper() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $outputObject += $elementObject } } else { $outputObject += $elementObject } } } } if ($PsBoundParameters.ContainsKey('html')) { if (($inputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-service"></a><h3>Service Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-service"></a><h3>Service Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmldata $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-service"></a><h3>Service Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: Services data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ServiceHealth Function Publish-VcenterHealth { <# .SYNOPSIS Formats the vCenter Server Health data from the SoS JSON output. .DESCRIPTION The Publish-VcenterHealth cmdlet formats the vCenter Server Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-VcenterHealth -json <file-name> This example extracts and formats the vCenter Server Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-VcenterHealth -json <file-name> -html This example extracts and formats the vCenter Server Health data as an HTML object from the JSON file. .EXAMPLE Publish-VcenterHealth -json <file-name> -failureOnly This example extracts and formats the vCenter Server Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # vCenter Server Overall Health $jsonInputData = $targetContent.Compute.'vCenter Overall Health' # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning "vCenter Server Overall Health data not found in the JSON file: SKIPPED" } else { if ($PsBoundParameters.ContainsKey('failureOnly')) { $vcenterOverall = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $vcenterOverall = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } # Ring Topology Health $ringTopologyHealth = New-Object System.Collections.ArrayList $vcfVersion = ((Get-VCFManager).version -Split ('\.\d{1}\-\d{8}')) -split '\s+' -match '\S' if ($vcfVersion -eq "4.2.1") { $jsonInputData = $targetContent.Connectivity.'Vcenter Ring Topology Status'.'Vcenter Ring Topology Status' # Extract Data from the provided SOS JSON } else { $jsonInputData = $targetContent.General.'Vcenter Ring Topology Status'.'Vcenter Ring Topology Status' # Extract Data from the provided SOS JSON } $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -notepropertyname 'Component' -notepropertyvalue ($jsonInputData.area -SPlit ("SDDC:"))[-1].Trim() $elementObject | Add-Member -notepropertyname 'Alert' -notepropertyvalue $jsonInputData.alert $elementObject | Add-Member -notepropertyname 'Message' -notepropertyvalue $jsonInputData.message if ($PsBoundParameters.ContainsKey("failureOnly")) { if (($jsonInputData.status -eq "FAILED")) { $ringTopologyHealth += $elementObject } } else { $ringTopologyHealth += $elementObject } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($vcenterOverall.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $vcenterOverall = $vcenterOverall | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-overall"></a><h3>vCenter Server Overall Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $vcenterOverall = $vcenterOverall | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-overall"></a><h3>vCenter Server Overall Health Status</h3>' -As Table } $vcenterOverall = Convert-CssClass -htmldata $vcenterOverall } else { $vcenterOverall = $vcenterOverall | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-overall"></a><h3>vCenter Server Overall Health Status</h3>' -PostContent '<p><p><strong>WARNING</strong>: vCenter Server Overall Health data not found.</p>' -As Table } $vcenterOverall } else { $vcenterOverall | Sort-Object Component, Resource } if ($PsBoundParameters.ContainsKey('html')) { if (($jsonInputData | Measure-Object).Count -gt 0) { if (@($ringTopologyHealth).Count -lt 1) { $addNoIssues = $true } if ($addNoIssues) { $ringTopologyHealth = $ringTopologyHealth | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-ring-topology"></a><h3>vCenter Server Ring Topology Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $ringTopologyHealth = $ringTopologyHealth | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-ring-topology"></a><h3>vCenter Server Ring Topology Health Status</h3>' -As Table } $ringTopologyHealth = Convert-CssClass -htmldata $ringTopologyHealth } else { $ringTopologyHealth = $ringTopologyHealth | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vcenter-ring-topology"></a><h3>vCenter Server Ring Topology Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: vCenter Server Overall Health data not found.</p>' -As Table } $ringTopologyHealth } else { $ringTopologyHealth | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VcenterHealth Function Publish-VsanHealth { <# .SYNOPSIS Formats the vSAN Health data from the SoS JSON output. .DESCRIPTION The Publish-VsanHealth cmdlet formats the vSAN Health data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-VsanHealth -json <file-name> This example extracts and formats the vSAN Health data as a PowerShell object from the JSON file. .EXAMPLE Publish-VsanHealth -json <file-name> -html This example extracts and formats the vSAN Health data as an HTML object from the JSON file. .EXAMPLE Publish-VsanHealth -json <file-name> -failureOnly This example extracts and formats the vSAN Health data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } $customObject = New-Object System.Collections.ArrayList $jsonInputCheck = $targetContent.vSAN # Extract Data from the provided SOS JSON if (($jsonInputCheck | Measure-Object).Count -lt 1) { Write-Warning 'vSAN data not found in the JSON file: SKIPPED' } else { # Cluster Health Status $jsonInputData = $targetContent.vSAN.'Cluster vSAN Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Disk Status $jsonInputData = $targetContent.vSAN.'Cluster Disk Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Capacity Utilization $jsonInputData = $targetContent.vSAN.'vSAN Capacity Utilization' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Active ReSync Objects $jsonInputData = $targetContent.vSAN.'Active ReSync Objects' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Data Compression Status $jsonInputData = $targetContent.vSAN.'Cluster Data Compression Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Data Encryption Status $jsonInputData = $targetContent.vSAN.'Cluster Data Encryption Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Cluster Data Deduplication Status $jsonInputData = $targetContent.vSAN.'Cluster Data Deduplication Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Stretched Cluster Status $jsonInputData = $targetContent.vSAN.'Stretched Cluster Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Stretched Cluster Health Status $jsonInputData = $targetContent.vSAN.'Stretched Cluster Health Status' # Extract Data from the provided SOS JSON if ($PsBoundParameters.ContainsKey("failureOnly")) { $outputObject = Read-JsonElement -inputData $jsonInputData -failureOnly # Call Function to Structure the Data for Report Output } else { $outputObject = Read-JsonElement -inputData $jsonInputData # Call Function to Structure the Data for Report Output } $customObject += $outputObject # Adding individual component to main customObject # Stretched Cluster Tests $jsonInputData = $targetContent.vSAN.'Stretched Cluster Tests' # Extract Data from the provided SOS JSON foreach ($component in $jsonInputData.PsObject.Properties.Value) { foreach ($element in $component.PsObject.Properties.Value) { $outputObject = New-Object -TypeName psobject $outputObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue ($element.area -Split (':'))[0].Trim() $outputObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.area -Split (':'))[-1].Trim() $outputObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $outputObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $element.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $customObject += $outputObject } } else { $customObject += $outputObject # Adding individual component to main customObject } } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($customObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vsan-overall"></a><h3>vSAN Overall Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $customObject = $customObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vsan-overall"></a><h3>vSAN Overall Health Status</h3>' -As Table } $customObject = Convert-CssClass -htmldata $customObject } else { $customObject = $customObject | ConvertTo-Html -Fragment -PreContent '<a id="vsan-overall"></a><h3>vSAN Overall Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: vSAN data not found.</p>' } $customObject } else { $customObject | Sort-Object Component, Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VsanHealth Function Publish-VsanStoragePolicy { <# .SYNOPSIS Formats the vSAN Storage Policy for virtual machines from the SoS JSON output. .DESCRIPTION The Publish-VsanStoragePolicy cmdlet formats the vSAN Storage Policy data from the SoS JSON output and publishes it as either a standard PowerShell object or an HTML object. .EXAMPLE Publish-VsanStoragePolicy -json <file-name> This example extracts and formats the vSAN Storage Policy data as a PowerShell object from the JSON file. .EXAMPLE Publish-VsanStoragePolicy -json <file-name> -html This example extracts and formats the vSAN Storage Policy data as an HTML object from the JSON file. .EXAMPLE Publish-VsanStoragePolicy -json <file-name> -failureOnly This example extracts and formats the vSAN Storage Policy data as a PowerShell object from the JSON file for only the failed items. .PARAMETER json The path to the JSON file containing the SoS Health Summary data. .PARAMETER html Specifies that the output should be formatted as an HTML object. .PARAMETER failureOnly Specifies that the output should only contain failed items. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) Try { if (!(Test-Path -Path $json)) { Write-Error "Unable to find JSON file at location ($json)" -ErrorAction Stop } else { $targetContent = Get-Content $json | ConvertFrom-Json } # VSAN Storage Policy $jsonInputData = $targetContent.vSAN # Extract Data from the provided SOS JSON if (($jsonInputData | Measure-Object).Count -lt 1) { Write-Warning 'vSAN data not found in the JSON file: SKIPPED' } else { $jsonInputData.PSObject.Properties.Remove('Host vSAN Status') $jsonInputData.PSObject.Properties.Remove('Host Disk Status') $jsonInputData.PSObject.Properties.Remove('Cluster vSAN Status') $jsonInputData.PSObject.Properties.Remove('Cluster Disk Status') $jsonInputData.PSObject.Properties.Remove('vCenter HCL Status') $jsonInputData.PSObject.Properties.Remove('Cluster Data Compression Status') $jsonInputData.PSObject.Properties.Remove('Cluster Data Encryption Status') $jsonInputData.PSObject.Properties.Remove('Cluster Data Deduplication Status') $jsonInputData.PSObject.Properties.Remove('Stretched Cluster Status') $jsonInputData.PSObject.Properties.Remove('Stretched Cluster Health Status') $jsonInputData.PSObject.Properties.Remove('Stretched Cluster Tests') $outputObject = New-Object System.Collections.ArrayList foreach ($element in $jsonInputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "Virtual Machine" $elementObject | Add-Member -NotePropertyName 'vCenter Server' -NotePropertyValue (($element.area -Split (' : '))[-1] -Split (' VM '))[0].Trim() $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue ($element.Message -Split (" "),2)[0].Trim() $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $element.alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue ($element.Message -Split (" "),2)[-1].Trim() if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($element.status -eq 'FAILED')) { $outputObject += $elementObject } } else { $outputObject += $elementObject } } } # Return the structured data to the console or format using HTML CSS Styles if ($PsBoundParameters.ContainsKey("html")) { if (($jsonInputData | Measure-Object).Count -gt 0) { if ($outputObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $outputObject = $outputObject | Sort-Object Component, 'vCenter Server', Resource | ConvertTo-Html -Fragment -PreContent '<a id="vsan-spbm"></a><h3>vSAN Storage Policy Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $outputObject = $outputObject | Sort-Object Component, 'vCenter Server', Resource | ConvertTo-Html -Fragment -PreContent '<a id="vsan-spbm"></a><h3>vSAN Storage Policy Health Status</h3>' -As Table } $outputObject = Convert-CssClass -htmldata $outputObject } else { $outputObject = $outputObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="vsan-spbm"></a><h3>vSAN Storage Policy Health Status</h3>' -PostContent '<p><strong>WARNING</strong>: vSAN data not found.</p>' -As Table } $outputObject } else { $outputObject | Sort-Object Component, 'vCenter Server', Resource } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VsanStoragePolicy ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ####################################################################################################################### #################################### H E A L T H C H E C K F U N C T I O N S ################################### Function Publish-BackupStatus { <# .SYNOPSIS Request and publish the backup status. .DESCRIPTION The Publish-BackupStatus cmdlet checks the backup status for SDDC Manager, vCenter Server instances, and NSX Local Manager clusters in a VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs checks on the backup status and outputs the results - outputJson parameter takes in name of the folder to save the json. Filename is autogenerated. .EXAMPLE Publish-BackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the backup status for the SDDC Manager, vCenter Server instances, and NSX Local Manager clusters in a VMware Cloud Foundation instance. .EXAMPLE Publish-BackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish the backup status for the SDDC Manager, vCenter Server instances, and NSX Local Manager clusters in a VMware Cloud Foundation instance but only reports issues. .EXAMPLE Publish-BackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the backup status for the vCenter Server instances, and NSX Local Manager clusters in workload domain sfo-w01. .EXAMPLE Publish-BackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the backup status for the vCenter Server instances, and NSX Local Manager clusters in a VMware Cloud Foundation instance and saves it under F:\Reporting with filename <timestamp>-backup-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $singleWorkloadDomain = Get-VCFWorkloadDomain | Where-Object {$_.name -eq $workloadDomain} $allBackupStatusObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey("allDomains")) { $sddcManagerBackupStatus = Request-SddcManagerBackupStatus -server $server -user $user -pass $pass -failureOnly; $allBackupStatusObject += $sddcManagerBackupStatus foreach ($domain in $allWorkloadDomains ) { $vcenterBackupStatus = Request-vCenterBackupStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allBackupStatusObject += $vcenterBackupStatus $nsxtManagerBackupStatus = Request-NsxtManagerBackupStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allBackupStatusObject += $nsxtManagerBackupStatus } } else { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerBackupStatus = Request-SddcManagerBackupStatus -server $server -user $user -pass $pass -failureOnly; $allBackupStatusObject += $sddcManagerBackupStatus } $vcenterBackupStatus = Request-vCenterBackupStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allBackupStatusObject += $vcenterBackupStatus $nsxtManagerBackupStatus = Request-NsxtManagerBackupStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allBackupStatusObject += $nsxtManagerBackupStatus } } else { if ($PsBoundParameters.ContainsKey("allDomains")) { $sddcManagerBackupStatus = Request-SddcManagerBackupStatus -server $server -user $user -pass $pass; $allBackupStatusObject += $sddcManagerBackupStatus foreach ($domain in $allWorkloadDomains ) { $vcenterBackupStatus = Request-vCenterBackupStatus -server $server -user $user -pass $pass -domain $domain.name; $allBackupStatusObject += $vcenterBackupStatus $nsxtManagerBackupStatus = Request-NsxtManagerBackupStatus -server $server -user $user -pass $pass -domain $domain.name; $allBackupStatusObject += $nsxtManagerBackupStatus } } else { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerBackupStatus = Request-SddcManagerBackupStatus -server $server -user $user -pass $pass; $allBackupStatusObject += $sddcManagerBackupStatus } $vcenterBackupStatus = Request-VcenterBackupStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allBackupStatusObject += $vcenterBackupStatus $nsxtManagerBackupStatus = Request-NsxtManagerBackupStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allBackupStatusObject += $nsxtManagerBackupStatus } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $backupJsonSuffix $allBackupStatusObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allBackupStatusObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allBackupStatusObject = $allBackupStatusObject | Sort-Object Component, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="infra-backup"></a><h3>Backups Status</h3>' -PostContent "<p>No issues found.</p><p>Please verify that each successful file-based backup exists on the destination.</p>" } else { $allBackupStatusObject = $allBackupStatusObject | Sort-Object Component, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="infra-backup"></a><h3>Backups Status</h3>' -PostContent "<p>Please verify that each successful file-based backup exists on the destination.</p>" -As Table } $allBackupStatusObject = Convert-CssClass -htmldata $allBackupStatusObject $allBackupStatusObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-BackupStatus Function Publish-NsxtTransportNodeStatus { <# .SYNOPSIS Request and publish the status of NSX transport nodes managed by an NSX Manager cluster. .DESCRIPTION The Publish-NsxtTransportNodeStatus cmdlet checks the status NSX transport nodes managed by an NSX Manager cluster and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs checks on the NSX transport node status and outputs the results .EXAMPLE Publish-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the status of all NSX transport nodes in a VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish thestatus of all NSX transport nodes in a VMware Cloud Foundation instance but only reports issues. .EXAMPLE Publish-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the BGP status for the NSX transport nodes in a VMware Cloud Foundation instance for a workload domain named sfo-w01. .EXAMPLE Publish-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the status of all NSX transport nodes in a VMware Cloud Foundation instance. and saves it under F:\Reporting with filename <timestamp>-nsxttransportnode-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allNsxtTransportNodeStatusObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTransportNodeStatus = Request-NsxtTransportNodeStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtTransportNodeStatusObject += $nsxtTransportNodeStatus } } else { $nsxtTransportNodeStatus = Request-NsxtTransportNodeStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtTransportNodeStatusObject += $nsxtTransportNodeStatus } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTransportNodeStatus = Request-NsxtTransportNodeStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtTransportNodeStatusObject += $nsxtTransportNodeStatus } } else { $nsxtTransportNodeStatus = Request-NsxtTransportNodeStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtTransportNodeStatusObject += $nsxtTransportNodeStatus } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $nsxtTransportJsonSuffix $allNsxtTransportNodeStatusObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allNsxtTransportNodeStatusObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allNsxtTransportNodeStatusObject = $allNsxtTransportNodeStatusObject | Sort-Object Domain, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="nsx-tn"></a><h3>NSX Transport Node Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allNsxtTransportNodeStatusObject = $allNsxtTransportNodeStatusObject | Sort-Object Domain, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="nsx-tn"></a><h3>NSX Transport Node Status</h3>' -As Table } $allNsxtTransportNodeStatusObject = Convert-CssClass -htmldata $allNsxtTransportNodeStatusObject $allNsxtTransportNodeStatusObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtTransportNodeStatus Function Publish-NsxtTransportNodeTunnelStatus { <# .SYNOPSIS Request and publish the status of NSX transport node tunnels. .DESCRIPTION The Publish-NsxtTransportNodeStatus cmdlet checks the status NSX transport node tunnels and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs checks on the NSX transport node tunnel status and outputs the results .EXAMPLE Publish-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the status of all NSX transport node tunnels in a VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish thestatus of all NSX transport node tunnels in a VMware Cloud Foundation instance but only reports issues. .EXAMPLE Publish-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the BGP status for the NSX transport node tunnels in a VMware Cloud Foundation instance for a workload domain named sfo-w01. .EXAMPLE Publish-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the status of all NSX transport node tunnels in a VMware Cloud Foundation instance and saves it under F:\Reporting with filename <timestamp>-nsxttntunnel-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allNsxtTransportNodeTunnelStatusObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTransportNodeTunnelStatus = Request-NsxtTransportNodeTunnelStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtTransportNodeTunnelStatusObject += $nsxtTransportNodeTunnelStatus } } else { $nsxtTransportNodeTunnelStatus = Request-NsxtTransportNodeTunnelStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtTransportNodeTunnelStatusObject += $nsxtTransportNodeTunnelStatus } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTransportNodeTunnelStatus = Request-NsxtTransportNodeTunnelStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtTransportNodeTunnelStatusObject += $nsxtTransportNodeTunnelStatus } } else { $nsxtTransportNodeTunnelStatus = Request-NsxtTransportNodeTunnelStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtTransportNodeTunnelStatusObject += $nsxtTransportNodeTunnelStatus } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $nsxttntunnelJsonSuffix $allNsxtTransportNodeTunnelStatusObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allNsxtTransportNodeTunnelStatusObject.Count -eq 0) { $addNoIssues = $true } if ($allNsxtTransportNodeTunnelStatusObject.Count -ne 0) { if ($addNoIssues) { $allNsxtTransportNodeTunnelStatusObject = $allNsxtTransportNodeTunnelStatusObject | Sort-Object Domain, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="nsx-tn-tunnel"></a><h3>NSX Transport Node Tunnel Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allNsxtTransportNodeTunnelStatusObject = $allNsxtTransportNodeTunnelStatusObject | Sort-Object Domain, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="nsx-tn-tunnel"></a><h3>NSX Transport Node Tunnel Status</h3>' -As Table } $allNsxtTransportNodeTunnelStatusObject = Convert-CssClass -htmlData $allNsxtTransportNodeTunnelStatusObject } else { $allNsxtTransportNodeTunnelStatusObject = $allNsxtTransportNodeTunnelStatusObject | ConvertTo-Html -Fragment -PreContent '<a id="nsx-tn-tunnel"></a><h3>NSX Transport Node Tunnel Status</h3>' -PostContent '<p>No NSX Transport Node Tunnels found.</p>' } $allNsxtTransportNodeTunnelStatusObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtTransportNodeTunnelStatus Function Publish-NsxtTier0BgpStatus { <# .SYNOPSIS Request and publish the BGP status for the NSX Tier-0 gateways. .DESCRIPTION The Publish-NsxtTier0BgpStatus cmdlet checks the BGP status for the NSX Tier-0 gateways in a VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs checks on the BGP status and outputs the results .EXAMPLE Publish-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the BGP status for all NSX Tier-0 gateways in a VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish the BGP status for all NSX Tier-0 gateways in a VMware Cloud Foundation instance but only for the failed items. .EXAMPLE Publish-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the BGP status for the NSX Tier-0 gateways in a VMware Cloud Foundation instance for a workload domain named sfo-w01. .EXAMPLE Publish-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the BGP status for all NSX Tier-0 gateways in a VMware Cloud Foundation instance. and saves it under F:\Reporting with filename <timestamp>-nsxttier0bgp-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allNsxtTier0BgpStatusObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTier0BgpStatus = Request-NsxtTier0BgpStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtTier0BgpStatusObject += $nsxtTier0BgpStatus } } else { $nsxtTier0BgpStatus = Request-NsxtTier0BgpStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtTier0BgpStatusObject += $nsxtTier0BgpStatus } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $nsxtTier0BgpStatus = Request-NsxtTier0BgpStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtTier0BgpStatusObject += $nsxtTier0BgpStatus } } else { $nsxtTier0BgpStatus = Request-NsxtTier0BgpStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtTier0BgpStatusObject += $nsxtTier0BgpStatus } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $nsxttier0bgpJsonSuffix $allNsxtTier0BgpStatusObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allNsxtTier0BgpStatusObject.Count -eq 0) { $addNoIssues = $true } if ($nsxtTier0BgpStatus.Count -gt 0) { if ($addNoIssues) { $allNsxtTier0BgpStatusObject = $allNsxtTier0BgpStatusObject | Sort-Object 'NSX Manager', 'Domain', 'Tier-0 ID', 'Source Address' | ConvertTo-Html -Fragment -PreContent '<a id="nsx-t0-bgp"></a><h3>NSX Tier-0 Gateway BGP Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allNsxtTier0BgpStatusObject = $allNsxtTier0BgpStatusObject | Sort-Object 'NSX Manager', 'Domain', 'Tier-0 ID', 'Source Address' | ConvertTo-Html -Fragment -PreContent '<a id="nsx-t0-bgp"></a><h3>NSX Tier-0 Gateway BGP Status</h3>' -As Table } $allNsxtTier0BgpStatusObject = Convert-CssClass -htmldata $allNsxtTier0BgpStatusObject } else { $allNsxtTier0BgpStatusObject = $allNsxtTier0BgpStatusObject | ConvertTo-Html -Fragment -PreContent '<a id="nsx-t0-bgp"></a><h3>NSX Tier-0 Gateway BGP Status</h3>' -PostContent '<p>No BGP configuration found on NSX Tier-0 Gateway(s).</p>' -As Table $allNsxtTier0BgpStatusObject = Convert-CssClass -htmldata $allNsxtTier0BgpStatusObject } $allNsxtTier0BgpStatusObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtTier0BgpStatus Function Publish-SnapshotStatus { <# .SYNOPSIS Request and publish the snapshot status for the SDDC Manager, vCenter Server instances, and NSX Edge nodes managed by SDDC Manager. .DESCRIPTION The Publish-SnapshotStatus cmdlet checks the snapshot status for SDDC Manager, vCenter Server instances, and NSX Edge nodes in a VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs checks on the snapshot status and outputs the results .NOTES The cmdlet will not publish the snapshot status for NSX Local Manager cluster appliances managed by SDDC Manager. Snapshots are not recommended for NSX Manager cluster appliances and are disabled by default. .EXAMPLE Publish-SnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the snapshot status for the SDDC Manager, vCenter Server instances, and NSX Edge nodes managed by SDDC Manager. .EXAMPLE Publish-SnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish the snapshot status for the SDDC Manager, vCenter Server instances, and NSX Edge nodes managed by SDDC Manager but only failed items .EXAMPLE Publish-SnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the snapshot status for the SDDC Manager, vCenter Server instance, and NSX Edge nodes managed by SDDC Manager for a workload domain named sfo-w01. .EXAMPLE Publish-SnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the snapshot status for the SDDC Manager, vCenter Server instances, and NSX Edge nodes managed by SDDC Manager. and saves it under F:\Reporting with filename <timestamp>-snapshot-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allSnapshotStatusObject = New-Object System.Collections.ArrayList $allWorkloadDomains = Get-VCFWorkloadDomain $singleWorkloadDomain = Get-VCFWorkloadDomain | Where-Object {$_.name -eq $workloadDomain} if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { $sddcManagerSnapshotStatus = Request-SddcManagerSnapshotStatus -server $server -user $user -pass $pass -failureOnly; $allSnapshotStatusObject += $sddcManagerSnapshotStatus foreach ($domain in $allWorkloadDomains ) { $vcenterSnapshotStatus = Request-VcenterSnapshotStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allSnapshotStatusObject += $vcenterSnapshotStatus $nsxtEdgeSnapshotStatus = Request-NsxtEdgeSnapshotStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allSnapshotStatusObject += $nsxtEdgeSnapshotStatus } } else { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerSnapshotStatus = Request-SddcManagerSnapshotStatus -server $server -user $user -pass $pass -failureOnly; $allSnapshotStatusObject += $sddcManagerSnapshotStatus } $vcenterSnapshotStatus = Request-VcenterSnapshotStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allSnapshotStatusObject += $vcenterSnapshotStatus $nsxtEdgeSnapshotStatus = Request-NsxtEdgeSnapshotStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allSnapshotStatusObject += $nsxtEdgeSnapshotStatus } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { $sddcManagerSnapshotStatus = Request-SddcManagerSnapshotStatus -server $server -user $user -pass $pass; $allSnapshotStatusObject += $sddcManagerSnapshotStatus foreach ($domain in $allWorkloadDomains ) { $vcenterSnapshotStatus = Request-VcenterSnapshotStatus -server $server -user $user -pass $pass -domain $domain.name; $allSnapshotStatusObject += $vcenterSnapshotStatus $nsxtEdgeSnapshotStatus = Request-NsxtEdgeSnapshotStatus -server $server -user $user -pass $pass -domain $domain.name; $allSnapshotStatusObject += $nsxtEdgeSnapshotStatus } } else { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerSnapshotStatus = Request-SddcManagerSnapshotStatus -server $server -user $user -pass $pass; $allSnapshotStatusObject += $sddcManagerSnapshotStatus } $vcenterSnapshotStatus = Request-VcenterSnapshotStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allSnapshotStatusObject += $vcenterSnapshotStatus $nsxtEdgeSnapshotStatus = Request-NsxtEdgeSnapshotStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allSnapshotStatusObject += $nsxtEdgeSnapshotStatus } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $snapshotJsonSuffix $allSnapshotStatusObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allSnapshotStatusObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allSnapshotStatusObject = $allSnapshotStatusObject | Sort-Object Component, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="infra-snapshot"></a><h3>Snapshot Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allSnapshotStatusObject = $allSnapshotStatusObject | Sort-Object Component, Resource, Element | ConvertTo-Html -Fragment -PreContent '<a id="infra-snapshot"></a><h3>Snapshot Status</h3>' -PostContent '<p>Only checks snapshots for SDDC Manager, vCenter Server instances, and NSX Edge nodes managed by SDDC Manager. By default, snapshots for NSX Local Manager cluster appliances are disabled and are not recommended.</p>' -As Table } $allSnapshotStatusObject = Convert-CssClass -htmldata $allSnapshotStatusObject $allSnapshotStatusObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-SnapshotStatus Function Publish-NsxtHealthNonSOS { <# .SYNOPSIS Publish NSX Manager Health only for health checks which are not a part of SOS Utility NSX health. Data obtained is a subset of Publish-NsxtCombinedHealth cmdlet. .DESCRIPTION The Publish-NsxtHealthNonSOS cmdlet performs additional checks outside of SOS Utility to get the health of NSX Manager on the VMware Cloud Foundation instance and prepares the data to be published to an HTML report. Data obtained is subset of Publish-NsxtCombinedHealth cmdlet. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity and autehentication is available to SDDC Manager - Validates that network connectivity and autehentication is available to NSX Manager - Performs health checks and outputs the results .EXAMPLE Publish-NsxtHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example checks NSX Manager health outside SOS Utility for all workload domains across the VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example checks NSX Manager health outside SOS Utility for a single workload domain in a VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example checks NSX Manager health outside SOS Utility for all workload domains across the VMware Cloud Foundation instance but only reports issues. .EXAMPLE Publish-NsxtHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example checks NSX Manager health outside SOS Utility for all workload domains across the VMware Cloud Foundation instance and and saves it as JSON under F:\Reporting with filename <timestamp>-nsxtcombinedhealthnonsos-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass Try { $allNsxtHealthObject = New-Object System.Collections.ArrayList $allWorkloadDomains = Get-VCFWorkloadDomain if ($PsBoundParameters.ContainsKey("allDomains") -and $PsBoundParameters.ContainsKey("failureOnly")) { foreach ($domain in $allWorkloadDomains ) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtHealthObject += $nsxtComputeManagerStatus } } elseif ($PsBoundParameters.ContainsKey("allDomains")) { foreach ($domain in $allWorkloadDomains ) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtHealthObject += $nsxtComputeManagerStatus } } if ($PsBoundParameters.ContainsKey("workloadDomain") -and $PsBoundParameters.ContainsKey("failureOnly")) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtHealthObject += $nsxtComputeManagerStatus } elseif ($PsBoundParameters.ContainsKey("workloadDomain")) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtHealthObject += $nsxtComputeManagerStatus } if ($PsBoundParameters.ContainsKey("outputJson")) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $nsxtCombinedHealthNonSOSJsonSuffix $allNsxtHealthObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allNsxtHealthObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allNsxtHealthObject = $allNsxtHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status - Non SOS</h3>' -PostContent '<p>No issues found.</p>' } else { $allNsxtHealthObject = $allNsxtHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status - Non SOS</h3>' -As Table } $allNsxtHealthObject = Convert-CssClass -htmldata $allNsxtHealthObject $allNsxtHealthObject } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtHealthNonSOS Function Publish-NsxtCombinedHealth { <# .SYNOPSIS Request and publish NSX Manager Health. .DESCRIPTION The Publish-NsxtCombinedHealth cmdlet checks the health of NSX Manager on the VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity and autehentication is available to SDDC Manager - Validates that network connectivity and autehentication is available to NSX Manager - Performs health checks and outputs the results .EXAMPLE Publish-NsxtCombinedHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -allDomains This example checks NSX Manager health for all workload domains across the VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtCombinedHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -workloadDomain sfo-w01 This example checks NSX Manager health for a single workload domain in a VMware Cloud Foundation instance. .EXAMPLE Publish-NsxtCombinedHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -allDomains -failureOnly This example checks NSX Manager health for all workload domains across the VMware Cloud Foundation instance but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -user $user -password $pass Try { $allNsxtHealthObject = New-Object System.Collections.ArrayList $allWorkloadDomains = Get-VCFWorkloadDomain if ($PsBoundParameters.ContainsKey("allDomains") -and $PsBoundParameters.ContainsKey("failureOnly")) { foreach ($domain in $allWorkloadDomains ) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allNsxtHealthObject += $nsxtComputeManagerStatus } $nsxtHtml = Publish-NsxtHealth -json $json -failureOnly; $allNsxtHealthObject += $nsxtHtml } elseif ($PsBoundParameters.ContainsKey("allDomains")) { foreach ($domain in $allWorkloadDomains ) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $domain.name; $allNsxtHealthObject += $nsxtComputeManagerStatus } $nsxtHtml = Publish-NsxtHealth -json $json; $allNsxtHealthObject += $nsxtHtml } if ($PsBoundParameters.ContainsKey("workloadDomain") -and $PsBoundParameters.ContainsKey("failureOnly")) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allNsxtHealthObject += $nsxtComputeManagerStatus $nsxtHtml = Publish-NsxtHealth -json $json -failureOnly; $allNsxtHealthObject += $nsxtHtml } elseif ($PsBoundParameters.ContainsKey("workloadDomain")) { $nsxtVidmStatus = Request-NsxtVidmStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtHealthObject += $nsxtVidmStatus $nsxtComputeManagerStatus = Request-NsxtComputeManagerStatus -server $server -user $user -pass $pass -domain $workloadDomain; $allNsxtHealthObject += $nsxtComputeManagerStatus $nsxtHtml = Publish-NsxtHealth -json $json; $allNsxtHealthObject += $nsxtHtml } if ($allNsxtHealthObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allNsxtHealthObject = $allNsxtHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allNsxtHealthObject = $allNsxtHealthObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="nsx-local-manager"></a><h3>NSX Manager Health Status</h3>' -As Table } $allNsxtHealthObject = Convert-CssClass -htmldata $allNsxtHealthObject $allNsxtHealthObject } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtCombinedHealth Function Publish-StorageCapacityHealth { <# .SYNOPSIS Request and publish the storage capacity status. .DESCRIPTION The Publish-StorageCapacityHealth cmdlet checks the storage usage status for SDDC Manager, vCenter Server, Datastores and ESXi hosts, in a VMware Cloud Foundation instance and prepares the data to be published to an HTML report or plain text to console. The cmdlet connects to SDDC Manager using the -server, -user, -pass, -localUser, and -localPass values: - Validates the network connectivity and authantication to the SDDC Manager instance - Performs checks on the storage usage status and outputs the results .EXAMPLE Publish-StorageCapacityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! -allDomains This example will publish storage usage status for SDDC Manager, vCenter Server instances, ESXi hosts, and datastores in a VMware Cloud Foundation instance .EXAMPLE Publish-StorageCapacityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! -allDomains -failureOnly This example will publish storage usage status for SDDC Manager, vCenter Server instances, ESXi hosts, and datastores in a VMware Cloud Foundation instance but only for the failed items. .EXAMPLE Publish-StorageCapacityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! -workloadDomain sfo-w01 This example will publish storage usage status for a specific workload domain in a VMware Cloud Foundation instance .EXAMPLE Publish-StorageCapacityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! -workloadDomain sfo-w01 -outputJson F:\Reporting This example will publish storage usage status for a specific workload domain in a VMware Cloud Foundation instance and saves it as JSON under F:\Reporting with filename <timestamp>-storagecapacityhealth-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER localUser The username to authenticate to the SDDC Manager appliance as a local user. .PARAMETER localPass The password to authenticate to the SDDC Manager appliance as a local user. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$localUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$localPass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -user $user -password $pass $localPass = Get-Password -user $localUser -password $localPass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $singleWorkloadDomain = Get-VCFWorkloadDomain | Where-Object {$_.name -eq $workloadDomain} $allStorageCapacityHealth = New-Object System.Collections.ArrayList $allVcenterStorageHealth = New-Object System.Collections.ArrayList $allEsxiStorageCapacity = New-Object System.Collections.ArrayList $allDatastoreStorageCapacity = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey("allDomains")) { if ($PsBoundParameters.ContainsKey("failureOnly")) { $sddcManagerStorageHealth = Request-SddcManagerStorageHealth -server $server -user $user -pass $pass -localUser $localUser -localPass $localPass -failureOnly; foreach ($domain in $allWorkloadDomains ) { $vCenterStorageHealth = Request-VcenterStorageHealth -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allVcenterStorageHealth += $vCenterStorageHealth $esxiStorageCapacity = Request-EsxiStorageCapacity -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allEsxiStorageCapacity += $esxiStorageCapacity $datastoreStorageCapacity = Request-DatastoreStorageCapacity -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allDatastoreStorageCapacity += $datastoreStorageCapacity } } else { $sddcManagerStorageHealth = Request-SddcManagerStorageHealth -server $server -user $user -pass $pass -localUser $localUser -localPass $localPass foreach ($domain in $allWorkloadDomains ) { $vCenterStorageHealth = Request-VcenterStorageHealth -server $server -user $user -pass $pass -domain $domain.name; $allVcenterStorageHealth += $vCenterStorageHealth $esxiStorageCapacity = Request-EsxiStorageCapacity -server $server -user $user -pass $pass -domain $domain.name; $allEsxiStorageCapacity += $esxiStorageCapacity $datastoreStorageCapacity = Request-DatastoreStorageCapacity -server $server -user $user -pass $pass -domain $domain.name; $allDatastoreStorageCapacity += $datastoreStorageCapacity } } } else { if ($PsBoundParameters.ContainsKey("failureOnly")) { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerStorageHealth = Request-SddcManagerStorageHealth -server $server -user $user -pass $pass -localUser $localUser -localPass $localPass -failureOnly } $vCenterStorageHealth = Request-VcenterStorageHealth -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allVcenterStorageHealth += $vCenterStorageHealth $esxiStorageCapacity = Request-EsxiStorageCapacity -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allEsxiStorageCapacity += $esxiStorageCapacity $datastoreStorageCapacity = Request-DatastoreStorageCapacity -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allDatastoreStorageCapacity += $datastoreStorageCapacity } else { if ($singleWorkloadDomain.type -eq "MANAGEMENT") { $sddcManagerStorageHealth = Request-SddcManagerStorageHealth -server $server -user $user -pass $pass -localUser $localUser -localPass $localPass } $vCenterStorageHealth = Request-VcenterStorageHealth -server $server -user $user -pass $pass -domain $workloadDomain; $allVcenterStorageHealth += $vCenterStorageHealth $esxiStorageCapacity = Request-EsxiStorageCapacity -server $server -user $user -pass $pass -domain $workloadDomain; $allEsxiStorageCapacity += $esxiStorageCapacity $datastoreStorageCapacity = Request-DatastoreStorageCapacity -server $server -user $user -pass $pass -domain $workloadDomain; $allDatastoreStorageCapacity += $datastoreStorageCapacity } } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $storageCapacityHealthJsonSuffix $combinedjson = @{ "sddc-manager" = $sddcManagerStorageHealth "vcenter" = $allVcenterStorageHealth "esxi" = $allEsxiStorageCapacity "datastore" = $allDatastoreStorageCapacity } $combinedJson | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($sddcManagerStorageHealth.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $sddcManagerStorageHealth = $sddcManagerStorageHealth | ConvertTo-Html -Fragment -PreContent '<a id="storage-sddcmanager"></a><h3>SDDC Manager Disk Health Status</h3>' -PostContent '<p>No Issues Found.</p>' } else { $sddcManagerStorageHealth = $sddcManagerStorageHealth | ConvertTo-Html -Fragment -PreContent '<a id="storage-sddcmanager"></a><h3>SDDC Manager Disk Health Status</h3>' -As Table } $sddcManagerStorageHealth = Convert-CssClass -htmldata $sddcManagerStorageHealth if ($allVcenterStorageHealth.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allVcenterStorageHealth = $allVcenterStorageHealth | Sort-Object FQDN, Filesystem | ConvertTo-Html -Fragment -PreContent '<a id="storage-vcenter"></a><h3>vCenter Server Disk Health</h3>' -PostContent '<p>No Issues Found.</p>' } else { $allVcenterStorageHealth = $allVcenterStorageHealth | Sort-Object FQDN, Filesystem | ConvertTo-Html -Fragment -PreContent '<a id="storage-vcenter"></a><h3>vCenter Server Disk Health</h3>' -As Table } $allVcenterStorageHealth = Convert-CssClass -htmldata $allVcenterStorageHealth if ($allEsxiStorageCapacity.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allEsxiStorageCapacity = $allEsxiStorageCapacity | Sort-Object Domain, 'ESXi FQDN', 'Volume Name' | ConvertTo-Html -Fragment -PreContent '<a id="storage-esxi"></a><h3>ESXi Host Local Volume Capacity</h3>' -PostContent '<p>No Issues Found.</p>' } else { $allEsxiStorageCapacity = $allEsxiStorageCapacity | Sort-Object Domain, 'ESXi FQDN', 'Volume Name' | ConvertTo-Html -Fragment -PreContent '<a id="storage-esxi"></a><h3>ESXi Host Local Volume Capacity</h3>' -As Table } $allEsxiStorageCapacity = Convert-CssClass -htmldata $allEsxiStorageCapacity if ($allDatastoreStorageCapacity.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allDatastoreStorageCapacity = $allDatastoreStorageCapacity | Sort-Object 'vCenter Server', 'Datastore Name' | ConvertTo-Html -Fragment -PreContent '<a id="storage-datastore"></a><h3>Datastore Space Usage Report</h3>' -PostContent '<p>No Issues Found.</p>' } else { $allDatastoreStorageCapacity = $allDatastoreStorageCapacity | Sort-Object 'vCenter Server', 'Datastore Name' | ConvertTo-Html -Fragment -PreContent '<a id="storage-datastore"></a><h3>Datastore Space Usage Report</h3>' -As Table } $allDatastoreStorageCapacity = Convert-CssClass -htmldata $allDatastoreStorageCapacity $allStorageCapacityHealth += $sddcManagerStorageHealth $allStorageCapacityHealth += $allVcenterStorageHealth $allStorageCapacityHealth += $allEsxiStorageCapacity $allStorageCapacityHealth += $allDatastoreStorageCapacity $allStorageCapacityHealth } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-StorageCapacityHealth Function Request-NsxtVidmStatus { <# .SYNOPSIS Returns the status of the Identity Manager integration for an NSX Manager cluster. .DESCRIPTION The Request-NsxtVidmStatus cmdlet returns the status of the Identity Manager integration for an NSX Manager cluster. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instanc - Gathers the details for the NSX Manager cluster from the SDDC Manager - Validates network connectivity and authentication to the NSX Local Manager cluster - Collects the Identity Manager integration status details .EXAMPLE Request-NsxtVidmStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the Identity Manager integration for an NSX Manager cluster managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtVidmStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the Identity Manager integration for an NSX Manager cluster managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $customObject = New-Object System.Collections.ArrayList $component = 'Identity Manager Integration' # Define the component name $resource = $vcfNsxDetails.fqdn # Define the resource name $integration = Get-NsxtVidmStatus # Set the alert and message based on the status of the integration if ($integration.vidm_enable -eq $true) { $alert = 'GREEN' # Ok; enabled $message = 'Integration is enabled ' # Set the status message } else { $alert = '-' # Notice; not enabled $message = 'Integration is not enabled. ' # Critical; failure } # Set the alert and message based on the status of the runtime state if ($integration.runtime_state -eq 'ALL_OK') { $alert = 'GREEN' # Ok; integration status is OK $messageState = 'and healthy.' # Set the alert message } elseif ($integration.vidm_enable -eq $true -and $integration.runtime_state -ne 'ALL_OK') { $alert = 'RED' # Critical; integration status is has failed $messageState = 'but unhealthy.' # Set the alert message } elseif ($integration.vidm_enable -eq $false -and $integration.runtime_state -ne 'ALL_OK') { $alert = '-' # Notice; integration is not enabled } $message += $messageState # Combine the alert message # Add the properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } $customObject | Sort-Object Component, Resource } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtVidmStatus Function Request-NsxtComputeManagerStatus { <# .SYNOPSIS Returns the status of the compute managers attached to an NSX Manager cluster. .DESCRIPTION The Request-NsxtComputeManagerStatus cmdlet returns the status of the compute managers attached to an NSX Manager cluster. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the NSX Manager cluster from the SDDC Manager - Validates network connectivity and authentication to the NSX Local Manager cluster - Collects the status of the compute managers .EXAMPLE Request-NsxtComputeManagerStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the compute managers attached to an NSX Manager cluster managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtComputeManagerStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the compute managers attached to an NSX Manager cluster managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -First 1) -pass ($vcfNsxDetails.adminPass | Select-Object -First 1)) { $computeManagers = (Get-NsxtComputeManager) foreach ($computeManager in $computeManagers) { $customObject = New-Object System.Collections.ArrayList $component = 'Compute Manager' # Define the component name $resource = $vcfNsxDetails.fqdn # Define the resource name # Set the alert and message based on the status of the compute manager if ($computeManager.server -notin (( Get-VCFWorkloadDomain | Where-Object { $_.nsxtCluster.vipFqdn -eq $vcfNsxDetails.fqdn }).vcenters.fqdn) ) { $alert = 'RED' # Critical; rogue addition detected $message = "$($computeManager.server) has been detected as a rogue addition." # Critical; rogue addition detected } else { $status = (Get-NsxtComputeManagerStatus -id $computeManager.id) if ($status.registration_status -eq 'REGISTERED' -and $status.connection_status -eq 'UP') { $alert = 'GREEN' # Ok; registered and up $message = "$($computeManager.server) is registered and healthy." # Ok; registered and up } elseif ($status.registration_status -eq 'REGISTERED' -and $status.connection_status -ne 'UP') { $alert = 'RED' # Critical; registered and not up $message = "$($computeManager.server) is registered but unhealthy." # Critical; registered and not up } else { $alert = 'RED' # Critical; not registered $message = "$($computeManager.server) is not registered." # Critical; not registered } } # Add the properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($elementObject.alert -eq 'RED') { $customObject += $elementObject } } else { $customObject += $elementObject } $outputObject += $customObject } } $outputObject | Sort-Object Component, Resource } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtComputeManagerStatus Function Request-SddcManagerSnapshotStatus { <# .SYNOPSIS Request the snapshot status for the SDDC Manager. .DESCRIPTION The Request-SddcManagerSnapshotStatus cmdlet checks the snapshot status for SDDC Manager. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates network connectivity and authenticaton to the SDDC Manager instance - Gathers the details for the vCenter Server instance from the SDDC Manager - Validates network connectivity and authentication to the vCenter Server instance - Performs checks on the snapshot status and outputs the results .EXAMPLE Request-SddcManagerSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will publish the snapshot status for the SDDC Manager in a VMware Cloud Foundation instance. .EXAMPLE Request-SddcManagerSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -failureOnly This example will publish the snapshot status for the SDDC Manager in a VMware Cloud Foundation instance, but for only failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $vcfVcenterDetails.fqdn) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $customObject = New-Object System.Collections.ArrayList $component = 'SDDC Manager' $resource = 'SDDC Manager Snapshot' $domain = (Get-VCFWorkloadDomain | Sort-Object -Property type, name).name -join ',' $snapshotStatus = Get-SnapshotStatus -vm ($server.Split('.')[0]) $snapshotCount = ($snapshotStatus | Measure-Object).count $snapshotLast = $snapshotStatus.Created | select -Last 1 $snapshotAge = [math]::Ceiling(((Get-Date) - ([DateTime]$snapshotLast)).TotalDays) # Set the alert color based on the age of the snapshot if ($snapshotCount -eq 0) { $alert = 'GREEN' # Ok; = 0 snapshots $message = 'No snapshots exist.' } elseif ($snapshotAge -le 1) { $alert = 'GREEN' # OK; <= 1 days $message = 'Latest snapshot is less than 1 day old. ' } elseif ($snapshotAge -gt 1 -and $snapshotAge -le 3) { $alert = 'YELLOW' # Warning; > 1 days and <= 3 days $message = 'Latest snapshot is greater than 1 day old. ' } elseif ($snapshotAge -gt 3) { $alert = 'RED' # Critical; >= 7 days $message = 'Latest snapshot is greater than 3 days old. ' } # Set the alert color based on the number of snapshots. if ($snapshotCount -eq 1) { $messageCount = 'A single snapshot exists. ' } elseif ($snapshotCount -gt 1) { $messageCount = 'More than 1 snapshot exist. ' } $message += $messageCount # Combine the alert message # Set the alert message based on the snapshot consolidation status. if (Get-SnapshotConsolidation -vm ($server.Split('.')[0])) { $alert = 'RED' # Critical; Consolidation is required $consolidationRequired = $true $messageConsolidation += 'Snapshot consolidation is required.' } else { $consolidationRequired = $false } $message += $messageConsolidation $elementObject = New-Object -TypeName psobject # Add the snapshot details to the PSObject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $server $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Snapshots' -NotePropertyValue $snapshotCount $elementObject | Add-Member -NotePropertyName 'Latest' -NotePropertyValue $snapshotLast $elementObject | Add-Member -NotePropertyName 'Consolidation Required' -NotePropertyValue $consolidationRequired $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } $customObject | Sort-Object Component, Resource, Element } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-SddcManagerSnapshotStatus Function Request-VcenterSnapshotStatus { <# .SYNOPSIS Request the snapshot status for the vCenter Server instance. .DESCRIPTION The Request-VcenterSnapshotStatus cmdlet checks the snapshot status for vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the vCenter Server instance from the SDDC Manager - Validates network connectivity and authentication to the vCenter Server instance - Performs checks on the snapshot status and outputs the results .EXAMPLE Request-VcenterSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will publish the snapshot status for a vCenter Server instance for a specific workload domain. .EXAMPLE Request-VcenterSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will publish the snapshot status for a vCenter Server instance for a specific workload domain, but only failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $vcenter = (Get-VCFWorkloadDomain | Where-Object { $_.name -eq $domain }).vcenters $vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $customObject = New-Object System.Collections.ArrayList $component = 'vCenter Server' $resource = 'vCenter Server Snapshot' $domain = (Get-VCFWorkloadDomain | Where-Object { $_.vcenters.fqdn -eq $vcenter.fqdn }).name $snapshotStatus = Get-SnapshotStatus -vm ($vcenter.fqdn.Split('.')[0]) $snapshotCount = ($snapshotStatus | Measure-Object).count $snapshotLast = $snapshotStatus.Created | select -Last 1 $snapshotAge = [math]::Ceiling(((Get-Date) - ([DateTime]$snapshotLast)).TotalDays) # Set the alert color based on the age of the snapshot if ($snapshotCount -eq 0) { $alert = 'GREEN' # Ok; = 0 snapshots $message = 'No snapshots exist.' } elseif ($snapshotAge -le 1) { $alert = 'GREEN' # OK; <= 1 days $message = 'Latest snapshot is less than 1 day old. ' } elseif ($snapshotAge -gt 1 -and $snapshotAge -le 3) { $alert = 'YELLOW' # Warning; > 1 days and <= 3 days $message = 'Latest snapshot is greater than 1 day old. ' } elseif ($snapshotAge -gt 3) { $alert = 'RED' # Critical; >= 7 days $message = 'Latest snapshot is greater than 3 days old. ' } # Set the alert message based on the number of snapshots. if ($snapshotCount -eq 1) { $messageCount = 'A single snapshot exists. ' } elseif ($snapshotCount -gt 1) { $messageCount = 'More than 1 snapshot exist. ' } $message += $messageCount # Combine the alert message # Set the alert message based on the snapshot consolidation status. if (Get-SnapshotConsolidation -vm ($vcenter.fqdn.Split('.')[0])) { $alert = 'RED' # Critical; Consolidation is required $consolidationRequired = $true $messageConsolidation += 'Snapshot consolidation is required.' } else { $consolidationRequired = $false } $message += $messageConsolidation $elementObject = New-Object -TypeName psobject # Add the snapshot details to the PSObject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $vcenter.fqdn $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Snapshots' -NotePropertyValue $snapshotCount $elementObject | Add-Member -NotePropertyName 'Latest' -NotePropertyValue $snapshotLast $elementObject | Add-Member -NotePropertyName 'Consolidation Required' -NotePropertyValue $consolidationRequired $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } $outputObject += $customObject # Add the custom object to the output object } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } $outputObject | Sort-Object Component, Resource, Element } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VcenterSnapshotStatus Function Request-NsxtEdgeSnapshotStatus { <# .SYNOPSIS Request the snapshot status for NSX Edge nodes. .DESCRIPTION The Request-NsxtEdgeSnapshotStatus cmdlet checks the snapshot status for NSX Edge nodes. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the NSX Manager and NSX Edge node details from the SDDC Manager - Validates network connectivity and authentication to the vCenter Server instance - Performs checks on the snapshot status and outputs the results .EXAMPLE Request-NsxtEdgeSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will publish the snapshot status for all NSX Edge nodes managed by SDDC Manager for a specific workload domain. .EXAMPLE Request-NsxtEdgeSnapshotStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will publish the snapshot status for all NSX Edge nodes managed by SDDC Manager for a specific workload domain. but only failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $nsxtManager = Get-VCFNsxtCluster | Where-Object { $_.domains.name -eq $domain } if ($nsxtEdgeDetails = Get-VCFEdgeCluster | Where-Object { $_.nsxtCluster.vipfqdn -eq $nsxtManager.vipFqdn }) { foreach ($nsxtEdgeNode in $nsxtEdgeDetails.edgeNodes) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $customObject = New-Object System.Collections.ArrayList $message = '' $component = 'NSX' $resource = 'NSX Edge Node Snapshot' $domain = $domain $snapshotStatus = Get-SnapshotStatus -vm ($nsxtEdgeNode.hostName.Split('.')[0]) $snapshotCount = ($snapshotStatus | Measure-Object).count $snapshotLast = $snapshotStatus.Created | select -Last 1 $snapshotAge = [math]::Ceiling(((Get-Date) - ([DateTime]$snapshotLast)).TotalDays) # Set the alert color based on the age of the snapshot if ($snapshotCount -eq 0) { $alert = 'GREEN' # Ok; = 0 snapshots $message = 'No snapshots exist.' } elseif ($snapshotAge -le 1) { $alert = 'GREEN' # OK; <= 1 days $message = 'Latest snapshot is less than 1 day old. ' } elseif ($snapshotAge -gt 1 -and $snapshotAge -le 3) { $alert = 'YELLOW' # Warning; > 1 days and <= 3 days $message = 'Latest snapshot is greater than 1 day old. ' } elseif ($snapshotAge -gt 3) { $alert = 'RED' # Critical; >= 7 days $message = 'Latest snapshot is greater than 3 days old. ' } # Set the alert message based on the number of snapshots. if ($snapshotCount -eq 1) { $messageCount = 'A single snapshot exists. ' } elseif ($snapshotCount -gt 1) { $messageCount = 'More than 1 snapshot exist. ' } $message += $messageCount # Combine the alert message # Set the alert message based on snapshots consolidation status. if (Get-SnapshotConsolidation -vm ($nsxtEdgeNode.hostName.Split('.')[0])) { $alert = 'RED' # Critical; Consolidation is required $consolidationRequired = $true $messageConsolidation += 'Snapshot consolidation is required.' } else { $consolidationRequired = $false } $message += $messageConsolidation $elementObject = New-Object -TypeName psobject # Add the snapshot details to the PSObject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $nsxtEdgeNode.hostName $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Snapshots' -NotePropertyValue $snapshotCount $elementObject | Add-Member -NotePropertyName 'Latest' -NotePropertyValue $snapshotLast $elementObject | Add-Member -NotePropertyName 'Consolidation Required' -NotePropertyValue $consolidationRequired $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } $outputObject += $customObject # Add the custom object to the output object } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } $outputObject | Sort-Object Component, Resource, Element } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtEdgeSnapshotStatus Function Request-SddcManagerBackupStatus { <# .SYNOPSIS Returns the status of the file-level latest backup task in an SDDC Manager instance. .DESCRIPTION The Request-SddcManagerBackupStatus cmdlet returns the status of the latest file-level backup task in an SDDC Manager instance. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Collects the latest file-level backup status details .EXAMPLE Request-SddcManagerBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return the status of the latest file-level backup task in an SDDC Manager instance. .EXAMPLE Request-SddcManagerBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -failureOnly This example will return the status of the latest file-level backup task in an SDDC Manager instance but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $customObject = New-Object System.Collections.ArrayList $component = 'SDDC Manager' # Define the component name $resource = 'SDDC Manager Backup Operation' # Define the resource name $domain = (Get-VCFWorkloadDomain | Sort-Object -Property type, name).name -join ',' # Define the domain(s) $backupTask = Get-VCFTask | Where-Object { $_.type -eq 'SDDCMANAGER_BACKUP' } | Select-Object -First 1 if ($backupTask) { if ($PSEdition -eq 'Core') { $date = $backupTask.creationTimestamp } else { $date = [DateTime]::ParseExact($backupTask.creationTimestamp, 'yyyy-MM-ddTHH:mm:ss.fffZ', [System.Globalization.CultureInfo]::InvariantCulture) # Define the date } $backupAge = [math]::Ceiling(((Get-Date) - ([DateTime]$date)).TotalDays) # Calculate the number of days since the backup was created # Set the status for the backup task if ($backupTask.status -eq 'Successful') { $alert = "GREEN" # Ok; success } else { $alert = "RED" # Critical; failure } # Set the message for the backup task if ([string]::IsNullOrEmpty($errors)) { $message = "The backup completed without errors. " # Ok; success } else { $message = "The backup failed with errors. Please investigate before proceeding. " # Critical; failure } # Set the alert and message for the backup task based on the age of the backup if ($backupAge -ge 3) { $alert = "RED" # Critical; >= 3 days $messageBackupAge = "Backup is more than 3 days old." # Set the alert message } elseif ($backupAge -gt 1) { $alert = "YELLOW" # Warning; > 1 days $messageBackupAge = "Backup is more than 1 days old." # Set the alert message } else { $alert = "GREEN" # Ok; <= 1 days $messageBackupAge = "Backup is less than 1 day old." # Set the alert message } $message += $messageBackupAge # Combine the alert message # Set the alert and message if the backup is located on the SDDC Manager $backupServer = (Get-VCFBackupConfiguration).server # Get the backup server if ($backupServer -eq (Get-VCFManager).fqdn -or $backupServer -eq (Get-VCFManager).ipAddress) { $alert = "RED" # Critical; backup server is located on the SDDC Manager $messageBackupServer = "Backup is located on the SDDC Manager ($server). Reconfigure backups to use another location." # Set the alert message $message = $messageBackupServer # Override the message } } else { $alert = "RED" # Critical; backup is not configured $message = "Backup is not configured." # Set the alert message } # Add Backup Status Properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $server # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain(s) $elementObject | Add-Member -NotePropertyName 'Date' -NotePropertyValue $date # Set the timestamp $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } $customObject | Sort-Object Component, Resource, Element } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-SddcManagerBackupStatus Function Request-NsxtManagerBackupStatus { <# .SYNOPSIS Returns the status of the latest file-level backup of an NSX Manager cluster. .DESCRIPTION The Request-NsxtManagerBackupStatus cmdlet returns the status of the latest backup of an NSX Manager cluster. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the NSX Manager cluster from the SDDC Manager - Validates network connectivity and authentication to the NSX Manager cluster - Collects the file-level backup status details .EXAMPLE Request-NsxtManagerBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the latest file-level backup of an NSX Manager cluster managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtManagerBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the latest file-level backup of an NSX Manager cluster managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $backupTask = Get-NsxtBackupHistory -fqdn $vcfNsxDetails.fqdn $customObject = New-Object System.Collections.ArrayList # NSX Node Backup $component = 'NSX Manager' # Define the component name $resource = 'Node Backup Operation' # Define the resource name foreach ($element in $backupTask.node_backup_statuses) { $timestamp = [DateTimeOffset]::FromUnixTimeMilliseconds($backupTask.node_backup_statuses.end_time).DateTime $backupAge = [math]::Ceiling(((Get-Date) - ([DateTime]$timestamp)).TotalDays) # Calculate the number of days since the backup was created # Set the alert and message based on the status of the backup if ($backupTask.node_backup_statuses.success -eq $true) { $alert = "GREEN" # Ok; success $message = 'The backup completed without errors. ' # Set the backup status message } else { $alert = "RED" # Critical; failure $message = "The backup failed with errors. Please investigate before proceeding. " # Critical; failure } # Set the alert and message update for the backup task based on the age of the backup if ($backupAge -ge 3) { $alert = 'RED' # Critical; >= 3 days $messageBackupAge = 'Backup is more than 3 days old.' # Set the alert message } elseif ($backupAge -gt 1) { $alert = 'YELLOW' # Warning; > 1 days $messageBackupAge = 'Backup is more than 1 days old.' # Set the alert message } else { $alert = 'GREEN' # Ok; <= 1 days $messageBackupAge = 'Backup is less than 1 day old.' # Set the alert message } $message += $messageBackupAge # Combine the alert message # Set the alert and message if the backup is located on the SDDC Manager. $backupServer = (Get-NsxtBackupConfiguration -fqdn $vcfNsxDetails.fqdn).remote_file_server.server # Get the backup server if ($backupServer -eq (Get-VCFManager).fqdn -or $backupServer -eq (Get-VCFManager).ipAddress) { $alert = 'RED' # Critical; backup server is located on the SDDC Manager. $messageBackupServer = "Backup is located on the SDDC Manager ($server). Reconfigure backups to use another location." # Set the alert message $message = $messageBackupServer # Override the message } # Add Backup Status Properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $vcfNsxDetails.fqdn # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain $elementObject | Add-Member -NotePropertyName 'Date' -NotePropertyValue $timestamp # Set the end timestamp $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } $outputObject += $customObject # Add the custom object to the output object # NSX Cluster Backup $component = 'NSX Manager' # Define the component name $resource = 'Cluster Backup Operation' # Define the resource name foreach ($element in $backupTask.cluster_backup_statuses) { $timestamp = [DateTimeOffset]::FromUnixTimeMilliseconds($backupTask.cluster_backup_statuses.end_time).DateTime $backupAge = [math]::Ceiling(((Get-Date) - ([DateTime]$timestamp)).TotalDays) # Calculate the number of days since the backup was created # Set the alert and message based on the status of the backup if ($backupTask.node_backup_statuses.success -eq $true) { $alert = 'GREEN' # Ok; success $message = 'The backup completed without errors. ' # Set the backup status message } else { $alert = 'RED' # Critical; failure $message = 'The backup failed with errors. Please investigate before proceeding. ' # Critical; failure } # Set the alert and message update for the backup task based on the age of the backup if ($backupAge -ge 3) { $alert = 'RED' # Critical; >= 3 days $messageBackupAge = 'Backup is more than 3 days old.' # Set the alert message } elseif ($backupAge -gt 1) { $alert = 'YELLOW' # Warning; > 1 days $messageBackupAge = 'Backup is more than 1 days old.' # Set the alert message } else { $alert = 'GREEN' # Ok; <= 1 days $messageBackupAge = 'Backup is less than 1 day old.' # Set the alert message } $message += $messageBackupAge # Combine the alert message # Set the alert and message if the backup is located on the SDDC Manager $backupServer = (Get-NsxtBackupConfiguration -fqdn $vcfNsxDetails.fqdn).remote_file_server.server # Get the backup server if ($backupServer -eq (Get-VCFManager).fqdn -or $backupServer -eq (Get-VCFManager).ipAddress) { $alert = 'RED' # Critical; backup server is located on the SDDC Manager $messageBackupServer = "Backup is located on the SDDC Manager ($server). Reconfigure backups to use another location." # Set the alert message $message = $messageBackupServer # Override the message } # Add Backup Status Properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $vcfNsxDetails.fqdn # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain $elementObject | Add-Member -NotePropertyName 'Date' -NotePropertyValue $timestamp # Set the end timestamp $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } $outputObject += $customObject # Add the custom object to the output object # NSX Cluster Backup $component = 'NSX Manager' # Define the component name $resource = 'Inventory Backup Operation' # Define the resource name foreach ($element in $backupTask.cluster_backup_statuses) { $timestamp = [DateTimeOffset]::FromUnixTimeMilliseconds($backupTask.cluster_backup_statuses.end_time).DateTime $backupAge = [math]::Ceiling(((Get-Date) - ([DateTime]$timestamp)).TotalDays) # Calculate the number of days since the backup was created # Set the alert and message based on the status of the backup if ($backupTask.node_backup_statuses.success -eq $true) { $alert = 'GREEN' # Ok; success $message = 'The backup completed without errors. ' # Set the backup status message } else { $alert = 'RED' # Critical; failure $message = 'The backup failed with errors. Please investigate before proceeding. ' # Critical; failure } # Set the alert and message update for the backup task based on the age of the backup if ($backupAge -ge 3) { $alert = 'RED' # Critical; >= 3 days $messageBackupAge = 'Backup is more than 3 days old.' # Set the alert message } elseif ($backupAge -gt 1) { $alert = 'YELLOW' # Warning; > 1 days $messageBackupAge = 'Backup is more than 1 days old.' # Set the alert message } else { $alert = 'GREEN' # Ok; <= 1 days $messageBackupAge = 'Backup is less than 1 day old.' # Set the alert message } $message += $messageBackupAge # Combine the alert message # Set the alert and message if the backup is located on the SDDC Manager $backupServer = (Get-NsxtBackupConfiguration -fqdn $vcfNsxDetails.fqdn).remote_file_server.server # Get the backup server if ($backupServer -eq (Get-VCFManager).fqdn -or $backupServer -eq (Get-VCFManager).ipAddress) { $alert = 'RED' # Critical; backup server is located on the SDDC Manager $messageBackupServer = "Backup is located on the SDDC Manager ($server). Reconfigure backups to use another location." # Set the alert message $message = $messageBackupServer # Override the message } # Add Backup Status Properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $vcfNsxDetails.fqdn # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain $elementObject | Add-Member -NotePropertyName 'Date' -NotePropertyValue $timestamp # Set the end timestamp $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } $customObject | Sort-Object Domain, Element, Resource } } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtManagerBackupStatus Function Request-VcenterBackupStatus { <# .SYNOPSIS Returns the status of the file-level latest backup of a vCenter Server instance. .DESCRIPTION The Request-VcenterBackupStatus cmdlet returns the status of the latest backup of a vCenter Server instance. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the NvCenter Server instance from the SDDC Manager - Validates network connectivity and authentication to the vCenter Server instance - Collects the file-level backup status details .EXAMPLE Request-VcenterBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the latest file-level backup of a vCenter Server instance managed by SDDC Manager for a workload domain. .EXAMPLE Request-VcenterBackupStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the latest file-level backup of a vCenter Server instance managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-VcenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { Request-VcenterApiToken -fqdn $vcfVcenterDetails.fqdn -username $vcfVcenterDetails.ssoAdmin -password $vcfVcenterDetails.ssoAdminPass | Out-Null $customObject = New-Object System.Collections.ArrayList $component = 'vCenter Server' # Define the component name $resource = 'vCenter Server Backup Operation' # Define the resource name if ($global:backupTask = (Get-VcenterBackupStatus | Select-Object -Last 1).Value) { if ($PSEdition -eq 'Core') { $timestamp = $backupTask.end_time } else { $timestamp = [DateTime]::ParseExact($backupTask.end_time, 'yyyy-MM-ddTHH:mm:ss.fffZ', [System.Globalization.CultureInfo]::InvariantCulture) # Define the date } if ($timestamp) { $backupAge = [math]::Ceiling(((Get-Date) - ([DateTime]$timestamp)).TotalDays) # Calculate the number of days since the backup was created } else { $backupAge = 0 # Set the backup age to 0 if not available } # Set the status for the backup task if ($backupTask.status -eq 'SUCCEEDED') { $alert = "GREEN" # Ok; success } elseif ($backupTask.status -eq 'IN PROGRESS') { $alert = "YELLOW" # Warning; in progress } else { $alert = "RED" # Critical; failure } if ($timestamp) { # Set the message for the backup task if ([String]::IsNullOrEmpty($backupTask.messages)) { $message = "The backup completed without errors. " # Ok; success } else { $message = "The backup failed with errors. Please investigate before proceeding. " # Critical; failure } } # Set the alert and message update for the backup task based on the age of the backup if ($backupAge -eq 0) { $alert = "RED" # Critical; 0 days $messageBackupAge = "Backup has not completed." # Set the alert message } elseif ($backupAge -ge 3) { $alert = "RED" # Critical; >= 3 days $messageBackupAge = "Backup is more than 3 days old." # Set the alert message } elseif ($backupAge -gt 1) { $alert = "YELLOW" # Warning; > 1 days $messageBackupAge = "Backup is more than 1 days old." # Set the alert message } else { $alert = "GREEN" # Ok; <= 1 days $messageBackupAge = "Backup is less than 1 day old." # Set the alert message } $message += $messageBackupAge # Combine the alert message # Set the alert and message if the backup is located on the SDDC Manager $backupLocation = $backupTask.location # Get the backup server $backupServer = (($backupLocation -Split ('sftp://'))[-1] -Split ('/'))[0] if ($backupServer -eq (Get-VCFManager).fqdn -or $backupServer -eq (Get-VCFManager).ipAddress) { # Compare against the `host` attribute $alert = 'RED' # Critical; backup server is located on the SDDC Manager $messageBackupServer = "Backup is located on the SDDC Manager. Reconfigure backups to use another location." # Set the alert message $message = $messageBackupServer # Override the message } } else { $alert = "RED" # Critical; backup job no $message = "Backup is not configured." # Set the alert message } # Add Backup Status Properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component # Set the component name $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $vcfVcenterDetails.fqdn # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain(s) $elementObject | Add-Member -NotePropertyName 'Date' -NotePropertyValue $timestamp # Set the timestamp $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } $customObject | Sort-Object Component, Resource, Element } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VcenterBackupStatus Function Request-DatastoreStorageCapacity { <# .SYNOPSIS Checks the datastore usage in all vCenter Server instances. .DESCRIPTION The Request-DatastoreStorageCapacity cmdlet checks the datastore usage in all vCenters. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the details for each vCenter Server - Collects information about datastore usage .EXAMPLE Request-DatastoreStorageCapacity -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will check datastores on all vCenter Servers managed by SDDC Manager in a VMware Cloud Foundation instance but only failed items. .EXAMPLE Request-DatastoreStorageCapacity -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will check datastore on a vCenter Servers managed by SDDC Manager for a workload domain. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass # Define thresholds Green < Yellow < Red $greenThreshold = 80 $redThreshold = 90 Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $customObject = New-Object System.Collections.ArrayList $vcenter = (Get-VCFWorkloadDomain | Where-Object { $_.name -eq $domain }).vcenters $vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain if (Test-VsphereConnection -server $($vcenter.fqdn)) { if (Test-VsphereAuthentication -server $vcenter.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $datastores = Get-Datastore foreach ($datastore in $datastores) { # Calculate datastore usage and capacity if ([int]$datastore.CapacityGB -eq 0){ $usage = 0 $capacity = 0 $alert = 'RED' $message = "Capacity is reported as 0. Please check the datastore." } else{ $usage = [math]::Round((($datastore.CapacityGB - $datastore.FreeSpaceGB) / $datastore.CapacityGB * 100)) $usage = [int]$usage $capacity = [int]$datastore.CapacityGB # Applying thresholds and creating collection from input Switch ($usage) { { $_ -le $greenThreshold } { $alert = 'GREEN' # Green if $usage is up to $greenThreshold $message = "Used space is less than $greenThreshold%." } { $_ -ge $redThreshold } { $alert = 'RED' # Red if $usage is equal or above $redThreshold $message = "Used space is above $redThreshold%. Please reclaim space on the datastore." } Default { $alert = 'YELLOW' # Yellow if above two are not matched $message = "Used space is between $greenThreshold% and $redThreshold%. Please consider reclaiming some space on the datastore." } } } # Populate data into the object if (($PsBoundParameters.ContainsKey("failureOnly")) -and ($alert -eq 'GREEN')) { continue } # Skip population of object if "failureOnly" is selected and alert is "GREEN" $userObject = New-Object -TypeName psobject $userObject | Add-Member -notepropertyname 'vCenter Server' -notepropertyvalue $vcenter.fqdn $userObject | Add-Member -notepropertyname 'Datastore Name' -notepropertyvalue $datastore.Name $userObject | Add-Member -notepropertyname 'Datastore Type' -notepropertyvalue $datastore.Type.ToUpper() $userObject | Add-Member -notepropertyname 'Size (GB)' -notepropertyvalue $capacity $userObject | Add-Member -notepropertyname 'Used %' -notepropertyvalue $usage $userObject | Add-Member -notepropertyname 'Alert' -notepropertyvalue $alert $userObject | Add-Member -notepropertyname 'Message' -notepropertyvalue $message $customObject += $userObject # Creating collection to work with afterwords } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } $customObject | Sort-Object 'vCenter Server', 'Datastore Type', 'Datastore Name' } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-DatastoreStorageCapacity Function Request-VcenterStorageHealth { <# .SYNOPSIS Checks the disk usage on a vCenter Server instance. .DESCRIPTION The Request-VcenterStorageHealth cmdlets checks the disk space usage on a vCenter Server. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Validates network connectivity and authentication to the vCenter Server instance - Collects information for the disk usage - Checks disk usage against thresholds and outputs the results .EXAMPLE Request-VcenterStorageHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will check disk usage for a single workload domain .EXAMPLE Request-VcenterStorageHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will check the disk usage for all vCenter Server instances but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { # Define the command for retrieving the disk information. # Ignore the loopback device and the archive volume from the output. # Reference: https://kb.vmware.com/s/article/76563. $command = 'df -h | grep -e "^/" | grep -v "/dev/loop" | grep -v "/dev/mapper/archive_vg-archive"' $vcenter = (Get-VCFWorkloadDomain | Where-Object { $_.name -eq $domain }).vcenters $vcenterfqdn = $vcenter.fqdn $rootPass = (Get-VCFCredential | Where-Object { $_.credentialType -eq "SSH" -and $_.resource.resourceName -eq $vcenter.fqdn }).password $dfOutput = Invoke-VMScript -VM ($vcenter.fqdn.Split(".")[0]) -ScriptText $command -GuestUser root -GuestPassword $rootPass -Server $vcfVcenterDetails.fqdn if ($dfOutput.ExitCode -eq 0) { if ($PsBoundParameters.ContainsKey("failureOnly")) { Format-DfStorageHealth -dfOutput $dfOutput -systemFqdn $vcenter.fqdn -failureOnly } else { Format-DfStorageHealth -dfOutput $dfOutput -systemFqdn $vcenter.fqdn } } else { $alert = "RED" $message = "vCenter Connection check failed!" $elementObject = New-Object System.Collections.ArrayList $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "vCenter" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcenterfqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message $customObject += $elementObject } $customObject | Sort-Object Component, Resource } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VcenterStorageHealth Function Request-SddcManagerStorageHealth { <# .SYNOPSIS Checks the storage health (capacity) in an SDDC Manager appliance. .DESCRIPTION The Request-SddcManagerStorageHealth cmdlet checks the disk free space on the SDDC Manager appliance. The cmdlet connects to SDDC Manager using the -server, -user, -pass, -localUser, and -localPass values: - Performs checks on the local storage used space and outputs the results .EXAMPLE Request-SddcManagerStorageHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! This example checks the hard disk space in the SDDC Manager appliance. .EXAMPLE Request-SddcManagerStorageHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -localUser vcf -localPass VMw@re1! -failureOnly This example checks the hard disk space in the SDDC Manager appliance and outputs only the failures. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER localUser The username to authenticate to the SDDC Manager appliance as a local user. .PARAMETER localPass The password to authenticate to the SDDC Manager appliance as a local user. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$localUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$localPass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass $localPass = Get-Password -User $localUser -Password $localPass Try { # Define the command for retrieving the disk information. # Ignore the loopback device from the output. $command = 'df -h | grep -e "^/" | grep -v "/dev/loop"' $dfOutput = Invoke-SddcCommand -server $server -user $user -pass $pass -vmUser $localUser -vmPass $localPass -command $command if ($PsBoundParameters.ContainsKey("failureOnly")) { Format-DfStorageHealth -dfOutput $dfOutput -systemFqdn $server -failureOnly } else { Format-DfStorageHealth -dfOutput $dfOutput -systemFqdn $server } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-SddcManagerStorageHealth Function Request-EsxiStorageCapacity { <# .SYNOPSIS Checks the disk usage for ESXi hosts. .DESCRIPTION The Request-EsxiStorageCapacity cmdlets checks the disk space usage on ESXi hosts. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Collects disk usage information for each ESXi host in the workload domain - Checks disk usage against thresholds and outputs the results .EXAMPLE Request-EsxiStorageCapacity -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will check disk usage for ESXi hosts managed by SDDC Manager for a single workload domain. .EXAMPLE Request-EsxiStorageCapacity -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will check disk usage for ESXi hosts managed by SDDC Manager for a single workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $esxiPartitionsObject = New-Object System.Collections.ArrayList if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $esxiHosts = Get-VMHost -Server $vcfVcenterDetails.fqdn foreach ($esxiHost in $esxiHosts) { $esxcli = Get-EsxCli -VMhost $esxiHost.Name -V2 $allPartitions = $esxcli.storage.filesystem.list.invoke() foreach ($partition in $allPartitions) { if ($partition.Type -eq "VMFS-L" -or $partition.Type -eq "vfat") { $threshold = Format-StorageThreshold -size $partition.Size -free $partition.Free $esxiPartition = New-Object -TypeName psobject $esxiPartition | Add-Member -notepropertyname 'Domain' -notepropertyvalue $domain $esxiPartition | Add-Member -notepropertyname 'ESXi FQDN' -notepropertyvalue $esxiHost.Name $esxiPartition | Add-Member -notepropertyname 'Volume Name' -notepropertyvalue $partition.VolumeName.ToLower() $esxiPartition | Add-Member -notepropertyname 'Filesystem' -notepropertyvalue $partition.Type.ToLower() $esxiPartition | Add-Member -notepropertyname 'Used %' -notepropertyvalue $threshold.usage $esxiPartition | Add-Member -notepropertyname 'Alert' -notepropertyvalue $threshold.alert $esxiPartition | Add-Member -notepropertyname 'Message' -notepropertyvalue $threshold.message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($esxiPartition.alert -eq 'RED') -or ($esxiPartition.alert -eq 'YELLOW')) { $esxiPartitionsObject += $esxiPartition } } else { $esxiPartitionsObject += $esxiPartition } } } } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } $esxiPartitionsObject | Sort-Object Domain, 'ESXi FQDN', 'Volume Name', Alert } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-EsxiStorageCapacity Function Publish-ComponentConnectivityHealthNonSOS { <# .SYNOPSIS Request and publish Component Connectivity Health only for health checks which are not a part of SOS Utility NSX health. Data obtained is a subset of Publish-ComponentConnectivityHealth cmdlet. .DESCRIPTION The Publish-ComponentConnectivityHealthNonSOS cmdlet checks component connectivity across the VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs connectivityy health checks and outputs the results .EXAMPLE Publish-ComponentConnectivityHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example checks the component connectivity outside of SOS utility for all workload domains across the VMware Cloud Foundation instance. .EXAMPLE Publish-ComponentConnectivityHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example checks the component connectivity outside of SOS utility for a single workload domain in a VMware Cloud Foundation instance. .EXAMPLE Publish-ComponentConnectivityHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example checks the component connectivity outside of SOS utility for all workload domains across the VMware Cloud Foundation instance but only reports issues. .EXAMPLE Publish-ComponentConnectivityHealthNonSOS -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -jsonOutput F:\Reporting This example checks the component connectivity outside of SOS utility for all workload domains across the VMware Cloud Foundation instance and saves it under F:\Reporting with filename <timestamp>-componentconnectivityhealthnonsos-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -User $user -Password $pass Try { $allConnectivityObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey("allDomains")) { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -alldomains -failureOnly; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -alldomains -failureOnly; $allConnectivityObject += $NsxtConnectivity } else { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain -failureOnly; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain -failureOnly; $allConnectivityObject += $NsxtConnectivity } } else { if ($PsBoundParameters.ContainsKey("allDomains")) { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -alldomains; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -alldomains; $allConnectivityObject += $NsxtConnectivity } else { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain; $allConnectivityObject += $NsxtConnectivity } } if ($PsBoundParameters.ContainsKey("outputJson")) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $ComponentConnectivityHealthNonSOSJsonSuffix $allConnectivityObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allConnectivityObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allConnectivityObject = $allConnectivityObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allConnectivityObject = $allConnectivityObject | Sort-Object Component, Resource | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -As Table } $allConnectivityObject = Convert-CssClass -htmldata $allConnectivityObject $allConnectivityObject } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ComponentConnectivityHealthNonSOS Function Publish-ComponentConnectivityHealth { <# .SYNOPSIS Request and publish Component Connectivity Health. .DESCRIPTION The Publish-ComponentConnectivityHealth cmdlet checks component connectivity across the VMware Cloud Foundation instance and prepares the data to be published to an HTML report. The cmdlet connects to SDDC Manager using the -server, -user, and password values: - Validates that network connectivity is available to the SDDC Manager instance - Performs connectivityy health checks and outputs the results .EXAMPLE Publish-ComponentConnectivityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -allDomains This example checks the component connectivity for all workload domains across the VMware Cloud Foundation instance. .EXAMPLE Publish-ComponentConnectivityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -workloadDomain sfo-w01 This example checks the component connectivity for a single workload domain in a VMware Cloud Foundation instance. .EXAMPLE Publish-ComponentConnectivityHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -json <json-file> -allDomains -failureOnly This example checks the component connectivity for all workload domains across the VMware Cloud Foundation instance but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER json The full path to the JSON file to output the results to. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$json, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { $allConnectivityObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey("allDomains")) { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -alldomains -failureOnly; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -alldomains -failureOnly; $allConnectivityObject += $NsxtConnectivity } else { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain -failureOnly; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain -failureOnly; $allConnectivityObject += $NsxtConnectivity } $apiSshConnectivity = Publish-ConnectivityHealth -json $json -failureOnly $pingConnectivity = Publish-PingConnectivityHealth -json $json -failureOnly } else { if ($PsBoundParameters.ContainsKey("allDomains")) { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -alldomains; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -alldomains; $allConnectivityObject += $NsxtConnectivity } else { $vcenterConnectivity = Request-VcenterAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain; $allConnectivityObject += $vcenterConnectivity $NsxtConnectivity = Request-NsxtAuthentication -server $server -user $user -pass $pass -workloadDomain $workloadDomain; $allConnectivityObject += $NsxtConnectivity } $apiSshConnectivity = Publish-ConnectivityHealth -json $json $pingConnectivity = Publish-PingConnectivityHealth -json $json } $allConnectivityObject += $apiSshConnectivity $allConnectivityObject += $pingConnectivity if ($allConnectivityObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allConnectivityObject = $allConnectivityObject | Sort-Object Component, Resource, Message | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -PostContent '<p>No issues found.</p>' } else { $allConnectivityObject = $allConnectivityObject | Sort-Object Component, Resource, Message | ConvertTo-Html -Fragment -PreContent '<a id="general-connectivity"></a><h3>Connectivity Health Status</h3>' -As Table } $allConnectivityObject = Convert-CssClass -htmldata $allConnectivityObject $allConnectivityObject } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ComponentConnectivityHealth Function Request-VcenterAuthentication { <# .SYNOPSIS Checks API authentication to vCenter Server instance. .DESCRIPTION The Request-VcenterAuthentication cmdlets checks the authentication to vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance .EXAMPLE Request-VcenterAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will check authentication to vCenter Server API for all vCenter Server instances managed by SDDC Manager. .EXAMPLE Request-VcenterAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will check authentication to vCenter Server API for a single workload domain .EXAMPLE Request-VcenterAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will check authentication to vCenter Server API for all vCenter Server instances but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $account = (Get-VCFCredential | Where-Object {$_.accountType -eq "SYSTEM" -and $_.resource.resourceType -eq "PSC"}) $customObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey("allDomains")) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain.name)) { if (Test-vSphereApiAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass -ErrorAction SilentlyContinue) { $alert = "GREEN" $message = "API Connection check successful!" } else { $alert = "RED" $message = "API Connection check failed!" } $elementObject = New-Object System.Collections.ArrayList $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "vCenter" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfVcenterDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } } else { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $workloadDomain)) { if (Test-vSphereApiAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass -ErrorAction SilentlyContinue) { $alert = "GREEN" $message = "API Connection check successful!" } else { $alert = "RED" $message = "API Connection check failed!" } $elementObject = New-Object System.Collections.ArrayList $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "vCenter" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfVcenterDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } $customObject | Sort-Object Component, Resource } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VcenterAuthentication Function Request-NsxtAuthentication { <# .SYNOPSIS Checks API authentication to NSX Manager cluster. .DESCRIPTION The Request-NsxtAuthentication cmdlets checks the authentication to NSX Manager cluster. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the NSX Manager cluster .EXAMPLE Request-NsxtAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will check authentication to NSX Manager API for all NSX Manager clusters managed by SDDC Manager. .EXAMPLE Request-NsxtAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will check authentication to NSX Manager API for a single workload domain .EXAMPLE Request-NsxtAuthentication -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will check authentication to NSX Manager API for all NSX Manager clusters but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $customObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey("allDomains")) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains) { $vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain.name -listNodes foreach ($node in $vcfNsxDetails.nodes) { if (Test-NsxtConnection -server $node.fqdn -ErrorAction SilentlyContinue -ErrorVariable ErrorMessage ) { if (Test-NsxtAuthentication -server $node.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $alert = "GREEN" $message = "API Connection check successful!" } else { $alert = "RED" $message = "API Connection check failed!" } } else { $alert = "RED" $message = "API Connection check failed! " + $ErrorMessage } $elementObject = New-Object System.Collections.ArrayList $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "NSX" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $node.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } } else { $vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $workloadDomain -listNodes foreach ($node in $vcfNsxDetails.nodes) { if (Test-NsxtConnection -server $node.fqdn -ErrorAction SilentlyContinue -ErrorVariable ErrorMessage ) { if (Test-NsxtAuthentication -server $node.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $alert = "GREEN" $message = "API Connection check successful!" } else { $alert = "RED" $message = "API Connection check failed!" } } else { $alert = "RED" $message = $ErrorMessage } $elementObject = New-Object System.Collections.ArrayList $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue "NSX" $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $node.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } $customObject | Sort-Object Component, Resource } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtAuthentication Function Request-NsxtTransportNodeStatus { <# .SYNOPSIS Returns the status of NSX transport nodes managed by an NSX Manager cluster. .DESCRIPTION The Request-NsxtTransportNodeStatus cmdlet returns the status NSX transport nodes managed by an NSX Manager cluster. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the NSX Manager cluster from the SDDC Manager - Validates network connectivity and authentication to the NSX Local Manager cluster - Collects the status of the transport nodes .EXAMPLE Request-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the NSX transport nodes managed by an NSX Manager cluster which is managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtTransportNodeStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the NSX transport nodes managed by an NSX Manager cluster which is managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -First 1) -pass ($vcfNsxDetails.adminPass | Select-Object -First 1)) { $customObject = New-Object System.Collections.ArrayList # NSX Transport Nodes $types = @("edge","host") foreach ($type in $types) { $resource = $vcfNsxDetails.fqdn # Define the resource name $transportNodeStatus = (Get-NsxtTransportNodeStatus -type $type) # Get the status of the transport nodes $nodeType = (Get-Culture).textinfo.ToTitleCase($type.ToLower()) # Convert the type to title case # Set the alert and message based on the status of the transport node if ($downCount -ge 0 -or $unknownCount -ge 0) { $alert = 'RED' # Critical, transport node(s) down or unknown $message = $nodeType + ' transport node(s) in down or unknown state.' # Set the alert message } elseif ($degradedCount -ge 0) { $alert = 'YELLOW' # Warning, transport node(s) degraded $message = $nodeType + ' transport node(s) in degraded state.' # } else { $alert = 'GREEN' # OK, transport node(s) up $message = $nodeType + ' transport node(s) in up state.' # Set the alert message } # Add the properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $nodeType # Set the node type $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the message $elementObject | Add-Member -NotePropertyName 'Up' -NotePropertyValue $transportNodeStatus.up_count # Set the up count $elementObject | Add-Member -NotePropertyName 'Down' -NotePropertyValue $transportNodeStatus.down_count # Set the down count $elementObject | Add-Member -NotePropertyName 'Degraded' -NotePropertyValue $transportNodeStatus.degraded_count # Set the degraded count $elementObject | Add-Member -NotePropertyName 'Unknown' -NotePropertyValue $transportNodeStatus.unknown_count # Set the unknown count $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($elementObject.alert -eq 'RED') { $customObject += $elementObject } } else { $customObject += $elementObject } } } $customObject | Sort-Object Domain, Resource, Element } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtTransportNodeStatus Function Request-NsxtTransportNodeTunnelStatus { <# .SYNOPSIS Returns the status of NSX transport node tunnels. .DESCRIPTION The Request-NsxtTransportNodeTunnelStatus cmdlet returns the status NSX transport nodes tunnels. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates network connectivity and authentication to the SDDC Manager instance - Gathers the details for the NSX Manager cluster from the SDDC Manager - Validates network connectivity and authentication to the NSX Local Manager cluster - Collects the status of the transport node tunnels .EXAMPLE Request-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the status of the NSX transport node tunnels for a workload domain. .EXAMPLE Request-NsxtTransportNodeTunnelStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the status of the NSX transport node tunnels for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -First 1) -pass ($vcfNsxDetails.adminPass | Select-Object -First 1)) { $customObject = New-Object System.Collections.ArrayList # NSX Transport Nodes $types = @("edge","host") foreach ($type in $types) { $transportNodes = Get-NsxtTransportNode -type $type foreach ($transportNode in $transportNodes) { $resource = $vcfNsxDetails.fqdn # Define the resource name $nodeType = (Get-Culture).textinfo.ToTitleCase($type.ToLower()) # Convert the type to title case $tunnels = (Get-NsxtTransportNodeTunnel -id $transportNode.id).tunnels # Get the tunnels for the transport node foreach ($tunnel in $tunnels) { # Set the alert and message based on the status of the tunnel if ($tunnel.status -eq 'UP') { $alert = 'GREEN' # OK, transport node up $message = $nodeType + ' transport node tunnel is up.' # Set the alert message } else { $alert = 'RED' # Critical, transport node down or unknown $message = $nodeType + ' transport node tunnel is down or in unknown state.' # Set the alert message } # Update the alert and message based on the status of BFD if ($tunnel.bfd.state -ne 'UP') { $alert = 'YELLOW' # WARNING, BFD down or unknown $message = "Bidirectional forwarding is down or in unknown state. Run '(Get-NsxtTransportNodeTunnelStatus -id $($transportNode.id)).tunnels' for more information." # Set the alert message } # Add the properties to the element object $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $resource # Set the resource name $elementObject | Add-Member -NotePropertyName 'Element' -NotePropertyValue $nodeType # Set the element name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain $elementObject | Add-Member -NotePropertyName 'Source IP' -NotePropertyValue $tunnel.local_ip # Set the source IP $elementObject | Add-Member -NotePropertyName 'Remote IP' -NotePropertyValue $tunnel.remote_ip # Set the remote IP $elementObject | Add-Member -NotePropertyName 'Source Node' -NotePropertyValue $transportNode.display_name # Set the source name $elementObject | Add-Member -NotePropertyName 'Remote Node' -NotePropertyValue $tunnel.remote_node_display_name # Set the source name $elementObject | Add-Member -NotePropertyName 'Interface' -NotePropertyValue $tunnel.egress_interface # Set the egress interface $elementObject | Add-Member -NotePropertyName 'Status' -NotePropertyValue $tunnel.status # Set the status $elementObject | Add-Member -NotePropertyName 'BFD' -NotePropertyValue $tunnel.bfd.state # Set the BFD status $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue "$message" # Set the message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($elementObject.alert -eq 'RED' -or $elementObject.alert -eq 'YELLOW') { $customObject += $elementObject } } else { $customObject += $elementObject } } } $outputObject += $customObject } } $outputObject | Sort-Object Domain, Resource, Element, 'Local Transport Node', Interface } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtTransportNodeTunnelStatus Function Request-NsxtTier0BgpStatus { <# .SYNOPSIS Returns the BGP status for all Tier-0 gateways managed by the NSX Local Manager cluster. .DESCRIPTION The Request-NsxtTier0BgpStatus cmdlet returns the BGP status for all Tier-0 gateways managed by the NSX Manager cluster. The cmdlet connects to the NSX Local Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the NSX Local Manager cluster - Gathers the details for the NSX Local Manager cluster - Collects the BGP status for all Tier-0s managed by the NSX Local Manager cluster .EXAMPLE Request-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return the BGP status for all Tier-0 gateways managed by the NSX Local Manager cluster that is managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtTier0BgpStatus -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return the BGP status for all Tier-0 gateways managed by the NSX Local Manager cluster that is managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $customObject = New-Object System.Collections.ArrayList if ($tier0s = Get-NsxtTier0Gateway) { foreach ($tier0 in $tier0s) { $bgpStatus = Get-NsxtTier0BgpStatus -id $tier0.id | Where-Object {$_.type -eq 'USER'} $localAsn = (Get-NsxtTier0LocaleServiceBgp -id $tier0.id).local_as_num foreach ($element in $bgpStatus) { if ($element.connection_state -eq 'ESTABLISHED') { $alert = "GREEN" $message = "BGP is established." } else { $alert = "RED" $message = "BGP is not established. Please check the configuration." } $elementObject = New-Object -TypeName psobject # NSX Tier-0 BGP Status Properties $elementObject | Add-Member -NotePropertyName 'NSX Manager' -NotePropertyValue $vcfNsxDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Tier-0 ID' -NotePropertyValue $tier0.id $elementObject | Add-Member -NotePropertyName 'Connection' -NotePropertyValue $element.connection_state $elementObject | Add-Member -NotePropertyName 'Source Address' -NotePropertyValue $element.source_address $elementObject | Add-Member -NotePropertyName 'Neighbor Address' -NotePropertyValue $element.neighbor_address $elementObject | Add-Member -NotePropertyName 'Local ASN' -NotePropertyValue $localAsn $elementObject | Add-Member -NotePropertyName 'Remote ASN' -NotePropertyValue $element.remote_as_number $elementObject | Add-Member -NotePropertyName 'Hold' -NotePropertyValue $element.hold_time $elementObject | Add-Member -NotePropertyName 'Keep Alive ' -NotePropertyValue $element.keep_alive_interval $elementObject | Add-Member -NotePropertyName 'Established Time (sec)' -NotePropertyValue $element.time_since_established $elementObject | Add-Member -NotePropertyName 'Total In Prefix' -NotePropertyValue $element.total_in_prefix_count $elementObject | Add-Member -NotePropertyName 'Total Out Prefix' -NotePropertyValue $element.total_out_prefix_count $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($element.connection_state -ne 'ESTABLISHED') { $customObject += $elementObject } } else { $customObject += $elementObject } } } } $customObject | Sort-Object 'NSX Manager', 'Domain', 'Tier-0 ID', 'Source Address' } } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-NsxtTier0BgpStatus Function Publish-VmConnectedCdrom { <# .SYNOPSIS Publish the status of virtual machines with connected CD-ROMs in a workload domain in HTML format. .DESCRIPTION The Publish-VmConnectedCdrom cmdlet returns the status of virtual machines with connected CD-ROMS in a workload domain in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-VmConnectedCdrom -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will returns the status of virtual machines with connected CD-ROMs in all workload domains. .EXAMPLE Publish-VmConnectedCdrom -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will returns the status of virtual machines with connected CD-ROMs in a workload domain. .EXAMPLE Publish-VmConnectedCdrom -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json with the status of virtual machines with connected CD-ROMs in all workload domains and saves it under F:\Reporting with filename <timestamp>-cdrom-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allHealthObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $vmConnectedCdrom = Request-VmConnectedCdrom -server $server -user $user -pass $pass -domain $domain.name; $allHealthObject += $vmConnectedCdrom } } else { $vmConnectedCdrom = Request-VmConnectedCdrom -server $server -user $user -pass $pass -domain $workloadDomain; $allHealthObject += $vmConnectedCdrom } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $cdRomJsonSuffix $allHealthObject | ConvertTo-JSON -Depth 10 | Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allHealthObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allHealthObject = $allHealthObject | ConvertTo-Html -Fragment -PreContent '<a id="storage-vm-cdrom"></a><h3>Virtual Machines with Connected CD-ROMs</h3>' -PostContent '<p>No virtual machines with connected CD-ROMs found.</p>' } else { $allHealthObject = $allHealthObject | Sort-Object Cluster, 'VM Name' | ConvertTo-Html -Fragment -PreContent '<a id="storage-vm-cdrom"></a><h3>Virtual Machines with Connected CD-ROMs</h3>' -As Table } $allHealthObject = Convert-CssClass -htmlData $allHealthObject $allHealthObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VmConnectedCdrom Function Request-VmConnectedCdrom { <# .SYNOPSIS Returns the status of virtual machines with connected CD-ROMs in a workload domain. .DESCRIPTION The Request-VmConnectedCdrom cmdlet returns the status of virtual machines with connected CD-ROMs in a workload domain. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the status of virtual machines with connected CD-ROMs in a workload domain. .EXAMPLE Request-VmConnectedCdrom -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example returns the status of virtual machines with connected CD-ROMs in a workload domain. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { #$allVms = Get-VM -Server $vcfVcenterDetails.fqdn | Where-Object { $_ | Get-CDDrive | Where-Object { $_.ConnectionState.Connected -eq 'true' } } | Select-Object Name, @{Name = 'ISO Path'; Expression = { (Get-CDDrive $_).isopath } } $allVms = Get-VM -Server $vcfVcenterDetails.fqdn | Select-Object Name, @{Name = 'ISO Path'; Expression = { $cdDrive = $_ | Get-CDDrive | Where-Object { $_.ConnectionState.Connected -eq 'true' }; if ($cdDrive) { $cdDrive.isopath } else { 'N/A' } } } | Sort-Object "ISO Path" foreach ($vm in $allVms) { # Set the alert and message based on the CD-ROM connection # Warning, connected CD-ROM if ($vm.'ISO Path' -eq "N/A"){ $alert = 'GREEN' $message = 'No virtual CD-ROM connected.' } else { $alert = 'YELLOW' $message = 'A virtual CD-ROM is connected.' } # Set the object properties $customObject = New-Object -TypeName psobject $customObject | Add-Member -NotePropertyName 'vCenter Server' -NotePropertyValue $vcfVcenterDetails.fqdn $customObject | Add-Member -NotePropertyName 'Cluster' -NotePropertyValue $cluster.Name $customObject | Add-Member -NotePropertyName 'VM Name' -NotePropertyValue $vm.Name $customObject | Add-Member -NotePropertyName 'ISO Path' -NotePropertyValue $vm.'ISO Path' $customObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $customObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message $allClustersObject += $customObject } } $allClustersObject | Sort-Object -Property Alert -Descending } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VmConnectedCdrom Function Publish-EsxiConnectionHealth { <# .SYNOPSIS Publish the connection status of ESXi hosts in a workload domain in HTML format. .DESCRIPTION The Publish-EsxiConnectionHealth cmdlet returns the status of virtual machines with connected CD-ROMS in a workload domain in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will publish the connection status of ESXi hosts in all workload domains. .EXAMPLE Publish-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will publish the connection status of ESXi hosts in a workload domain. .EXAMPLE Publish-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will publish the connection status of ESXi hosts in all workload domains but only for failures. .EXAMPLE Publish-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -outputJson F:\Reporting This example will generate a json for the connection status of ESXi hosts in all workload domains and saves it under F:\Reporting with filename <timestamp>-esxi-connection-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allHealthObject = New-Object System.Collections.ArrayList $allWorkloadDomains = Get-VCFWorkloadDomain if ($PsBoundParameters.ContainsKey("allDomains") -and $PsBoundParameters.ContainsKey("failureOnly")) { foreach ($domain in $allWorkloadDomains ) { $esxiConnectionStatus = Request-EsxiConnectionHealth -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allHealthObject += $esxiConnectionStatus } } elseif ($PsBoundParameters.ContainsKey("allDomains")) { foreach ($domain in $allWorkloadDomains ) { $esxiConnectionStatus = Request-EsxiConnectionHealth -server $server -user $user -pass $pass -domain $domain.name; $allHealthObject += $esxiConnectionStatus } } if ($PsBoundParameters.ContainsKey("workloadDomain") -and $PsBoundParameters.ContainsKey("failureOnly")) { $esxiConnectionStatus = Request-EsxiConnectionHealth -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allHealthObject += $esxiConnectionStatus } elseif ($PsBoundParameters.ContainsKey("workloadDomain")) { $esxiConnectionStatus = Request-EsxiConnectionHealth -server $server -user $user -pass $pass -domain $workloadDomain; $allHealthObject += $esxiConnectionStatus } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $esxiConnectionHealthJsonSuffix $allHealthObject | ConvertTo-JSON -Depth 10 -EnumsAsStrings| Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" } else { if ($allHealthObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allHealthObject = $allHealthObject | ConvertTo-Html -Fragment -PreContent '<a id="esxi-connection"></a><h3>ESXi Connection Health</h3>' -PostContent '<p>No issues found.</p>' } else { $allHealthObject = $allHealthObject | Sort-Object Resource, Cluster | ConvertTo-Html -Fragment -PreContent '<a id="esxi-connection"></a><h3>ESXi Connection Health</h3>' -As Table } $allHealthObject = Convert-CssClass -htmlData $allHealthObject $allHealthObject } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-EsxiConnectionHealth Function Request-EsxiConnectionHealth { <# .SYNOPSIS Returns the connection status of ESXi hosts in a workload domain. .DESCRIPTION The Request-EsxiConnectionHealth cmdlet returns the connection status of ESXi hosts in a workload domain. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the connection status of ESXi hosts in a workload domain. .EXAMPLE Request-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example returns the connection status of ESXi hosts in a workload domain. .EXAMPLE Request-EsxiConnectionHealth -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example returns the connection status of ESXi hosts in a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $esxihosts = Get-VMHost -Server $vcfVcenterDetails.fqdn foreach ($esxiHost in $esxiHosts) { $component = "ESXi" # Set the alert and message based on the CD-ROM connection if ($esxiHost.ConnectionState -eq 'Connected') { $alert = 'GREEN' # Ok, connected $message = 'Host is connected.' # Set the status message } elseif ($esxiHost.ConnectionState -eq 'Maintenance') { $alert = 'YELLOW' # Warning, maintenance $message = 'Host is in maintenance mode.' # Set the status message } elseif ($esxiHost.ConnectionState -eq 'Disconnected') { $alert = 'RED' # Critical, disconnected $message = 'Host is disconnected.' # Set the status message } else { $alert = 'RED' # Critical, unknown state $message = 'Host is in an unknown state.' # Set the status message } # Set the object properties $customObject = New-Object -TypeName psobject $customObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue $component $customObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $esxiHost.Name $customObject | Add-Member -NotePropertyName 'Cluster' -NotePropertyValue $cluster.Name $customObject | Add-Member -NotePropertyName 'Connection' -NotePropertyValue $esxiHost.ConnectionState $customObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $customObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($esxiHost.ConnectionState -ne 'Connected' ) { $allClustersObject += $customObject } } else { $allClustersObject += $customObject } } } $allClustersObject | Sort-Object Resource, Cluster } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-EsxiConnectionHealth Function Publish-SddcManagerFreePool { <# .SYNOPSIS Publish SDDC Manager free pool health information in HTML format. .DESCRIPTION The Publish-SddcManagerFreePool cmdlet returns SDDC Manager free pool information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates the network connectivity and authentication to the SDDC Manager instance - Publishes information .EXAMPLE Publish-SddcManagerFreePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return the free pool health from SDDC Manager. .EXAMPLE Publish-SddcManagerFreePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -failureOnly This example will return the free pool health from SDDC Manager and return the failures only. .EXAMPLE Publish-SddcManagerFreePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -outputJson F:\Reporting This example will generate a json for the status the free pool health from SDDC Manager and saves it under F:\Reporting with filename <timestamp>-sddc-manager-free-pool-status.json .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER outputJson The path to save the output as a JSON file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputJson ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList $unassignedEsxiHosts = (Get-VCFHost | Where-Object {$_.status -eq "UNASSIGNED_USEABLE"}) if ($unassignedEsxiHosts.Count -gt 0) { if ($PsBoundParameters.ContainsKey('failureOnly')) { $allConfigurationObject = Request-SddcManagerFreePool -server $server -user $user -pass $pass -failureOnly } else { $allConfigurationObject = Request-SddcManagerFreePool -server $server -user $user -pass $pass } if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $sddcFreePoolJsonSuffix $allConfigurationObject | ConvertTo-JSON -Depth 10 -EnumsAsStrings| Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" return } else { if ($allConfigurationObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allConfigurationObject = $allConfigurationObject | ConvertTo-Html -Fragment -PreContent '<a id="esxi-free-pool"></a><h3>Free Pool Health</h3>' -As Table -PostContent '<p>No issues found.</p>' } else { $allConfigurationObject = $allConfigurationObject | ConvertTo-Html -Fragment -PreContent '<a id="esxi-free-pool"></a><h3>Free Pool Health</h3>' -As Table } } } else { if ($PsBoundParameters.ContainsKey('outputJson')) { $json = Start-CreateOutputJsonDirectory -jsonFolder $outputJson -jsonFileSuffix $sddcFreePoolJsonSuffix $allConfigurationObject | ConvertTo-JSON -Depth 10 -EnumsAsStrings| Out-File $json -Encoding ASCII Write-Output "JSON Created at $json" return }else{ $allConfigurationObject = $allConfigurationObject | ConvertTo-Html -Fragment -PreContent '<a id="esxi-free-pool"></a><h3>Free Pool Health</h3>' -As Table -PostContent '<p>No ESXi hosts present in the free pool.</p>' } } $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-SddcManagerFreePool Function Request-SddcManagerFreePool { <# .SYNOPSIS Returns the status of the ESXi hosts in the free pool. .DESCRIPTION The Request-SddcManagerFreePool cmdlet returns status of the ESXi hosts in the free pool. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication is possible to the SDDC Manager instance - Gathers the details for the ESXi hosts in the free pool .EXAMPLE Request-SddcManagerFreePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return the ESXi hosts in the free pool managed by SDDC Manager for a workload domain. .EXAMPLE Request-SddcManagerFreePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -failureOnly This example will return the ESXi hosts in the free pool managed by SDDC Manager for a workload domain but only reports issues. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $versionList = New-Object System.Collections.ArrayList $customObject = New-Object System.Collections.ArrayList $assignedEsxiHost = (Get-VCFHost | Where-Object {$_.status -eq "ASSIGNED"}) foreach ($esxiHost in $assignedEsxiHost) { if ($versionList -notcontains $esxiHost.esxiVersion ) { $versionList += $esxiHost.esxiVersion } } $unassignedEsxiHosts = (Get-VCFHost | Where-Object {$_.status -eq "UNASSIGNED_USEABLE"}) foreach ($esxiHost in $unassignedEsxiHosts) { if ($esxiHost.status -eq "UNASSIGNED_USEABLE") { foreach ($version in $versionList) { if ($esxiHost.esxiVersion -eq $version) { $alert = "GREEN" $message = "Current ESXi Host version $($esxiHost.esxiVersion) matches supported version(s)." } else { $alert = "RED" $message = "Current ESXi Host version $($esxiHost.esxiVersion), does not match supported version(s)." } } $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue 'ESXi Version' $elementObject | Add-Member -NotePropertyName 'ESXi FQDN' -NotePropertyValue $esxiHost.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($alert -eq "RED") { $customObject += $elementObject } } else { $customObject += $elementObject } $esxiCreds = Get-VCFCredential | Where-Object {$_.resource.resourceName -eq $esxiHost.fqdn -and $_.username -eq "root"} Connect-VIServer -Server $esxiHost.fqdn -User $esxiCreds.username -Password $esxiCreds.password | Out-Null $licenseManager = Get-View -Id "LicenseManager-ha-license-manager" foreach ($properties in $licenseManager.Evaluation.Properties) { if ($properties.key -eq "expirationDate") { $expirationDate = $properties.value $expiryDate = [math]::Ceiling((([DateTime]$expirationDate) - (Get-Date)).TotalDays) } elseif ($properties.key -eq "diagnostic") { $expiryDate = 0 } } if ($expiryDate -gt "0") { $alert = "GREEN" $message = "No expired license running on the host." } else { $alert = "RED" $message = "License installed on the ESXi host has expired." } $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Component' -NotePropertyValue 'ESXi License' $elementObject | Add-Member -NotePropertyName 'ESXi FQDN' -NotePropertyValue $esxiHost.fqdn $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert $elementObject | Add-Member -NotePropertyName 'Message' -NotePropertyValue $message Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($alert -eq "RED") { $customObject += $elementObject } } else { $customObject += $elementObject } } } $customObject | Sort-Object Component, 'ESXi FQDN' } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-SddcManagerFreePool ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ####################################################################################################################### ################################### S Y S T E M A L E R T F U N C T I O N S #################################### Function Publish-EsxiAlert { <# .SYNOPSIS Publish system alerts/alarms from ESXi hosts in a vCenter Server instance managed by SDDC Manager. .DESCRIPTION The Publish-EsxiAlert cmdlet returns all alarms from ESXi hosts managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the alerts from all ESXi hosts in vCenter Server instance .EXAMPLE Publish-EsxiAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return alarms from all ESXi hosts in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-EsxiAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will return alarms from all ESXi hosts in vCenter Server managed by SDDC Manager for a all workload domains but only for the failed items. .EXAMPLE Publish-EsxiAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return alarms from all ESXi hosts in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allAlertObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $esxiSystemAlert = Request-EsxiAlert -server $server -user $user -pass $pass $domain.name -failureOnly; $allAlertObject += $esxiSystemAlert } } else { $esxiSystemAlert = Request-EsxiAlert -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allAlertObject += $esxiSystemAlert } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $esxiSystemAlert = Request-EsxiAlert -server $server -user $user -pass $pass $domain.name; $allAlertObject += $esxiSystemAlert } } else { $esxiSystemAlert = Request-EsxiAlert -server $server -user $user -pass $pass -domain $workloadDomain; $allAlertObject += $esxiSystemAlert } } if ($allAlertObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-esxi"></a><h3>ESXi Host Alert</h3>' -PostContent '<p>No alerts found.</p>' } else { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-esxi"></a><h3>ESXi Host Alerts</h3>' -As Table } $allAlertObject = Convert-CssClass -htmldata $allAlertObject $allAlertObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-EsxiAlert Function Publish-NsxtAlert { <# .SYNOPSIS Publish system alerts/alarms from a NSX Manager cluster managed by SDDC Manager. .DESCRIPTION The Publish-NsxtAlert cmdlet returns all alarms from an NSX Manager cluster. The cmdlet connects to the NSX Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the NSX Manager cluster - Validates that network connectivity is available to the vCenter Server instance - Gathers the details for the NSX Manager cluster - Collects the alerts .EXAMPLE Publish-NsxtAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return alarms from all NSX Manager clusters managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-NsxtAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will return alarms from all NSX Manager clusters managed by SDDC Manager for a all workload domains but only for the failed items. .EXAMPLE Publish-NsxtAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return alarms from the NSX Manager cluster managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allAlertObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey("allDomains")) { foreach ($domain in $allWorkloadDomains ) { $nsxtSystemAlert = Request-NsxtAlert -server $server -user $user -pass $pass -domain $domain.name -failureOnly; $allAlertObject += $nsxtSystemAlert } } else { $nsxtSystemAlert = Request-NsxtAlert -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allAlertObject += $nsxtSystemAlert } } else { if ($PsBoundParameters.ContainsKey("allDomains")) { foreach ($domain in $allWorkloadDomains ) { $nsxtSystemAlert = Request-NsxtAlert -server $server -user $user -pass $pass -domain $domain.name; $allAlertObject += $nsxtSystemAlert } } else { $nsxtSystemAlert = Request-NsxtAlert -server $server -user $user -pass $pass -domain $workloadDomain; $allAlertObject += $nsxtSystemAlert } } if ($allAlertObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-nsx"></a><h3>NSX Alert</h3>' -PostContent '<p>No alerts found.</p>' } else { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-nsx"></a><h3>NSX Alerts</h3>' -As Table } $allAlertObject = Convert-CssClass -htmldata $allAlertObject $allAlertObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-NsxtAlert Function Publish-VcenterAlert { <# .SYNOPSIS Returns alarms from vCenter Server managed by SDDC Manager. .DESCRIPTION The Publish-VcenterAlert cmdlet returns all alarms from vCenter Server managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the alerts from vCenter Server .EXAMPLE Publish-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return alarms from a vCenter Server managed by SDDC Manager for all workload domains. .EXAMPLE Publish-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return alarms from a vCenter Server managed by SDDC Manager for all workload domains but only for the failed items. .EXAMPLE Publish-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return alarms from a vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allAlertObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $vcenterSystemAlert = Request-VcenterAlert -server $server -user $user -pass $pass $domain.name -failureOnly; $allAlertObject += $vcenterSystemAlert } } else { $vcenterSystemAlert = Request-VcenterAlert -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allAlertObject += $vcenterSystemAlert } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $vcenterSystemAlert = Request-VcenterAlert -server $server -user $user -pass $pass $domain.name; $allAlertObject += $vcenterSystemAlert } } else { $vcenterSystemAlert = Request-VcenterAlert -server $server -user $user -pass $pass -domain $workloadDomain; $allAlertObject += $vcenterSystemAlert } } if ($allAlertObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-vcenter"></a><h3>vCenter Server Alert</h3>' -PostContent '<p>No alerts found.</p>' } else { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-vcenter"></a><h3>vCenter Server Alerts</h3>' -As Table } $allAlertObject = Convert-CssClass -htmldata $allAlertObject $allAlertObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VcenterAlert Function Publish-VsanAlert { <# .SYNOPSIS RPublish the vSAN Healthcheck alarms from a vCenter Server instance. .DESCRIPTION The Publish-VsanAlert cmdlet returns vSAN Healthcheck alarms from vCenter Server managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the vSAN Healthcheck alarms from vCenter Server .EXAMPLE Publish-VsanAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return vSAN Healthcheck alarms for all vCenter Server instances managed by SDDC Manager for a workload domain. .EXAMPLE Publish-VsanAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains -failureOnly This example will return vSAN Healthcheck alarms for all vCenter Server instances managed by SDDC Manager for a workload domain but only failed items. .EXAMPLE Publish-VsanAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return vSAN Healthcheck alarms of a vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allAlertObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('failureOnly')) { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $vsanSystemAlert = Request-VsanAlert -server $server -user $user -pass $pass $domain.name -failureOnly; $allAlertObject += $vsanSystemAlert } } else { $vsanSystemAlert = Request-VsanAlert -server $server -user $user -pass $pass -domain $workloadDomain -failureOnly; $allAlertObject += $vsanSystemAlert } } else { if ($PsBoundParameters.ContainsKey('allDomains')) { foreach ($domain in $allWorkloadDomains ) { $vsanSystemAlert = Request-VsanAlert -server $server -user $user -pass $pass $domain.name; $allAlertObject += $vsanSystemAlert } } else { $vsanSystemAlert = Request-VsanAlert -server $server -user $user -pass $pass -domain $workloadDomain; $allAlertObject += $vsanSystemAlert } } if ($allAlertObject.Count -eq 0) { $addNoIssues = $true } if ($addNoIssues) { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-vsan"></a><h3>vSAN Alert</h3>' -PostContent '<p>No alerts found.</p>' } else { $allAlertObject = $allAlertObject | Sort-Object Component, Resource, Domain | ConvertTo-Html -Fragment -PreContent '<a id="alert-vsan"></a><h3>vSAN Alerts</h3>' -As Table } $allAlertObject = Convert-CssClass -htmldata $allAlertObject $allAlertObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VsanAlert Function Request-NsxtAlert { <# .SYNOPSIS Returns alarms from an NSX Manager cluster. .DESCRIPTION The Request-NsxtAlert cmdlet returns all alarms from an NSX Manager cluster. The cmdlet connects to the NSX Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the NSX Manager cluster - Validates that network connectivity is available to the vCenter Server instance - Gathers the details for the NSX Manager cluster - Collects the alerts .EXAMPLE Request-NsxtAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return alarms of an NSX Manager cluster managed by SDDC Manager for a workload domain. .EXAMPLE Request-NsxtAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return alarms of an NSX Manager cluster managed by SDDC Manager for a workload domain but only for the failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user ($vcfNsxDetails.adminUser | Select-Object -first 1) -pass ($vcfNsxDetails.adminPass | Select-Object -first 1)) { $nsxtAlarms = Get-NsxtAlarm -fqdn $vcfNsxDetails.fqdn # Get the NSX alarms $customObject = New-Object System.Collections.ArrayList foreach ($alarm in $nsxtAlarms.results) { if ($alarm.status -eq "RESOLVED") { $alert = "GREEN" } else { $alert = "RED" } $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfNsxDetails.fqdn # Set the resource name $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain # Set the domain $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Set the alert # Alarm properties $elementObject | Add-Member -NotePropertyName 'Feature Name' -NotePropertyValue $alarm.feature_name # Set the feature_name $elementObject | Add-Member -NotePropertyName 'Event Type' -NotePropertyValue $alarm.event_type # Set the event_type $elementObject | Add-Member -NotePropertyName 'Description' -NotePropertyValue $alarm.description # Set the description $elementObject | Add-Member -NotePropertyName 'Status' -NotePropertyValue $alarm.status # Set the status $elementObject | Add-Member -NotePropertyName 'Severity' -NotePropertyValue $alarm.severity # Set the severity $elementObject | Add-Member -NotePropertyName 'Node Name' -NotePropertyValue $alarm.node_display_name # Set the node_display_name $elementObject | Add-Member -NotePropertyName 'Node IP Address' -NotePropertyValue "$($alarm.node_ip_addresses)" # Set the node_ip_addresses array converted to sting if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } $customObject | Sort-Object Component, Resource, Domain, Status } } } } } } } Export-ModuleMember -Function Request-NsxtAlert Function Request-VsanAlert { <# .SYNOPSIS Returns vSAN Healthcheck alarms from a vCenter Server instance. .DESCRIPTION The Request-VsanAlert cmdlet returns vSAN Healthcheck alarms from vCenter Server managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the vSAN Healthcheck alarms from vCenter Server .EXAMPLE Request-VsanAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return vSAN Healthcheck alarms of a vCenter Server managed by SDDC Manager for a workload domain. .EXAMPLE Request-VsanAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return vSAN Healthcheck alarms of a vCenter Server managed by SDDC Manager for a workload domain but only for the failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { foreach ($cluster in Get-Cluster -Server $vcfVcenterDetails.fqdn ) { $vsanAlarms = Get-VsanHealthTest -cluster $cluster $customObject = New-Object System.Collections.ArrayList foreach ($vsanAlarm in $vsanAlarms) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfVcenterDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Cluster' -NotePropertyValue $cluster # Alarm properties $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $vsanAlarm.TestHealth $elementObject | Add-Member -NotePropertyName 'GroupName' -NotePropertyValue $vsanAlarm.GroupName $elementObject | Add-Member -NotePropertyName 'TestName' -NotePropertyValue $vsanAlarm.TestName if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null $customObject | Sort-Object Component, Resource, Domain, Cluster, Alert } } } } } } Export-ModuleMember -Function Request-VsanAlert Function Request-VcenterAlert { <# .SYNOPSIS Returns alarms from vCenter Server managed by SDDC Manager. .DESCRIPTION The Request-VcenterAlert cmdlet returns all alarms from vCenter Server managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the alerts from vCenter Server .EXAMPLE Request-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return alarms of a vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .EXAMPLE Request-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -filterOut hostOnly This example will return alarms from ESXi hosts of a vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .EXAMPLE Request-VcenterAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return alarms from vSAN clusters of a vCenter Server managed by SDDC Manager for a workload domain but only for the failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER filterOut Filter out alarms. One of: hostOnly, vsanOnly. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateSet("hostOnly","vsanOnly")][ValidateNotNullOrEmpty()] [String]$filterOut, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $vcenterAlarms = Get-VcenterTriggeredAlarm -server $vcfVcenterDetails.fqdn # Get the vCenter alarms $customObject = New-Object System.Collections.ArrayList foreach ($alarm in $vcenterAlarms) { [String]$alert = $alarm.Status $alert = $alert.ToUpper() $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfVcenterDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Alarm properties $elementObject | Add-Member -NotePropertyName 'Entity Type' -NotePropertyValue $alarm.EntityType $elementObject | Add-Member -NotePropertyName 'Alarm' -NotePropertyValue $alarm.Alarm $elementObject | Add-Member -NotePropertyName 'Time' -NotePropertyValue $alarm.Time $elementObject | Add-Member -NotePropertyName 'Acknowledged' -NotePropertyValue $alarm.Acknowledged $elementObject | Add-Member -NotePropertyName 'Acknowledged By' -NotePropertyValue $alarm.AckBy $elementObject | Add-Member -NotePropertyName 'Acknowledged Time' -NotePropertyValue $alarm.AcknowledgedTime $addToCustomObject = $false switch ($filterOut) { "hostOnly" { if ($elementObject.EntityType -eq "HostSystem") { $addToCustomObject = $true } Break } "vsanOnly" { if (($elementObject.EntityType -eq "ClusterComputeResource") -and ($elementObject.Alarm -like "vsan*")) { $addToCustomObject = $true } Break } default { $addToCustomObject = $true } } if ($addToCustomObject){ if ($PsBoundParameters.ContainsKey('failureOnly')) { if (($elementObject.alert -eq 'RED') -or ($elementObject.alert -eq 'YELLOW')) { $customObject += $elementObject } } else { $customObject += $elementObject } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null $customObject | Sort-Object Component, Resource, Domain, 'Entity Type', Alert } } } } } } Export-ModuleMember -Function Request-VcenterAlert Function Request-EsxiAlert { <# .SYNOPSIS Returns Alarms from all ESXi hosts in vCenter Server instance. .DESCRIPTION The Request-EsxiAlert cmdlet returns all alarms from all ESXi hosts in vCenter Server managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Collects the alerts from all ESXi hosts in vCenter Server instance .EXAMPLE Request-EsxiAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 This example will return alarms from all ESXi hosts in vCenter Server managed by SDDC Manager for a workload domain sfo-w01. .EXAMPLE Request-EsxiAlert -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-w01 -failureOnly This example will return alarms from all ESXi hosts in vCenter Server managed by SDDC Manager for a workload domain sfo-w01 but only for the failed items. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. .PARAMETER failureOnly Switch to only output issues to the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$domain, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $pass = Get-Password -User $user -Password $pass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $customObject = New-Object System.Collections.ArrayList foreach ($allHosts in Get-VMHost -Server $vcfVcenterDetails.fqdn) { $esxiAlarms = Get-EsxiAlert -host $allHosts # Get the ESXi alarms foreach ($alarm in $esxiAlarms) { [String]$alert = $alarm.Status $alert = $alert.ToUpper() $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -NotePropertyName 'Resource' -NotePropertyValue $vcfVcenterDetails.fqdn $elementObject | Add-Member -NotePropertyName 'Domain' -NotePropertyValue $domain $elementObject | Add-Member -NotePropertyName 'Alert' -NotePropertyValue $alert # Alarm properties $elementObject | Add-Member -NotePropertyName 'Entity' -NotePropertyValue $alarm.Entity $elementObject | Add-Member -NotePropertyName 'Alarm' -NotePropertyValue $alarm.Alarm $elementObject | Add-Member -NotePropertyName 'Time' -NotePropertyValue $alarm.Time $elementObject | Add-Member -NotePropertyName 'Acknowledged' -NotePropertyValue $alarm.Acknowledged $elementObject | Add-Member -NotePropertyName 'Acknowledged By' -NotePropertyValue $alarm.AckBy $elementObject | Add-Member -NotePropertyName 'Acknowledged Time' -NotePropertyValue $alarm.AcknowledgedTime if ($PsBoundParameters.ContainsKey("failureOnly")) { if ((($elementObject.alert -eq 'RED') -or ($elementObject.Alert -eq 'YELLOW')) -and !($elementObject.Acknowledged)) { $customObject += $elementObject } } else { $customObject += $elementObject } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null $customObject | Sort-Object Component, Resource, Domain, Entity, Alert } } } } } } Export-ModuleMember -Function Request-EsxiAlert ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ####################################################################################################################### ################################# C O N F I G U R A T I O N F U N C T I O N S #################################### Function Publish-ClusterConfiguration { <# .SYNOPSIS Publish cluster configuration information in HTML format. .DESCRIPTION The Publish-ClusterConfiguration cmdlet returns cluster configuration information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-ClusterConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return cluster configuration from all clusters in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-ClusterConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return cluster configuration from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $clusterConfiguration = Request-ClusterConfiguration -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $clusterConfiguration } } else { $clusterConfiguration = Request-ClusterConfiguration -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $clusterConfiguration } $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster | ConvertTo-Html -Fragment -PreContent '<a id="cluster-config"></a><h3>Cluster Configuration</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ClusterConfiguration Function Publish-EsxiCoreDumpConfig { <# .SYNOPSIS Generates an ESXi core dump configuration report. .DESCRIPTION The Publish-EsxiCoreDumpConfig cmdlet generates an ESXi core dump report for a workload domain. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Generates an ESXi core dump report for all ESXi hosts in a workload domain .EXAMPLE Publish-EsxiCoreDumpConfig -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -alldomains This example generates an ESXi core dump report for all ESXi hosts across the VMware Cloud Foundation instance. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER html Switch to output the report in HTML format. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$html, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific--WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allHostObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey("allDomains")) { $allWorkloadDomains = Get-VCFWorkloadDomain $domainHostObject = New-Object System.Collections.ArrayList foreach ($domain in $allWorkloadDomains) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain.name)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $coreDumpObject = New-Object -TypeName psobject $esxiHosts = Get-VMHost foreach ($esxiHost in $esxiHosts) { $coreDumpObject = New-Object -TypeName psobject $esxcli = Get-EsxCli -VMhost $esxiHost.Name -V2 $coreDumpConfig = $esxcli.system.coredump.partition.get.invoke() $coreDumpObject | Add-Member -notepropertyname 'Domain' -notepropertyvalue $domain.name $coreDumpObject | Add-Member -notepropertyname 'Host' -notepropertyvalue $esxiHost.Name $coreDumpObject | Add-Member -notepropertyname 'Active Core Dump' -notepropertyvalue $coreDumpConfig.Active $coreDumpObject | Add-Member -notepropertyname 'Configured Core Dump' -notepropertyvalue $coreDumpConfig.Configured $domainHostObject += $coreDumpObject } } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } $allHostObject += $domainHostObject } else { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $workloadDomain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $coreDumpObject = New-Object -TypeName psobject $esxiHosts = Get-VMHost foreach ($esxiHost in $esxiHosts) { $coreDumpObject = New-Object -TypeName psobject $esxcli = Get-EsxCli -VMhost $esxiHost.Name -V2 $coreDumpConfig = $esxcli.system.coredump.partition.get.invoke() $coreDumpObject | Add-Member -notepropertyname 'Domain' -notepropertyvalue $workloadDomain $coreDumpObject | Add-Member -notepropertyname 'Host' -notepropertyvalue $esxiHost.Name $coreDumpObject | Add-Member -notepropertyname 'Active Core Dump' -notepropertyvalue $coreDumpConfig.Active $coreDumpObject | Add-Member -notepropertyname 'Configured Core Dump' -notepropertyvalue $coreDumpConfig.Configured $allHostObject += $coreDumpObject } } } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } if ($PsBoundParameters.ContainsKey('html')) { $allHostObject = $allHostObject | Sort-Object Domain, Host | ConvertTo-Html -Fragment -PreContent '<a id="esxi-coredmp"></a><h3>ESXi Core Dump Configuration</h3>' -As Table } $allHostObject = Convert-CssClass -htmldata $allHostObject $allHostObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-EsxiCoreDumpConfig Function Request-ClusterConfiguration { <# .SYNOPSIS Gets cluster configuration from a vCenter Server instance. .DESCRIPTION The Request-ClusterConfiguration cmdlets gets the cluster configuration for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the cluster details from vCenter Server .EXAMPLE Request-ClusterConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the cluster configuration for a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $haStatus = if ($cluster.HAEnabled -eq "True") { "Enabled" } else { "Disabled" } $drsStatus = if ($cluster.DrsEnabled -eq "True") { "Enabled" } else { "Disabled" } $evcStatus = if ($null -eq $cluster.EVCMode) { "Disabled" } else { $cluster.EVCMode } $clusterAdvancedSettings = Get-AdvancedSetting -Entity (Get-Cluster -Name $cluster) | Select-Object Name, Value $settingsObject = New-Object System.Collections.ArrayList foreach ($AdvancedSetting in $clusterAdvancedSettings) { $settingsObject += "$($AdvancedSetting.Name) : $($AdvancedSetting.Value)" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster Name" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "vSphere HA" -notepropertyvalue $haStatus $customObject | Add-Member -notepropertyname "vSphere DRS" -notepropertyvalue $drsStatus $customObject | Add-Member -notepropertyname "vSphere DRS Mode" -notepropertyvalue $cluster.DrsAutomationLevel $customObject | Add-Member -notepropertyname "vSphere EVC" -notepropertyvalue $evcStatus $customObject | Add-Member -Type NoteProperty -Name "Advanced Settings" -Value ($settingsObject -join ':-: ') $allClustersObject += $customObject } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-ClusterConfiguration Function Publish-ClusterDrsRule { <# .SYNOPSIS Publish cluster DRS rule information in HTML format. .DESCRIPTION The Publish-ClusterDrsRule cmdlet returns cluster DRS rule information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-ClusterDrsRule -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return cluster DRS rules from all clusters in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-ClusterDrsRule -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return cluster DRS rules from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $drsRulesConfig = Request-ClusterDrsRule -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $drsRulesConfig } } else { $drsRulesConfig = Request-ClusterDrsRule -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $drsRulesConfig } if ($allConfigurationObject.Count -ne 0) { $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'VM/Host Rule' | ConvertTo-Html -Fragment -PreContent '<a id="cluster-drs-rules"></a><h3>vSphere DRS Rules</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject } else { $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'VM/Host Rule' | ConvertTo-Html -Fragment -PreContent '<a id="cluster-drs-rules"></a><h3>vSphere DRS Rules</h3>' -PostContent '<p>No DRS Rules found.</p>' } $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ClusterDrsRule Function Request-ClusterDrsRule { <# .SYNOPSIS Gets cluster DRS rules from a vCenter Server instance. .DESCRIPTION The Request-ClusterDrsRule cmdlets gets the cluster DRS rules for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the cluster DRS rules from vCenter Server .EXAMPLE Request-ClusterDrsRule -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the cluster DRS rules for a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $drsRules = Get-Cluster -Name $cluster -Server $vcfVcenterDetails.fqdn | Get-DrsRule foreach ($drsRule in $drsRules) { $vmList = New-Object System.Collections.ArrayList foreach ($vm in $drsRule.VMIDS) { $vmName = (Get-VM -Id $vm -Server $vcfVcenterDetails.fqdn).Name $vmList += $vmName } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "VM/Host Rule" -notepropertyvalue $drsRule.Name $customObject | Add-Member -notepropertyname "Enabled" -notepropertyvalue $drsRule.Enabled $customObject | Add-Member -notepropertyname "Type" -notepropertyvalue $drsRule.Type $customObject | Add-Member -notepropertyname "VMs" -notepropertyvalue ($vmList -join ':-: ') $allClustersObject += $customObject } } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-ClusterDrsRule Function Publish-ResourcePool { <# .SYNOPSIS Publish resource pool information in HTML format. .DESCRIPTION The Publish-ResourcePool cmdlet returns resource pool information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes resource pool information .EXAMPLE Publish-ResourcePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return resource pool details from all clusters in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-ResourcePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return resource pool details from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $resourcePoolConfig = Request-ResourcePool -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $resourcePoolConfig } } else { $resourcePoolConfig = Request-ResourcePool -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $resourcePoolConfig } $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'Resource Pool' | ConvertTo-Html -Fragment -PreContent '<a id="cluster-resource-pools"></a><h3>Resource Pools</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-ResourcePool Function Request-ResourcePool { <# .SYNOPSIS Gets resource pool details from a vCenter Server instance. .DESCRIPTION The Request-ResourcePool cmdlets gets the resource pool details for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the resource pool details from vCenter Server .EXAMPLE Request-ResourcePool -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the resource pool details for a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $resourcePools = Get-ResourcePool -Server $vcfVcenterDetails.fqdn foreach ($resourcePool in $resourcePools) { if ($resourcePool.Parent -ne $cluster) { $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "Resource Pool" -notepropertyvalue $resourcePool.Name $customObject | Add-Member -notepropertyname "CPU Share Level" -notepropertyvalue $resourcePool.CpuSharesLevel $customObject | Add-Member -notepropertyname "CPU Expandable" -notepropertyvalue $resourcePool.CpuExpandableReservation $customObject | Add-Member -notepropertyname "Memory Share Level" -notepropertyvalue $resourcePool.MemSharesLevel $customObject | Add-Member -notepropertyname "Memory Expandable" -notepropertyvalue $resourcePool.MemExpandableReservation $allClustersObject += $customObject } } } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-ResourcePool Function Publish-VmOverride { <# .SYNOPSIS Publish VM Override information in HTML format. .DESCRIPTION The Publish-VmOverride cmdlet returns VM Override information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-VmOverride -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return VM Override details from all clusters in vCenter Server managed by SDDC Manager for all workload domains. .EXAMPLE Publish-VmOverride -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return VM Override details from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $vmOverRideConfig = Request-VmOverride -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $vmOverRideConfig } } else { $vmOverRideConfig = Request-VmOverride -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $vmOverRideConfig } $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'DRS Automation Level', 'VM Name' | ConvertTo-Html -Fragment -PreContent '<a id="cluster-overrides"></a><h3>VM Overrides</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VmOverride Function Request-VmOverride { <# .SYNOPSIS Gets VM Override setting from a vCenter Server instance. .DESCRIPTION The Request-VmOverride cmdlets gets VM Override setting for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the VM Override settings from vCenter Server .EXAMPLE Request-VmOverride -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the VM Override setting for a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $allVms = Get-VM -Server $vcfVcenterDetails.fqdn | Select-Object Name,DrsAutomationLevel foreach ($vm in $allVms) { $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "VM Name" -notepropertyvalue $vm.Name $customObject | Add-Member -notepropertyname "DRS Automation Level" -notepropertyvalue ($vm.DrsAutomationLevel -creplace '.(?![a-z])','$& ' ) $allClustersObject += $customObject } } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VmOverride Function Publish-VirtualNetwork { <# .SYNOPSIS Publish vSphere virtual networking information in HTML format. .DESCRIPTION The Publish-VirtualNetwork cmdlet returns vSphere virtual networking information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-VirtualNetwork -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return vSphere virtual networking details from all clusters in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-VirtualNetwork -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return vSphere virtual networking details from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $virtualNetworkConfig = Request-VirtualNetwork -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $virtualNetworkConfig } } else { $virtualNetworkConfig = Request-VirtualNetwork -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $virtualNetworkConfig } $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'vSphere Distributed Switch' | ConvertTo-Html -Fragment -PreContent '<a id="cluster-networks"></a><h3>Virtual Networks</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VirtualNetwork Function Request-VirtualNetwork { <# .SYNOPSIS Gets vSphere virtual networking configuration from a vCenter Server instance. .DESCRIPTION The Request-VirtualNetwork cmdlets gets vSphere virtual networking configuration for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the vSphere virtual networking configuration from vCenter Server .EXAMPLE Request-VirtualNetwork -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the vSphere virtual networking configurationfor a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $allVds = Get-VDSwitch -Server $vcfVcenterDetails.fqdn foreach ($vds in $allVds) { $allPortgroups = Get-VDPortgroup -Server $vcfVcenterDetails.fqdn | Where-Object {$_.VDSwitch -eq $vds} $settingsObject = New-Object System.Collections.ArrayList foreach ($portgroup in $allPortgroups) { $settingsObject += "$($portgroup.Name) : $($portgroup.PortBinding)" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "vSphere Distributed Switch" -notepropertyvalue $vds.Name $customObject | Add-Member -notepropertyname "Switch Version" -notepropertyvalue $vds.Version $customObject | Add-Member -notepropertyname "MTU" -notepropertyvalue $vds.Mtu $customObject | Add-Member -notepropertyname "Portgroups" -notepropertyvalue ($settingsObject -join ':-: ') $allClustersObject += $customObject } } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-VirtualNetwork Function Publish-EsxiSecurityConfiguration { <# .SYNOPSIS Publish ESXi security information in HTML format. .DESCRIPTION The Publish-EsxiSecurityConfiguration cmdlet returns ESXi security information in HTML format. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the vCenter Server instance - Validates the authentication to vCenter Server with credentials from SDDC Manager - Publishes information .EXAMPLE Publish-EsxiSecurityConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -allDomains This example will return ESXi security details from all clusters in vCenter Server managed by SDDC Manager for a all workload domains. .EXAMPLE Publish-EsxiSecurityConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -workloadDomain sfo-w01 This example will return ESXi security details from all clusters in vCenter Server managed by SDDC Manager for a workload domain named sfo-w01. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER allDomains Switch to run health checks across all workload domains. .PARAMETER workloadDomain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (ParameterSetName = 'All-WorkloadDomains', Mandatory = $true)] [ValidateNotNullOrEmpty()] [Switch]$allDomains, [Parameter (ParameterSetName = 'Specific-WorkloadDomain', Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$workloadDomain ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allConfigurationObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('allDomains')) { $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains ) { $esxiSecurityConfig = Request-EsxiSecurityConfiguration -server $server -user $user -pass $pass -domain $domain.name; $allConfigurationObject += $esxiSecurityConfig } } else { $esxiSecurityConfig = Request-EsxiSecurityConfiguration -server $server -user $user -pass $pass -domain $workloadDomain; $allConfigurationObject += $esxiSecurityConfig } $allConfigurationObject = $allConfigurationObject | Sort-Object Cluster, 'ESXi FQDN' | ConvertTo-Html -Fragment -PreContent '<a id="esxi-security"></a><h3>Security Configuration</h3>' -As Table $allConfigurationObject = Convert-CssClass -htmldata $allConfigurationObject $allConfigurationObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-EsxiSecurityConfiguration Function Request-EsxiSecurityConfiguration { <# .SYNOPSIS Gets ESXi security configuration from a vCenter Server instance. .DESCRIPTION The Request-EsxiSecurityConfiguration cmdlets gets ESXi security configuration for a vCenter Server instance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the vCenter Server instance - Gathers the ESXi security configuration from vCenter Server .EXAMPLE Request-EsxiSecurityConfiguration -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -domain sfo-m01 This example gets the ESXi security configurationfor a vCenter Server instance based on the workload domain provided. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER domain The name of the workload domain to run against. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$domain ) Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $allClustersObject = New-Object System.Collections.ArrayList $allClusters = Get-Cluster -Server $vcfVcenterDetails.fqdn foreach ($cluster in $allClusters) { $allHosts = Get-VMHost -Server $vcfVcenterDetails.fqdn foreach ($esxHost in $allHosts) { $lockdownStatus = if ($esxHost.ExtensionData.Config.LockdownMode -eq "lockdownDisabled" ) { "False" } else { "True" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Cluster" -notepropertyvalue $cluster.Name $customObject | Add-Member -notepropertyname "ESXi FQDN" -notepropertyvalue $esxHost.Name $customObject | Add-Member -notepropertyname "SSH Enabled" -notepropertyvalue (Get-VMHostService -VMHost $esxHost | Where-Object { $_.key -eq 'TSM-SSH' }).Running $customObject | Add-Member -notepropertyname "Lockdown Enabled" -notepropertyvalue $lockdownStatus $allClustersObject += $customObject } } $allClustersObject } Disconnect-VIServer * -Force -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Request-EsxiSecurityConfiguration ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ####################################################################################################################### ############################### S Y S T E M O V E R V I E W F U N C T I O N S ################################## Function Publish-VcfSystemOverview { <# .SYNOPSIS Publishs a system overview report. .DESCRIPTION The Publish-VcfSystemOverview cmdlet returns an overview of the Vmware Cloud Foundation instance. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Collects the system overview details from the environment .EXAMPLE Publish-VcfSystemOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return system overview report for a all workload domains. .EXAMPLE Publish-VcfSystemOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return system overview report for a all workload domains, but with anonymized data. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allOverviewObject = New-Object System.Collections.ArrayList if ($PsBoundParameters.ContainsKey('anonymized')) { $vcfOverview = Request-VcfOverview -server $server -user $user -pass $pass -anonymized $hardwareOverview = Request-HardwareOverview -server $server -user $user -pass $pass $vcenterOverview = Request-VcenterOverview -server $server -user $user -pass $pass -anonymized $esxiOverview = Request-EsxiOverview -server $server -user $user -pass $pass -anonymized $clusterOverview = Request-ClusterOverview -server $server -user $user -pass $pass -anonymized $networkingOverview = Request-NetworkOverview -server $server -user $user -pass $pass -anonymized $ariaSuiteOverview = Request-VMwareAriaSuiteOverview -server $server -user $user -pass $pass -anonymized $vvsOverview = Request-ValidatedSolutionOverview -server $server -user $user -pass $pass } else { $vcfOverview = Request-VcfOverview -server $server -user $user -pass $pass $hardwareOverview = Request-HardwareOverview -server $server -user $user -pass $pass $vcenterOverview = Request-VcenterOverview -server $server -user $user -pass $pass $clusterOverview = Request-ClusterOverview -server $server -user $user -pass $pass $esxiOverview = Request-EsxiOverview -server $server -user $user -pass $pass $networkingOverview = Request-NetworkOverview -server $server -user $user -pass $pass $ariaSuiteOverview = Request-VMwareAriaSuiteOverview -server $server -user $user -pass $pass $vvsOverview = Request-ValidatedSolutionOverview -server $server -user $user -pass $pass } $vcfOverview = $vcfOverview | ConvertTo-Html -Fragment -PreContent '<h4>VMware Cloud Foundation Overview</h4>' $vcfOverview = Convert-CssClass -htmldata $vcfOverview $hardwareOverview = $hardwareOverview | ConvertTo-Html -Fragment -PreContent '<h4>Hardware Overview</h4>' $hardwareOverview = Convert-CssClass -htmldata $hardwareOverview $vcenterOverview = $vcenterOverview | ConvertTo-Html -Fragment -PreContent '<h4>vCenter Server Overview</h4>' -As Table $vcenterOverview = Convert-CssClass -htmldata $vcenterOverview $clusterOverview = $clusterOverview | ConvertTo-Html -Fragment -PreContent '<h4>vSphere Cluster Overview</h4>' $clusterOverview = Convert-CssClass -htmldata $clusterOverview $esxiOverview = $esxiOverview | ConvertTo-Html -Fragment -PreContent '<h4>ESXi Host Overview</h4>' $esxiOverview = Convert-CssClass -htmldata $esxiOverview $networkingOverview = $networkingOverview | ConvertTo-Html -Fragment -PreContent '<h4>Networking Overview</h4>' $networkingOverview = Convert-CssClass -htmldata $networkingOverview if ($ariaSuiteOverview) { $ariaSuiteOverview = $ariaSuiteOverview | ConvertTo-Html -Fragment -PreContent '<h4>VMware Aria Suite Overview</h4>' $ariaSuiteOverview = Convert-CssClass -htmldata $ariaSuiteOverview } else { $ariaSuiteOverview = $ariaSuiteOverview | ConvertTo-Html -Fragment -PreContent '<h4>VMware Aria Suite Overview</h4>' -PostContent '<p>No VMware Aria Suite products deployed in VMware Cloud Foundation mode.</p>' } $vvsOverview = $vvsOverview | ConvertTo-Html -Fragment -PreContent '<h4>VMware Validated Solutions Overview</h4>' $vvsOverview = Convert-CssClass -htmldata $vvsOverview $allOverviewObject += $vcfOverview $allOverviewObject += $hardwareOverview $allOverviewObject += $vcenterOverview $allOverviewObject += $clusterOverview $allOverviewObject += $networkingOverview $allOverviewObject += $ariaSuiteOverview $allOverviewObject += $vvsOverview $allOverviewObject += $esxiOverview $allOverviewObject } } } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Publish-VcfSystemOverview Function Request-VcfOverview { <# .SYNOPSIS Returns System Overview. .DESCRIPTION The Request-VcfOverview cmdlet returns an overview of the SDDC Manager instance. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Collects the overview detail .EXAMPLE Request-VcfOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the SDDC Manager instance. .EXAMPLE Request-VcfOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return an overview of the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { # Gather VCF Architecture if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $vcfArchitecture = (Get-AdvancedSetting -Name "config.SDDC.Deployed.Flavor" -Entity $vcfVcenterDetails.fqdn -Server $vcfVcenterDetails.fqdn).value Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } if (Get-VCFDepotCredential) { $depotUser = (Get-VCFDepotCredential).username.ToLower() $depotStatus = ((Get-VCFDepotCredential).message -Split ": ")[-1] } else { $depotUser = "Not Configured" $depotStatus = "Not Connected" } $systemObject = New-Object -TypeName psobject if ($PsBoundParameters.ContainsKey('anonymized')) { $systemObject | Add-Member -notepropertyname "SDDC Manager UUID" -notepropertyvalue (Get-VCFManager).id } else { $systemObject | Add-Member -notepropertyname "SDDC Manager FQDN" -notepropertyvalue (Get-VCFManager).fqdn } $systemObject | Add-Member -notepropertyname "Version" -notepropertyvalue (Get-VCFManager).version $systemObject | Add-Member -notepropertyname "Architecture" -notepropertyvalue $vcfArchitecture $systemObject | Add-Member -notepropertyname "CEIP Status" -notepropertyvalue (Get-Culture).TextInfo.ToTitleCase((Get-VCFCeip).status.ToLower()) $systemObject | Add-Member -notepropertyname "Customer Connect User" -notepropertyvalue $depotUser $systemObject | Add-Member -notepropertyname "Customer Connect Status" -notepropertyvalue $depotStatus $systemObject } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-VcfOverview Function Request-HardwareOverview { <# .SYNOPSIS Returns Hardware Overview. .DESCRIPTION The Request-VcfOverview cmdlet returns an overview of the hardware in an SDDC Manager instance. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Collects the hardware details .EXAMPLE Request-HardwareOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the SDDC Manager instance. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { # Calculate Total VMs Across VCF Instance $totalVms = $null $totalPoweredOnVms = $null $totalPoweredOffVms = $null $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain.name)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $totalVms += (Get-VM -Server $vcfVcenterDetails.fqdn).Count $totalPoweredOnVms += (Get-VM -Server $vcfVcenterDetails.fqdn | Where-Object {$_.PowerState -eq "PoweredOn"}).Count $totalPoweredOffVms += (Get-VM -Server $vcfVcenterDetails.fqdn | Where-Object {$_.PowerState -eq "PoweredOff"}).Count } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } # Gather Hardware OEM $harwdareOemObject = New-Object System.Collections.ArrayList foreach ($esxiHost in (Get-VCFHost)) { if (-not ($harwdareOemObject -match $esxiHost.hardwareVendor)) { $harwdareOemObject.Add($esxiHost.hardwareVendor) } } # Gather Hardware Platform $harwdareModelObject = New-Object System.Collections.ArrayList foreach ($esxiHost in (Get-VCFHost)) { if (-not ($harwdareModelObject -match $esxiHost.hardwareModel)) { $harwdareModelObject.Add($esxiHost.hardwareModel) } } # Gather CPU Sockets / Cores Platform $totalSockets = $null foreach ($esxiHost in (Get-VCFHost)) { $totalSockets = $totalSockets + $esxiHost.cpu.cpuCores.Count } $customObject = New-Object -TypeName psobject $customObject | Add-Member -Type NoteProperty -Name "Hardware OEM" -Value ($harwdareOemObject -join ':-: ') $customObject | Add-Member -Type NoteProperty -Name "Hardware Platform" -Value ($harwdareModelObject -join ':-: ') $customObject | Add-Member -notepropertyname "CPUs Sockets Deployed" -notepropertyvalue $totalSockets $customObject | Add-Member -notepropertyname "Hosts Deployed" -notepropertyvalue (Get-VCFHost).Count $customObject | Add-Member -notepropertyname "Workload Domains" -notepropertyvalue (Get-VCFWorkloadDomain | Measure-Object).Count $customObject | Add-Member -notepropertyname "Total VMs" -notepropertyvalue $totalVms $customObject | Add-Member -notepropertyname "Powered On" -notepropertyvalue $totalPoweredOnVms $customObject | Add-Member -notepropertyname "Powered Off" -notepropertyvalue $totalPoweredOffVms $customObject } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-HardwareOverview Function Request-VcenterOverview { <# .SYNOPSIS Returns overview of vSphere. .DESCRIPTION The Request-VcenterOverview cmdlet returns an overview of the vSphere environment managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication to the SDDC Manager instance - Validates that network connectivity and authentication to the vCenter Server instances - Collects the vSphere overview detail .EXAMPLE Request-VcenterOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the vSphere environment managed by the SDDC Manager instance. .EXAMPLE Request-VcenterOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -aanonymized This example will return an overview of the vSphere environment managed by the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server -ErrorAction SilentlyContinue -ErrorVariable ErrorMessage) { if (Test-VCFAuthentication -server $server -user $user -pass $pass -ErrorAction SilentlyContinue -ErrorVariable ErrorMessage) { $allWorkloadDomains = Get-VCFWorkloadDomain $allVsphereObject = New-Object System.Collections.ArrayList if (($vcfMgmtVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $vcfMgmtVcenterDetails.fqdn) { if (Test-VsphereAuthentication -server $vcfMgmtVcenterDetails.fqdn -user $vcfMgmtVcenterDetails.ssoAdmin -pass $vcfMgmtVcenterDetails.ssoAdminPass) { foreach ($domain in $allWorkloadDomains) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain.name)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $customObject = New-Object -TypeName psobject $vm = Get-VM -Server $vcfMgmtVcenterDetails.fqdn -ErrorAction SilentlyContinue | Where-Object {$_.Name -eq $vcfVcenterDetails.vmName} | Select-Object Name, NumCpu, MemoryGB $vcsaSizing = $vcsaSize | ConvertFrom-Json if ($PsBoundParameters.ContainsKey('anonymized')) { $customObject | Add-Member -notepropertyname "vCenter Server UUID" -notepropertyvalue $domain.vcenters.id $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue (Get-VCFvCenter -id $domain.vcenters.id).version $customObject | Add-Member -notepropertyname "Domain UUID" -notepropertyvalue $domain.id } else { $customObject | Add-Member -notepropertyname "vCenter Server FQDN" -notepropertyvalue $domain.vcenters.fqdn $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue (Get-VCFvCenter -id $domain.vcenters.id).version $customObject | Add-Member -notepropertyname "Domain Name" -notepropertyvalue $domain.name } $customObject | Add-Member -notepropertyname "Domain Type" -notepropertyvalue $domain.type.ToLower() if ($size = $vcsaSizing | Where-Object {$_.resources.cpu -eq $vm.NumCpu -and $_.resources.memory -eq $vm.MemoryGB}) { $customObject | Add-Member -notepropertyname "Size" -notepropertyvalue $size.name } else { $customObject | Add-Member -notepropertyname "Size" -notepropertyvalue "custom" } $customObject | Add-Member -notepropertyname "CPU" -notepropertyvalue $vm.NumCpu $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue $vm.MemoryGB $customObject | Add-Member -notepropertyname "Total Clusters" -notepropertyvalue (Get-VCFWorkloadDomain -id $domain.id).clusters.Count $customObject | Add-Member -notepropertyname "Total Hosts" -notepropertyvalue (Get-VCFHost | Where-Object {$_.domain.id -eq $domain.id}).Count $customObject | Add-Member -notepropertyname "Total VMs" -notepropertyvalue (Get-VM -Server $vcfVcenterDetails.fqdn).Count $customObject | Add-Member -notepropertyname "Powered On VMs" -notepropertyvalue (Get-VM -Server $vcfVcenterDetails.fqdn | Where-Object {$_.PowerState -eq "PoweredOn"}).Count $customObject | Add-Member -notepropertyname "Powered Off VMs" -notepropertyvalue (Get-VM -Server $vcfVcenterDetails.fqdn | Where-Object {$_.PowerState -eq "PoweredOff"}).Count } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } $allVsphereObject += $customObject } } Disconnect-VIServer -Server $vcfMgmtVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } $allVsphereObject | Sort-Object 'Domain Type', 'Domain Name' } else { Write-LogMessage -Type ERROR -Message "$ErrorMessage" } } else { Write-LogMessage -Type ERROR -Message "$ErrorMessage" } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-VcenterOverview Function Request-EsxiOverview { <# .SYNOPSIS Returns overview of ESXi hosts. .DESCRIPTION The Request-EsxiOverview cmdlet returns an overview of the ESXi host managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication to the SDDC Manager instance - Validates that network connectivity and authentication to the vCenter Server instances - Collects the ESXi host overview detail .EXAMPLE Request-EsxiOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the ESXi hosts managed by the SDDC Manager instance. .EXAMPLE Request-EsxiOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -subscription This example will return an overview of the ESXi hosts managed by the SDDC Manager instance with the number of cores for VCF+ subscription. .EXAMPLE Request-EsxiOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -subscription -outputCsv F:\Reporting This example will return an overview of the ESXi hosts managed by the SDDC Manager instance with the number of cores for VCF+ subscription and save as a CSV file to F:\Reporting. .EXAMPLE Request-EsxiOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return an overview of the ESXi hosts managed by the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. .PARAMETER subscription Switch to enable subscription output for the report. .PARAMETER outputCsv The path to save the output as a CSV file. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$subscription, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$outputCsv ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allEsxiHostObject = New-Object System.Collections.ArrayList foreach ($domain in $allWorkloadDomains) { foreach ($cluster in $domain.clusters) { $allAssignedEsxiHosts = (Get-VCFHost | Where-Object {$_.domain.id -eq $domain.id}) foreach ($assignedEsxiHost in $allAssignedEsxiHosts) { $customObject = New-Object -TypeName psobject $sockets = $assignedEsxiHost.cpu.cpuCores.Count $coresPerSocket = $assignedEsxiHost.cpu.Cores / $sockets if ($coresPerSocket -le 16) { $vcfPlusLicenseCount = $sockets * 16 } else { $vcfPlusLicenseCount = $sockets * $coresPerSocket } if ($PsBoundParameters.ContainsKey('anonymized')) { $customObject | Add-Member -notepropertyname "Domain UUID" -notepropertyvalue $domain.id $customObject | Add-Member -notepropertyname "Cluster UUID" -notepropertyvalue $cluster.id $customObject | Add-Member -notepropertyname "ESXi Host UUID" -notepropertyvalue $assignedEsxiHost.id $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue $assignedEsxiHost.esxiVersion $customObject | Add-Member -notepropertyname "Hardware OEM" -notepropertyvalue $assignedEsxiHost.hardwareVendor $customObject | Add-Member -notepropertyname "Hardware Platform" -notepropertyvalue $assignedEsxiHost.hardwareModel $customObject | Add-Member -notepropertyname "CPU Sockets" -notepropertyvalue $assignedEsxiHost.cpu.cpuCores.Count $customObject | Add-Member -notepropertyname "CPU Cores" -notepropertyvalue $assignedEsxiHost.cpu.Cores $customObject | Add-Member -notepropertyname "CPU Cores per Socket" -notepropertyvalue ($assignedEsxiHost.cpu.Cores / $assignedEsxiHost.cpu.cpuCores.Count) $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue ([Math]::round(($assignedEsxiHost.memory.totalCapacityMB) / 1024)) if ($PsBoundParameters.ContainsKey('subscription')) { $customObject | Add-Member -notepropertyname "VCF+ Subscription Core Count" -notepropertyvalue $vcfPlusLicenseCount } $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $assignedEsxiHost.status } else { $customObject | Add-Member -notepropertyname "Domain Name" -notepropertyvalue $domain.name $customObject | Add-Member -notepropertyname "Cluster Name" -notepropertyvalue (Get-VCFCluster -id $cluster.id).name $customObject | Add-Member -notepropertyname "ESXi Host FQDN" -notepropertyvalue (Get-VCFHost -id $assignedEsxiHost.id).fqdn $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue $assignedEsxiHost.esxiVersion $customObject | Add-Member -notepropertyname "Hardware OEM" -notepropertyvalue $assignedEsxiHost.hardwareVendor $customObject | Add-Member -notepropertyname "Hardware Platform" -notepropertyvalue $assignedEsxiHost.hardwareModel $customObject | Add-Member -notepropertyname "CPU Sockets" -notepropertyvalue $assignedEsxiHost.cpu.cpuCores.Count $customObject | Add-Member -notepropertyname "CPU Cores" -notepropertyvalue $assignedEsxiHost.cpu.Cores $customObject | Add-Member -notepropertyname "CPU Cores per Socket" -notepropertyvalue ($assignedEsxiHost.cpu.Cores / $assignedEsxiHost.cpu.cpuCores.Count) $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue ([Math]::round(($assignedEsxiHost.memory.totalCapacityMB) / 1024)) if ($PsBoundParameters.ContainsKey('subscription')) { $customObject | Add-Member -notepropertyname "VCF+ Subscription Core Count" -notepropertyvalue $vcfPlusLicenseCount } $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $assignedEsxiHost.status } $allEsxiHostObject += $customObject } } } $allUnassignedEsxiHosts = (Get-VCFHost | Where-Object {$_.status -eq "UNASSIGNED_USEABLE"}) foreach ($unassignedEsxiHost in $allUnassignedEsxiHosts) { $customObject = New-Object -TypeName psobject $sockets = $unassignedEsxiHost.cpu.cpuCores.Count $coresPerSocket = $unassignedEsxiHost.cpu.Cores / $sockets if ($coresPerSocket -le 16) { $vcfPlusLicenseCount = $sockets * 16 } else { $vcfPlusLicenseCount = $sockets * $coresPerSocket } if ($PsBoundParameters.ContainsKey('anonymized')) { $customObject | Add-Member -NotePropertyName 'Domain UUID' -NotePropertyValue "" $customObject | Add-Member -notepropertyname "Cluster UUID" -notepropertyvalue "" $customObject | Add-Member -notepropertyname "ESXi Host UUID" -notepropertyvalue $unassignedEsxiHost.id $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue $unassignedEsxiHost.esxiVersion $customObject | Add-Member -notepropertyname "Hardware OEM" -notepropertyvalue $assignedEsxiHost.hardwareVendor $customObject | Add-Member -notepropertyname "Hardware Platform" -notepropertyvalue $assignedEsxiHost.hardwareModel $customObject | Add-Member -notepropertyname "CPU Sockets" -notepropertyvalue $assignedEsxiHost.cpu.cpuCores.Count $customObject | Add-Member -notepropertyname "CPU Cores" -notepropertyvalue $assignedEsxiHost.cpu.Cores $customObject | Add-Member -notepropertyname "CPU Cores per Socket" -notepropertyvalue ($assignedEsxiHost.cpu.Cores / $assignedEsxiHost.cpu.cpuCores.Count) $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue ([Math]::round(($unassignedEsxiHost.memory.totalCapacityMB) / 1024)) if ($PsBoundParameters.ContainsKey('subscription')) { $customObject | Add-Member -notepropertyname "VCF+ Subscription Core Count" -notepropertyvalue $vcfPlusLicenseCount } $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $unassignedEsxiHost.status } else { $customObject | Add-Member -notepropertyname "Domain Name" -notepropertyvalue "" $customObject | Add-Member -notepropertyname "Cluster Name" -notepropertyvalue "" $customObject | Add-Member -notepropertyname "ESXi Host FQDN" -notepropertyvalue (Get-VCFHost -id $unassignedEsxiHost.id).fqdn $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue $unassignedEsxiHost.esxiVersion $customObject | Add-Member -notepropertyname "Hardware OEM" -notepropertyvalue $assignedEsxiHost.hardwareVendor $customObject | Add-Member -notepropertyname "Hardware Platform" -notepropertyvalue $assignedEsxiHost.hardwareModel $customObject | Add-Member -notepropertyname "CPU Sockets" -notepropertyvalue $assignedEsxiHost.cpu.cpuCores.Count $customObject | Add-Member -notepropertyname "CPU Cores" -notepropertyvalue $assignedEsxiHost.cpu.Cores $customObject | Add-Member -notepropertyname "CPU Cores per Socket" -notepropertyvalue ($assignedEsxiHost.cpu.Cores / $assignedEsxiHost.cpu.cpuCores.Count) $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue ([Math]::round(($unassignedEsxiHost.memory.totalCapacityMB) / 1024)) if ($PsBoundParameters.ContainsKey('subscription')) { $customObject | Add-Member -notepropertyname "VCF+ Subscription Core Count" -notepropertyvalue $vcfPlusLicenseCount } $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $unassignedEsxiHost.status } $allEsxiHostObject += $customObject } if ($PsBoundParameters.ContainsKey('outputCsv')) { $csv = Start-CreateOutputCsvDirectory -csvFolder $outputCsv -csvFileSuffix $server $allEsxiHostObject | ConvertTo-Csv -NoTypeInformation | Out-File $csv Write-Output "CSV file saved to $csv" } else { $allEsxiHostObject | Sort-Object -Property Status, 'Domain Name', 'Domain UUID', 'Cluster Name', 'Cluster UUID', 'ESXi Host FQDN', 'ESXi Host UUID' } } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-EsxiOverview Function Request-ClusterOverview { <# .SYNOPSIS Returns overview of vSphere. .DESCRIPTION The Request-ClusterOverview cmdlet returns an overview of the vSphere environment managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication to the SDDC Manager instance - Validates that network connectivity and authentication to the vCenter Server instances - Collects the vSphere overview detail .EXAMPLE Request-ClusterOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the vSphere environment managed by the SDDC Manager instance. .EXAMPLE Request-ClusterOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return an overview of the vSphere environment managed by the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allClusterObject = New-Object System.Collections.ArrayList foreach ($domain in $allWorkloadDomains) { foreach ($cluster in $domain.clusters) { $customObject = New-Object -TypeName psobject if ($PsBoundParameters.ContainsKey('anonymized')) { $customObject | Add-Member -notepropertyname "Domain UUID" -notepropertyvalue $domain.id $customObject | Add-Member -notepropertyname "Cluster UUID" -notepropertyvalue $cluster.id } else { $customObject | Add-Member -notepropertyname "Domain Name" -notepropertyvalue $domain.name $customObject | Add-Member -notepropertyname "Cluster Name" -notepropertyvalue (Get-VCFCluster -id $cluster.id).name } $customObject | Add-Member -notepropertyname "Principal Storage" -notepropertyvalue (Get-VCFCluster -id $cluster.id).primaryDatastoreType $customObject | Add-Member -notepropertyname "Stretched Cluster" -notepropertyvalue (Get-VCFCluster -id $cluster.id).isStretched $allClusterObject += $customObject } } $allClusterObject | Sort-Object 'Domain Name','Cluster Name','Domain UUID','Cluster UUID' } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-ClusterOverview Function Request-NetworkOverview { <# .SYNOPSIS Returns overview of networking. .DESCRIPTION The Request-NetworkOverview cmdlet returns an overview of the networking managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication to the SDDC Manager instance - Collects the networking overview detail .EXAMPLE Request-NetworkOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of the networking managed by the SDDC Manager instance. .EXAMPLE Request-NetworkOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return an overview of the networking managed by the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allWorkloadDomains = Get-VCFWorkloadDomain $allNetworkingObject = New-Object System.Collections.ArrayList if (($vcfMgmtVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $vcfMgmtVcenterDetails.fqdn) { if (Test-VsphereAuthentication -server $vcfMgmtVcenterDetails.fqdn -user $vcfMgmtVcenterDetails.ssoAdmin -pass $vcfMgmtVcenterDetails.ssoAdminPass) { foreach ($domain in $allWorkloadDomains) { foreach ($cluster in $domain.clusters) { if (Get-VCFEdgeCluster | Where-Object {$_.nsxtCluster.id -eq $domain.nsxtCluster.id}) { $edgeCluster = "True" } else { $edgeCluster = "False" } if ($domain.type -eq "MANAGEMENT" -and (Get-VCFApplicationVirtualNetwork)) { $avnStatus = "True"} else { $avnStatus = "False" } $customObject = New-Object -TypeName psobject $nsxManagerNode = ($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domain $domain.name -listNodes).nodes | Select-Object -First 1 $vm = Get-VM -Server $vcfMgmtVcenterDetails.fqdn -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $nsxManagerNode.name } | Select-Object Name, NumCpu, MemoryGB $nsxLocalManagerSizing = $nsxLocalManagerSize | ConvertFrom-Json if ($PsBoundParameters.ContainsKey('anonymized')) { $customObject | Add-Member -notepropertyname "NSX Manager Cluster UUID" -notepropertyvalue $domain.nsxtCluster.id $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue (Get-VCFNsxtCluster -id $domain.nsxtCluster.id).version $customObject | Add-Member -notepropertyname "Domain UUID" -notepropertyvalue $domain.id } else { $customObject | Add-Member -notepropertyname "NSX Manager Cluster FQDN" -notepropertyvalue $domain.nsxtCluster.vipFqdn $customObject | Add-Member -notepropertyname "Version" -notepropertyvalue (Get-VCFNsxtCluster -id $domain.nsxtCluster.id).version $customObject | Add-Member -notepropertyname "Domain Name" -notepropertyvalue $domain.name } $customObject | Add-Member -notepropertyname "Domain Type" -notepropertyvalue $domain.type.ToLower() $customObject | Add-Member -notepropertyname "Shared" -notepropertyvalue (Get-VCFNsxtCluster -id $domain.nsxtCluster.id).isShared if ($size = $nsxLocalManagerSizing | Where-Object {$_.resources.cpu -eq $vm.NumCpu -and $_.resources.memory -eq $vm.MemoryGB}) { $customObject | Add-Member -notepropertyname "Size" -notepropertyvalue $size.name } else { $customObject | Add-Member -notepropertyname "Size" -notepropertyvalue "custom" } $customObject | Add-Member -notepropertyname "CPU" -notepropertyvalue $vm.NumCpu $customObject | Add-Member -notepropertyname "Memory (GB)" -notepropertyvalue $vm.MemoryGB $customObject | Add-Member -notepropertyname "Edge Cluster" -notepropertyvalue $edgeCluster $customObject | Add-Member -notepropertyname "AVN" -notepropertyvalue $avnStatus } $allNetworkingObject += $customObject } } Disconnect-VIServer -Server $vcfMgmtVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } $allNetworkingObject | Sort-Object 'Domain Type' } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-NetworkOverview Function Request-VMwareAriaSuiteOverview { <# .SYNOPSIS Returns an overview of VMware Aria Suite products managed by SDDC Manager. .DESCRIPTION The Request-VMwareAriaSuiteOverview cmdlet returns an overview of VMware Aria Suite products managed by SDDC Manager. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity and authentication to the SDDC Manager instance - Collects the VMware Aria Suite product overview detail .EXAMPLE Request-VMwareAriaSuiteOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of VMware Aria Suite products managed by the SDDC Manager instance. .EXAMPLE Request-VMwareAriaSuiteOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -anonymized This example will return an overview of VMware Aria Suite products managed by the SDDC Manager instance, but will anonymize the output. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER anonymized Switch to enable anonymized output for the report. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$anonymized ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allAriaSuiteObject = New-Object System.Collections.ArrayList $vcfApiCmdlet = @("Get-VCFvRSLCM","Get-VCFWSA","Get-VCFvRLI","Get-VCFvROPS","Get-VCFvRA") foreach ($apiCmdlet in $vcfApiCmdlet) { if ((Invoke-Expression $apiCmdlet).status -eq "ACTIVE") { if ($apiCmdlet -eq "Get-VCFvRSLCM") { $product = "VMware Aria Suite Lifecycle" $nodeCount = "1" } elseif ($apiCmdlet -eq "Get-VCFWSA") { $product = "VMware Workspace ONE Access" $nodeCount = (Get-VCFWSA).nodes.Count } elseif ($apiCmdlet -eq "Get-VCFvRLI") { $product = "VMware Aria Operations for Logs" $nodeCount = (Get-VCFvRLI).nodes.Count } elseif ($apiCmdlet -eq "Get-VCFvROPS") { $product = "VMware Aria Operations" $nodeCount = (Get-VCFvROPS).nodes.Count } elseif ($apiCmdlet -eq "Get-VCFvRA") { $product = "VMware Aria Automation" $nodeCount = (Get-VCFvRA).nodes.Count } $customObject = New-Object -TypeName psobject $customObject | Add-Member -NotePropertyName "VMware Aria Suite Product" -NotePropertyValue $product if ($PsBoundParameters.ContainsKey("anonymized")) { $customObject | Add-Member -NotePropertyName "UUID" -NotePropertyValue (Invoke-Expression $apiCmdlet).id } else { if ($apiCmdlet -eq "Get-VCFvRSLCM") { $customObject | Add-Member -NotePropertyName "FQDN" -NotePropertyValue (Invoke-Expression $apiCmdlet).fqdn } else { $customObject | Add-Member -NotePropertyName "FQDN" -NotePropertyValue (Invoke-Expression $apiCmdlet).loadBalancerFqdn } } $customObject | Add-Member -NotePropertyName "Version" -NotePropertyValue (Invoke-Expression $apiCmdlet).version $customObject | Add-Member -NotePropertyName "Status" -NotePropertyValue (Invoke-Expression $apiCmdlet).status $customObject | Add-Member -NotePropertyName "Nodes" -NotePropertyValue $nodeCount $allAriaSuiteObject += $customObject } } $allAriaSuiteObject } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-VMwareAriaSuiteOverview Function Request-ValidatedSolutionOverview { <# .SYNOPSIS Returns VMware Validated Solution Overview. .DESCRIPTION The Request-ValidatedSolutionOverview cmdlet returns an overview of VMware Validated Solutions deployed. The cmdlet connects to the SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Collects the VMware Validated Solution details .EXAMPLE Request-ValidatedSolutionOverview -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! This example will return an overview of VMware Validated Solutions. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass ) $pass = Get-Password -User $user -Password $pass Try { if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { $allVvsObject = New-Object System.Collections.ArrayList # Validate IAM Deployment if (($vcfNsxDetails = Get-NsxtServerDetail -fqdn $server -username $user -password $pass -domainType MANAGEMENT)) { if (Test-NSXTConnection -server $vcfNsxDetails.fqdn) { if (Test-NSXTAuthentication -server $vcfNsxDetails.fqdn -user $vcfNsxDetails.adminUser -pass $vcfNsxDetails.adminPass) { if ((Get-NsxtVidm).vidm_enable -eq "True") { $iamEnabled = "Enabled" } else { $iamEnabled = "Not Enabled" } } } } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Identity and Access Management" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $iamEnabled $allVvsObject += $customObject # Validate DRI Deployment $allWorkloadDomains = Get-VCFWorkloadDomain foreach ($domain in $allWorkloadDomains) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domain $domain.name)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { foreach ($cluster in Get-Cluster -Server $vcfVcenterDetails.fqdn ) { if (Get-WMCluster -Server $vcfVcenterDetails.fqdn) { $driEnabled = "Enabled" } else { $driEnabled = "Not Enabled" } } } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Developer Ready Infrastructure" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $driEnabled $allVvsObject += $customObject # Validate ILA Deployment if ((Get-VCFvRLI).status -eq "ACTIVE") { if (($vcfVrliDetails = Get-vRLIServerDetail -fqdn $server -username $user -password $pass)) { if (Test-vRLIConnection -server $vcfVrliDetails.fqdn) { if (Test-vRLIAuthentication -server $vcfVrliDetails.fqdn -user $vcfVrliDetails.adminUser -pass $vcfVrliDetails.adminPass) { if (Get-vRLISmtpConfiguration) { $ilaEnabled = "Enabled" } else { $ilaEnabled = "Not Enabled" } } } } } else { $ilaEnabled = "Not Enabled" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Intelligent Logging and Analytics" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $ilaEnabled $allVvsObject += $customObject # Validate IOM Deployment if ((Get-VCFvROPS).status -eq "ACTIVE") { if (($vcfVropsDetails = Get-vROPsServerDetail -fqdn $server -username $user -password $pass)) { if (Test-vROPSConnection -server $vcfVropsDetails.loadBalancerFqdn) { if (Test-vROPSAuthentication -server $vcfVropsDetails.loadBalancerFqdn -user $vcfVropsDetails.adminUser -pass $vcfVropsDetails.adminPass) { if ((Get-vROPSCollectorGroup).Count -gt 1 ) { $iomEnabled = "Enabled" } else { $iomEnabled = "Not Enabled" } } } } } else { $iomEnabled = "Not Enabled" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Intelligent Operations Management" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $iomEnabled $allVvsObject += $customObject # Validate PCA Deployment if ((Get-VCFvRA).status -eq "ACTIVE") { $vraEnabled = "Enabled" } else { $vraEnabled = "Not Enabled" } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Private Cloud Automation" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $vraEnabled $allVvsObject += $customObject # Validate PDR Deployment if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { if (((Get-View -Id 'ExtensionManager-ExtensionManager').ExtensionList | Where-Object {$_.Key -eq "com.vmware.vcDr"}) -and ((Get-View -Id 'ExtensionManager-ExtensionManager').ExtensionList | Where-Object {$_.Key -eq "com.vmware.vcHms"})) { $pdrEnabled = "Enabled" } else { $pdrEnabled = "Not Enabled" } } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } $customObject = New-Object -TypeName psobject $customObject | Add-Member -notepropertyname "Name" -notepropertyvalue "Site Protection and Disaster Recovery" $customObject | Add-Member -notepropertyname "Status" -notepropertyvalue $pdrEnabled $allVvsObject += $customObject $allVvsObject | Sort-Object Name } } } Catch { Debug-ExceptionWriter -object $_ } } Export-ModuleMember -Function Request-ValidatedSolutionOverview ########################################## E N D O F F U N C T I O N S ########################################## ####################################################################################################################### ######################################################################################### ############################# Start Supporting Functions ############################## Function Test-VcfReportingPrereq { <# .SYNOPSIS Verifies that the minimum dependencies are met to run the PowerShell module. .DESCRIPTION The Test-VcfReportingPrereq cmdlet verifies that the minimum dependencies are met to run the the PowerShell module. .EXAMPLE Test-VcfReportingPrereq -sddcManagerFqdn sfo-vcf01.sfo.rainpole.io -sddcManagerUser admin@local -sddcManagerPass VMw@re1!VMw@re1! This example runs the prerequisite validation. .PARAMETER sddcManagerFqdn The fully qualified domain name of the SDDC Manager. .PARAMETER sddcManagerUser The username to authenticate to the SDDC Manager. .PARAMETER sddcManagerPass The password to authenticate to the SDDC Manager. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerFqdn, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sddcManagerUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$sddcManagerPass ) $sddcManagerPass = Get-Password -user $sddcManagerUser -password $sddcManagerPass Try { Clear-Host; Write-Host "" $vcfMinVersion = "4.4.0" if (Test-VCFConnection -server $sddcManagerFqdn) { if (Test-VCFAuthentication -server $sddcManagerFqdn -user $sddcManagerUser -pass $sddcManagerPass) { $vcfVersion = ((Get-VCFManager).version).Split('-')[0] if ($vcfVersion -lt $vcfMinVersion) { $message = "VMware Cloud Foundation: SDDC Manager $vcfVersion ($($sddcManagerFqdn)) is not supported by this module. Minimum required version is $($vcfMinVersion)." Show-ReportingOutput -type ERROR -message $message Break } else { $message = "VMware Cloud Foundation: SDDC Manager $vcfVersion ($($sddcManagerFqdn)) and supports the minimum required version." Show-ReportingOutput -Type INFO -Message $message } $moduleName = $myInvocation.myCommand.ModuleName $moduleData = (Get-Module -Name $moduleName) if ($PSEdition -eq 'Core' -and ($PSVersionTable.OS).Split(' ')[0] -eq 'Linux') { $moduleManifestPath = $moduleData.ModuleBase + '/' + $moduleData.Name + '.psd1' } else { $moduleManifestPath = $moduleData.ModuleBase + '\' + $moduleData.Name + '.psd1' } $moduleManifest = Import-PowerShellDataFile -Path $moduleManifestPath $requiredModules = $moduleManifest.RequiredModules foreach ($module in $requiredModules) { $moduleName = $module.ModuleName $requiredVersion = $module.ModuleVersion $installedModule = Get-Module -ListAvailable -Name $moduleName if ($installedModule) { $installedVersion = $installedModule.Version if ($installedVersion -lt $requiredVersion) { $message = "$($moduleName) $($installedVersion) is installed. Install $($moduleName) $($requiredVersion) or higher." Show-ReportingOutput -type ERROR -message $message } elseif ($installedVersion -ge $requiredVersion) { $message = "$($moduleName) $($installedVersion) is installed version and meets the minimum required version of $($moduleName) $($requiredVersion)." Show-ReportingOutput -type INFO -message $message } } else { $message = "$($moduleName) is not installed. Install $($moduleName) $($requiredVersion) or higher." Show-ReportingOutput -type ERROR -message $message } } } } } Catch { Write-Error $_.Exception.Message } } Export-ModuleMember -Function Test-VcfReportingPrereq Function Show-ReportingOutput { Param ( [Parameter (Mandatory = $true)] [AllowEmptyString()] [String]$message, [Parameter (Mandatory = $false)] [ValidateSet("INFO", "ERROR", "WARNING", "EXCEPTION","ADVISORY","NOTE","QUESTION","WAIT")] [String]$type = "INFO", [Parameter (Mandatory = $false)] [Switch]$skipnewline ) If ($type -eq "INFO") { $messageColour = "92m" #Green } elseIf ($type -in "ERROR","EXCEPTION") { $messageColour = "91m" # Red } elseIf ($type -in "WARNING","ADVISORY","QUESTION") { $messageColour = "93m" #Yellow } elseIf ($type -in "NOTE","WAIT") { $messageColour = "97m" # White } $ESC = [char]0x1b $timestampColour = "97m" $timeStamp = Get-Date -Format "MM-dd-yyyy_HH:mm:ss" If ($skipnewline) { Write-Host -NoNewline "$ESC[${timestampcolour} [$timestamp]$ESC[${threadColour} $ESC[${messageColour} [$type] $message$ESC[0m" } else { Write-Host "$ESC[${timestampcolour} [$timestamp]$ESC[${threadColour} $ESC[${messageColour} [$type] $message$ESC[0m" } } Export-ModuleMember -Function Show-ReportingOutput Function Start-CreateReportDirectory { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$path, [Parameter (Mandatory = $true)] [ValidateSet("health","alert","config","upgrade","overview")] [String]$reportType ) $filetimeStamp = Get-Date -Format "MM-dd-yyyy_hh_mm_ss" if ($reportType -eq "health") { $Global:reportFolder = Join-Path -Path $path -ChildPath 'HealthReports' } if ($reportType -eq "alert") { $Global:reportFolder = Join-Path -Path $path -ChildPath 'AlertReports' } if ($reportType -eq "config") { $Global:reportFolder = Join-Path -Path $path -ChildPath 'ConfigReports' } if ($reportType -eq "upgrade") { $Global:reportFolder = Join-Path -Path $path -ChildPath 'UpgradeReports' } if ($reportType -eq "overview") { $Global:reportFolder = Join-Path -Path $path -ChildPath 'OverviewReports' } if (!(Test-Path -Path $reportFolder)) { New-Item -Path $reportFolder -ItemType "directory" | Out-Null } $filename = $filetimeStamp + "-" + $reportType + ".htm" $reportName = Join-Path -Path $reportFolder -ChildPath $fileName $reportName } Export-ModuleMember -Function Start-CreateReportDirectory Function Start-CreateOutputJsonDirectory { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$jsonFolder, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$jsonFileSuffix ) $filetimeStamp = Get-Date -Format "MM-dd-yyyy_hh_mm_ss" $Global:jsonFolder = $jsonFolder $jsonName = $filetimeStamp + "-" + $jsonFileSuffix $jsonDestination = Join-Path -Path $jsonFolder -ChildPath $jsonName if (!(Test-Path -Path $jsonFolder)) { New-Item -Path $jsonFolder -ItemType "directory" | Out-Null } $jsonDestination } Function Start-CreateOutputCsvDirectory { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$csvFolder, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$csvFileSuffix ) $filetimeStamp = Get-Date -Format "MM-dd-yyyy_hh_mm_ss" $Global:csvFolder = $csvFolder $csvName = $filetimeStamp + "-" + $csvFileSuffix + ".csv" $csvDestination = Join-Path -Path $csvFolder -ChildPath $csvName if (!(Test-Path -Path $csvFolder)) { New-Item -Path $csvFolder -ItemType "directory" | Out-Null } $csvDestination } Function Invoke-SddcCommand { <# .SYNOPSIS Run a command on SDDC Manager. .DESCRIPTION The Invoke-SddcCommand cmdlet runs a command within the SDDC Manager appliance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the Management Domain vCenter Server instance - Runs the command provided within the SDDC Manager appliance .EXAMPLE Invoke-SddcCommand -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -vmUser root -vmPass VMw@re1! -command "chage -l backup" This example runs the command provided on the SDDC Manager appliance as the root user. .EXAMPLE Invoke-SddcCommand -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -vmUser vcf -vmPass VMw@re1! -command "echo Hello World." This example runs the command provided on the SDDC Manager appliance as the vcf user. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER vmUser The username to authenticate to the virtual machine. .PARAMETER vmPass The password to authenticate to the virtual machine. .PARAMETER command The command to run on the virtual machine. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$vmUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$vmPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$command ) $pass = Get-Password -User $user -Password $pass $vmPass = Get-Password -User $vmUser -Password $vmPass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $output = Invoke-VMScript -VM ($server.Split(".")[0]) -ScriptText $command -GuestUser $vmUser -GuestPassword $vmPass -Server $vcfVcenterDetails.fqdn $output } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Export-ModuleMember -Function Invoke-SddcCommand Function Copy-FiletoSddc { <# .SYNOPSIS Copy a file to SDDC Manager. .DESCRIPTION The Copy-FiletoSddc cmdlet copies files to the SDDC Manager appliance. The cmdlet connects to SDDC Manager using the -server, -user, and -pass values: - Validates that network connectivity is available to the SDDC Manager instance - Validates that network connectivity is available to the Management Domain vCenter Server instance - Copies the files to the SDDC Manager appliance .EXAMPLE Copy-FiletoSddc -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -vmUser vcf -vmPass VMw@re1! -source "C:\Temp\foo.txt" -destination "/home/vcf/foo.txt" This example copies a file to the SDDC Manager appliance. .EXAMPLE Copy-FiletoSddc -server sfo-vcf01.sfo.rainpole.io -user admin@local -pass VMw@re1!VMw@re1! -vmuser vcf -vmPass VMw@re1! -source "C:\Temp\bar" -destination "/home/vcf/" This example copies a file to the SDDC Manager appliance. .PARAMETER server The fully qualified domain name of the SDDC Manager. .PARAMETER user The username to authenticate to the SDDC Manager. .PARAMETER pass The password to authenticate to the SDDC Manager. .PARAMETER vmUser The username to authenticate to the virtual machine. .PARAMETER vmPass The password to authenticate to the virtual machine. .PARAMETER source The source file or folder to copy to the SDDC Manager appliance. .PARAMETER destination The destination file or folder to copy to on the SDDC Manager appliance. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$server, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$user, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$pass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$vmUser, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$vmPass, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$source, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$destination ) $pass = Get-Password -User $user -Password $pass $vmPass = Get-Password -User $vmUser -Password $vmPass if (Test-VCFConnection -server $server) { if (Test-VCFAuthentication -server $server -user $user -pass $pass) { if (($vcfVcenterDetails = Get-vCenterServerDetail -server $server -user $user -pass $pass -domainType MANAGEMENT)) { if (Test-VsphereConnection -server $($vcfVcenterDetails.fqdn)) { if (Test-VsphereAuthentication -server $vcfVcenterDetails.fqdn -user $vcfVcenterDetails.ssoAdmin -pass $vcfVcenterDetails.ssoAdminPass) { $output = Copy-VMGuestFile -VM ($server.Split('.')[0]) -LocalToGuest -GuestUser $vmUser -GuestPassword $vmPass -Source $source -Destination $destination -Server $vcfVcenterDetails.fqdn $output } Disconnect-VIServer -Server $vcfVcenterDetails.fqdn -Confirm:$false -WarningAction SilentlyContinue | Out-Null } } } } } Export-ModuleMember -Function Copy-FiletoSddc Function Read-JsonElement { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [PSCustomObject]$inputData, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly ) $outputData = New-Object System.Collections.ArrayList foreach ($element in $inputData.PsObject.Properties.Value) { $elementObject = New-Object -TypeName psobject $elementObject | Add-Member -notepropertyname 'Component' -notepropertyvalue ($element.area -Split (":"))[0].Trim() $elementObject | Add-Member -notepropertyname 'Resource' -notepropertyvalue ($element.area -Split (":"))[-1].Trim() $elementObject | Add-Member -notepropertyname 'Alert' -notepropertyvalue $element.alert $elementObject | Add-Member -notepropertyname 'Message' -notepropertyvalue $element.message if ($PsBoundParameters.ContainsKey("failureOnly")) { if (($element.status -eq "FAILED")) { $outputData += $elementObject } } else { $outputData += $elementObject } } $outputData } Export-ModuleMember -Function Read-JsonElement Function Convert-TextToHtml { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$sourceFile, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$label ) Get-Content $sourceFile | ConvertTo-HTML -Property @{Label=$label;Expression={$_}} -Fragment } Export-ModuleMember -Function Convert-TextToHtml Function Get-ClarityReportHeader { Param ( [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$dark ) # Define the default Clarity Cascading Style Sheets (CSS) for the HTML report header if ($PsBoundParameters.ContainsKey("dark")) { $clarityCssHeader = ' <head> <style> <!--- Used Clarify CSS components for this project ---> article, aside, details, figcaption, figure, footer, header, main, menu, nav, section, summary { display: block; } .main-container { display: flex; flex-direction: column; height: 100vh; background: var(--clr-global-app-background, #21333b); } header.header-6, .header.header-6 { background-color: #0e161b; } header, .header { display: flex; color: #fafafa; background-color: #0e161b; height: 3rem; white-space: nowrap; } .nav { display: flex; height: 1.8rem; list-style-type: none; align-items: center; margin: 0; width: 100%; white-space: nowrap; box-shadow: 0 -0.05rem 0 #495865 inset; } .nav .nav-item { display: inline-block; margin-right: 1.2rem; } .nav .nav-item.active > .nav-link { color: white; box-shadow: 0 -0.05rem 0 #495865 inset; } .nav .nav-link { color: #acbac3; font-size: 0.7rem; font-weight: 400; letter-spacing: normal; line-height: 1.8rem; display: inline-block; padding: 0 0.15rem; box-shadow: none; } .nav .nav-link.btn { text-transform: none; margin: 0; margin-bottom: -0.05rem; border-radius: 0; } .nav .nav-link:hover, .nav .nav-link:focus, .nav .nav-link:active { color: inherit; } .nav .nav-link:hover, .nav .nav-link.active { box-shadow: 0 -0.15rem 0 #4aaed9 inset; transition: box-shadow 0.2s ease-in; } .nav .nav-link:hover, .nav .nav-link:focus, .nav .nav-link:active, .nav .nav-link.active { text-decoration: none; } .nav .nav-link.active { color: white; font-weight: 400; } .nav .nav-link.nav-item { margin-right: 1.2rem; } .sub-nav, .subnav { display: flex; box-shadow: 0 -0.05rem 0 #cccccc inset; justify-content: space-between; align-items: center; background-color: #17242b; height: 1.8rem; } .sub-nav .nav, .subnav .nav { flex: 1 1 auto; padding-left: 1.2rem; } .sub-nav aside, .subnav aside { flex: 0 0 auto; display: flex; align-items: center; height: 1.8rem; padding: 0 1.2rem; } .sub-nav aside > :last-child, .subnav aside > :last-child { margin-right: 0; padding-right: 0; } .sidenav { line-height: 1.2rem; max-width: 15.6rem; min-width: 10.8rem; width: 18%; border-right: 0.05rem solid #152228; display: flex; flex-direction: column; } .sidenav .sidenav-content { flex: 1 1 auto; overflow-x: hidden; padding-bottom: 1.2rem; } .sidenav .sidenav-content .nav-link { border-radius: 0; border-top-left-radius: 0.15rem; border-bottom-left-radius: 0.15rem; display: inline-block; color: inherit; cursor: pointer; text-decoration: none; width: 100%; } .sidenav .sidenav-content > .nav-link { margin: 1.2rem 0 0 1.5rem; padding-left: 0.6rem; color: #acbac3; font-weight: 500; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 0.7rem; line-height: 1.2rem; letter-spacing: normal; } .sidenav .sidenav-content > .nav-link:hover { background: #324f62; } .sidenav .sidenav-content > .nav-link.active { background: #324f62; color: black; } .sidenav .nav-group { color: #acbac3; font-weight: 400; font-size: 0.7rem; letter-spacing: normal; margin-top: 1.2rem; width: 100%; } .sidenav .nav-group .nav-list, .sidenav .nav-group label { padding: 0 0 0 1.8rem; cursor: pointer; display: inline-block; width: 100%; margin: 0 0.3rem; } .sidenav .nav-group .nav-list { list-style: none; margin-top: 0; } .sidenav .nav-group .nav-list .nav-link { line-height: 0.8rem; padding: 0.2rem 0 0.2rem 0.6rem; } .sidenav .nav-group .nav-list .nav-link:hover { background: #324f62; } .sidenav .nav-group .nav-list .nav-link.active { background: #324f62; color: black; } .sidenav .nav-group label { color: #acbac3; font-weight: 500; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 0.7rem; line-height: 1.2rem; letter-spacing: normal; } .sidenav .nav-group input[type=checkbox] { position: absolute; clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); padding: 0; border: 0; height: 1px; width: 1px; overflow: hidden; white-space: nowrap; top: 0; left: 0; } .sidenav .nav-group input[type=checkbox]:focus + label { outline: #3b99fc auto 0.25rem; } .sidenav .collapsible label { padding: 0 0 0 1.3rem; } .sidenav .collapsible label:after { content: ""; float: left; height: 0.5rem; width: 0.5rem; transform: translateX(-0.4rem) translateY(0.35rem); background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2012%22%3E%0A%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3Cstyle%3E.cls-1%7Bfill%3A%239a9a9a%3B%7D%3C%2Fstyle%3E%0A%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%3Ctitle%3ECaret%3C%2Ftitle%3E%0A%20%20%20%20%3Cpath%20class%3D%22cls-1%22%20d%3D%22M6%2C9L1.2%2C4.2a0.68%2C0.68%2C0%2C0%2C1%2C1-1L6%2C7.08%2C9.84%2C3.24a0.68%2C0.68%2C0%2C1%2C1%2C1%2C1Z%22%2F%3E%0A%3C%2Fsvg%3E%0A"); background-repeat: no-repeat; background-size: contain; vertical-align: middle; margin: 0; } .sidenav .collapsible input[type=checkbox]:checked ~ .nav-list, .sidenav .collapsible input[type=checkbox]:checked ~ ul { height: 0; display: none; } .sidenav .collapsible input[type=checkbox] ~ .nav-list, .sidenav .collapsible input[type=checkbox] ~ ul { height: auto; } .sidenav .collapsible input[type=checkbox]:checked ~ label:after { transform: rotate(-90deg) translateX(-0.35rem) translateY(-0.4rem); } body:not([cds-text]) { color: #acbac3; font-weight: 400; font-size: 0.7rem; letter-spacing: normal; line-height: 1.2rem; margin-bottom: 0px; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; margin-top: 0px !important; } html:not([cds-text]) { color: #eaedf0; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 125%; } a:link { color: #4aaed9; text-decoration: none; } h1:not([cds-text]) { color: #eaedf0; font-weight: 200; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 1.6rem; letter-spacing: normal; line-height: 2.4rem; margin-top: 1.2rem; margin-bottom: 0; } h2:not([cds-text]) { color: #eaedf0; font-weight: 200; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 1.4rem; letter-spacing: normal; line-height: 2.4rem; margin-top: 1.2rem; margin-bottom: 0; } h3:not([cds-text]) { color: #eaedf0; font-weight: 200; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 1.1rem; letter-spacing: normal; line-height: 1.2rem; margin-top: 1.2rem; margin-bottom: 0; } h4:not([cds-text]) { color: #eaedf0; font-weight: 200; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 0.9rem; letter-spacing: normal; line-height: 1.2rem; margin-top: 1.2rem; margin-bottom: 0; } .table th { color: #eaedf0; font-size: 0.55rem; font-weight: 600; letter-spacing: 0.03em; background-color: #1b2a32; vertical-align: bottom; border-bottom-style: solid; border-bottom-width: 0.05rem; border-bottom-color: #495865; border-top: 0 none; } .table { border-collapse: separate; border-style: solid; border-width: 0.05rem; border-color: #495865; border-radius: 0.15rem; background-color: #21333b; color: #acbac3; margin: 0; margin-top: 1.2rem; max-width: 100%; width: 100%; } h3 { display: block; font-size: 1.17em; margin-block-start: 1em; margin-block-end: 1em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold; } h4 { display: block; margin-block-start: 1.33em; margin-block-end: 1.33em; margin-inline-start: 0px; margin-inline-end: 0px; font-weight: bold; } .table th, .table td {font-size: 0.65rem; line-height: 0.7rem; border-top-style: solid; border-top-width: 0.05rem; border-top-color: #495865; padding: 0.55rem 0.6rem 0.55rem; text-align: left; vertical-align: top; } th { display: table-cell; vertical-align: inherit; font-weight: bold; text-align: -internal-center; } table { display: table; border-collapse: separate; box-sizing: border-box; text-indent: initial; border-spacing: 2px; border-color: gray; } ' } else { $clarityCssHeader = ' <head> <style> <!--- Used Clarify CSS components for this project ---> article, aside, details, figcaption, figure, footer, header, main, menu, nav, section, summary { display: block; } .main-container { display: flex; flex-direction: column; height: 100vh; background: var(--clr-global-app-background, #fafafa); } header.header-6, .header.header-6 { background-color: var(--clr-header-6-bg-color, #00364d); } header, .header { display: flex; color: var(--clr-header-font-color, #fafafa); background-color: var(--clr-header-bg-color, #333333); height: 3rem; white-space: nowrap; } .nav {display: flex; height: 1.8rem; list-style-type: none; align-items: center; margin: 0; width: 100%; white-space: nowrap; box-shadow: 0 -0.05rem 0 #cccccc inset; box-shadow: 0 -0.05rem 0 var(--clr-nav-box-shadow-color, #cccccc) inset; } .nav .nav-item { display: inline-block; margin-right: 1.2rem; } .nav .nav-item.active > .nav-link { color: black; color: var(--clr-nav-link-active-color, black); box-shadow: 0 -0.05rem 0 #cccccc inset; box-shadow: 0 -0.05rem 0 var(--clr-nav-box-shadow-color, #cccccc) inset; } .nav .nav-link { color: #666666; color: var(--clr-nav-link-color, #666666); font-size: 0.7rem; font-weight: 400; font-weight: var(--clr-nav-link-font-weight, 400); letter-spacing: normal; line-height: 1.8rem; display: inline-block; padding: 0 0.15rem; box-shadow: none; } .nav .nav-link.btn { text-transform: none; margin: 0; margin-bottom: -0.05rem; border-radius: 0; } .nav .nav-link:hover, .nav .nav-link:focus, .nav .nav-link:active { color: inherit; } .nav .nav-link:hover, .nav .nav-link.active { box-shadow: 0 -0.15rem 0 #0072a3 inset; box-shadow: 0 -0.15rem 0 var(--clr-nav-active-box-shadow-color, #0072a3) inset; transition: box-shadow 0.2s ease-in; } .nav .nav-link:hover, .nav .nav-link:focus, .nav .nav-link:active, .nav .nav-link.active { text-decoration: none; } .nav .nav-link.active { color: black; color: var(--clr-nav-link-active-color, black); font-weight: 400; font-weight: var(--clr-nav-link-active-font-weight, 400); } .nav .nav-link.nav-item { margin-right: 1.2rem; } .sub-nav, .subnav { display: flex; box-shadow: 0 -0.05rem 0 #cccccc inset; box-shadow: 0 -0.05rem 0 var(--clr-nav-box-shadow-color, #cccccc) inset; justify-content: space-between; align-items: center; background-color: white; background-color: var(--clr-subnav-bg-color, white); height: 1.8rem; } .sub-nav .nav, .subnav .nav { flex: 1 1 auto; padding-left: 1.2rem; } .sub-nav aside, .subnav aside { flex: 0 0 auto; display: flex; align-items: center; height: 1.8rem; padding: 0 1.2rem; } .sub-nav aside > :last-child, .subnav aside > :last-child { margin-right: 0; padding-right: 0; } .sidenav { line-height: 1.2rem; max-width: 15.6rem; min-width: 10.8rem; width: 18%; border-right: 0.05rem solid #cccccc; display: flex; flex-direction: column; } .sidenav .collapsible label padding: 0 0 0 1.3rem; } .sidenav .nav-group label {color: #333333; color: var(--clr-sidenav-header-color, #333333); font-weight: 500; font-weight: var(--clr-sidenav-header-font-weight, 500); font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-family: var(--clr-sidenav-header-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 0.7rem; line-height: 1.2rem; letter-spacing: normal; } .sidenav { line-height: 1.2rem; max-width: 15.6rem; min-width: 10.8rem; width: 18%; border-right: 0.05rem solid #cccccc; display: flex; flex-direction: column; } .sidenav .sidenav-content { flex: 1 1 auto; overflow-x: hidden; padding-bottom: 1.2rem; } .sidenav .sidenav-content .nav-link { border-radius: 0; border-top-left-radius: 0.15rem; border-top-left-radius: var(--clr-sidenav-link-active-border-radius, 0.15rem); border-bottom-left-radius: 0.15rem; border-bottom-left-radius: var(--clr-sidenav-link-active-border-radius, 0.15rem); display: inline-block; color: inherit; cursor: pointer; text-decoration: none; width: 100%; } .sidenav .sidenav-content > .nav-link { margin: 1.2rem 0 0 1.5rem; padding-left: 0.6rem; color: #333333; color: var(--clr-sidenav-header-color, #333333); font-weight: 500; font-weight: var(--clr-sidenav-header-font-weight, 500); font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-family: var(--clr-sidenav-header-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif);font-size: 0.7rem; line-height: 1.2rem; letter-spacing: normal; } .sidenav .sidenav-content > .nav-link:hover { background: #e8e8e8; background: var(--clr-sidenav-link-hover-color, #e8e8e8); } .sidenav .sidenav-content > .nav-link.active { background: #d8e3e9; background: var(--clr-sidenav-link-active-bg-color, #d8e3e9); color: black; color: var(--clr-sidenav-link-active-color, black); } .sidenav .nav-group { color: #666666; color: var(--clr-sidenav-color, #666666); font-weight: 400; font-weight: var(--clr-sidenav-font-weight, 400); font-size: 0.7rem; letter-spacing: normal; margin-top: 1.2rem; width: 100%; } .sidenav .nav-group .nav-list, .sidenav .nav-group label { padding: 0 0 0 1.8rem; cursor: pointer; display: inline-block; width: 100%; margin: 0 0.3rem; } .sidenav .nav-group .nav-list { list-style: none; margin-top: 0; } .sidenav .nav-group .nav-list .nav-link { line-height: 0.8rem; padding: 0.2rem 0 0.2rem 0.6rem; } .sidenav .nav-group .nav-list .nav-link:hover { background: #e8e8e8; background: var(--clr-sidenav-link-hover-color, #e8e8e8); } .sidenav .nav-group .nav-list .nav-link.active { background: #d8e3e9; background: var(--clr-sidenav-link-active-bg-color, #d8e3e9); color: black; color: var(--clr-sidenav-link-active-color, black); } .sidenav .nav-group label { color: #333333; color: var(--clr-sidenav-header-color, #333333); font-weight: 500; font-weight: var(--clr-sidenav-header-font-weight, 500); font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-family: var(--clr-sidenav-header-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 0.7rem; line-height: 1.2rem; letter-spacing: normal; } .sidenav .nav-group input[type=checkbox] { position: absolute; clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); padding: 0; border: 0; height: 1px; width: 1px; overflow: hidden; white-space: nowrap; top: 0; left: 0; } .sidenav .nav-group input[type=checkbox]:focus + label { outline: #3b99fc auto 0.25rem; } .sidenav .collapsible label { padding: 0 0 0 1.3rem; } .sidenav .collapsible label:after { content: ""; float: left; height: 0.5rem; width: 0.5rem; transform: translateX(-0.4rem) translateY(0.35rem); background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2012%22%3E%0A%20%20%20%20%3Cdefs%3E%0A%20%20%20%20%20%20%20%20%3Cstyle%3E.cls-1%7Bfill%3A%239a9a9a%3B%7D%3C%2Fstyle%3E%0A%20%20%20%20%3C%2Fdefs%3E%0A%20%20%20%20%3Ctitle%3ECaret%3C%2Ftitle%3E%0A%20%20%20%20%3Cpath%20class%3D%22cls-1%22%20d%3D%22M6%2C9L1.2%2C4.2a0.68%2C0.68%2C0%2C0%2C1%2C1-1L6%2C7.08%2C9.84%2C3.24a0.68%2C0.68%2C0%2C1%2C1%2C1%2C1Z%22%2F%3E%0A%3C%2Fsvg%3E%0A"); background-repeat: no-repeat; background-size: contain; vertical-align: middle; margin: 0; } .sidenav .collapsible input[type=checkbox]:checked ~ .nav-list, .sidenav .collapsible input[type=checkbox]:checked ~ ul { height: 0; display: none; } .sidenav .collapsible input[type=checkbox] ~ .nav-list, .sidenav .collapsible input[type=checkbox] ~ ul { height: auto; } .sidenav .collapsible input[type=checkbox]:checked ~ label:after { transform: rotate(-90deg) translateX(-0.35rem) translateY(-0.4rem); } body:not([cds-text]) { color: var(--clr-p1-color, #666666); font-weight: var(--clr-p1-font-weight, 400); font-size: 0.7rem; letter-spacing: normal; line-height: 1.2rem; margin-bottom: 0px; font-family: var(--clr-font, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); margin-top: 0px !important; } html:not([cds-text]) { color: var(--clr-global-font-color, #666666); font-family: var(--clr-font, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 125%; } a:link { color: var(--clr-link-color, #0072a3); text-decoration: none; } h1:not([cds-text]) { color: var(--clr-h1-color, black); font-weight: var(--clr-h1-font-weight, 200); font-family: var(--clr-h1-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 1.6rem; letter-spacing: normal; line-height: 2.4rem; margin-top: 1.2rem; margin-bottom: 0px; } h2:not([cds-text]) { color: var(--clr-h2-color, black); font-weight: var(--clr-h2-font-weight, 200); font-family: var(--clr-h2-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 1.4rem; letter-spacing: normal; line-height: 2.4rem; margin-top: 1.2rem; margin-bottom: 0px; } h3:not([cds-text]) { color: var(--clr-h3-color, black); font-weight: var(--clr-h3-font-weight, 200); font-family: var(--clr-h3-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 1.1rem; letter-spacing: normal; line-height: 1.2rem; margin-top: 1.2rem; margin-bottom: 0px; } h4:not([cds-text]) { color: var(--clr-h4-color, black); font-weight: var(--clr-h4-font-weight, 200); font-family: var(--clr-h4-font-family, ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif); font-size: 0.9rem; letter-spacing: normal; line-height: 1.2rem; margin-top: 1.2rem; margin-bottom: 0px; } .table th { color: var(--clr-thead-color, #666666); font-size: 0.55rem; font-weight: 600; letter-spacing: 0.03em; background-color: var(--clr-thead-bgcolor, #fafafa); vertical-align: bottom; border-bottom-style: solid; border-bottom-width: var(--clr-table-borderwidth, 0.05rem); border-bottom-color: var(--clr-table-border-color, #cccccc); border-top: 0px none; } .table { border-collapse: separate; border-style: solid; border-width: var(--clr-table-borderwidth, 0.05rem); border-color: var(--clr-table-border-color, #cccccc); border-radius: var(--clr-table-border-radius, 0.15rem); background-color: var(--clr-table-bgcolor, white); color: var(--clr-table-font-color, #666666); margin: 1.2rem 0px 0px; max-width: 100%; width: 100%; } a { background-color: transparent; } abbr[title] { border-bottom: none; text-decoration: underline dotted; } b, strong { font-weight: inherit; } b, strong { font-weight: bolder; } [type="checkbox"], [type="radio"] { box-sizing: border-box; padding: 0px; } pre { border-color: var(--clr-color-neutral-400, #cccccc); border-width: var(--clr-global-borderwidth, 0.05rem); border-style: solid; border-radius: var(--clr-global-borderradius, 0.15rem); } ul:not([cds-list]), ol:not([cds-list]) { list-style-position: inside; margin-left: 0px; margin-top: 0px; margin-bottom: 0px; padding-left: 0px; } li > ul:not([cds-list]) { margin-top: 0px; margin-left: 1.1em; } body p:not([cds-text]) { color: var(--clr-p1-color, #666666); font-weight: var(--clr-p1-font-weight, 400); font-size: 0.7rem; letter-spacing: normal; line-height: 1.2rem; margin-top: 1.2rem; margin-bottom: 0px; } a:visited { color: var(--clr-link-visited-color, #5659b8); text-decoration: none; } .main-container .content-container .content-area > :first-child { margin-top: 0px; } .nav .nav-link:hover, .nav .nav-link.active { box-shadow: 0 -0.15rem 0 var(--clr-nav-active-box-shadow-color, #0072a3) inset; transition: box-shadow 0.2s ease-in 0s; } .nav .nav-link.active { color: var(--clr-nav-link-active-color, black); font-weight: var(--clr-nav-link-active-font-weight, 400); } :root { --clr-subnav-bg-color:var(--clr-color-neutral-0); --clr-nav-box-shadow-color:var(--clr-color-neutral-400); } :root { --clr-sidenav-border-color:var(--clr-color-neutral-400); --clr-sidenav-border-width:var(--clr-global-borderwidth); --clr-sidenav-link-hover-color:var(--clr-color-neutral-200); --clr-sidenav-link-active-color:var(--clr-color-neutral-1000); --clr-sidenav-link-active-bg-color:var(--clr-global-selection-color); --clr-sidenav-link-active-border-radius:var(--clr-global-borderradius); --clr-sidenav-header-color:var(--clr-h6-color); --clr-sidenav-header-font-weight:var(--clr-h6-font-weight); --clr-sidenav-header-font-family:var(--clr-h6-font-family); --clr-sidenav-color:var(--clr-p1-color); --clr-sidenav-font-weight:var(--clr-p1-font-weight); } .table th, .table td { font-size: 0.65rem; line-height: 0.7rem; border-top-style: solid; border-top-width: var(--clr-table-borderwidth, 0.05rem); border-top-color: var(--clr-tablerow-bordercolor, #e8e8e8); padding: 0.55rem 0.6rem; text-align: left; vertical-align: top; } ' } $clarityCssShared = ' .alertOK { color: #61B715; font-weight: bold } .alertWarning { color: #FDD008; font-weight: bold } .alertCritical { color: #F55047; font-weight: bold } .alertSkipped { font-weight: bold } .table th, .table td { text-align: left; } :root { --cds-global-base: 20; } body { margin: 0px; } .main-container .content-container .sidenav { flex: 0 0 auto; order: -1; overflow: hidden; } .main-container .content-container .content-area > :first-child { margin-top: 0; } .main-container .content-container .content-area { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 1.2rem 1.2rem 1.2rem 1.2rem; } .main-container header, .main-container .header { flex: 0 0 3rem; } .main-container .header .branding { max-width: auto; min-width: 0px; overflow: hidden; } .main-container .sub-nav, .main-container .subnav { flex: 0 0 1.8rem; } .main-container .content-container { display: flex; flex: 1 1 auto; min-height: 0.05rem; } header .branding, .header .branding { display: flex; flex: 0 0 auto; min-width: 10.2rem; padding: 0px 1.2rem; height: 3rem; } header .branding .title, .header .branding .title { color: #fafafa; font-weight: 400; font-family: ClarityCityRegular, "Avenir Next", "Helvetica Neue", Arial, sans-serif; font-size: 0.8rem; letter-spacing: 0.01em; line-height: 3rem; text-decoration: none; } header .branding > a, header .branding > .nav-link, .header .branding > a, .header .branding > .nav-link { display: inline-flex; align-items: center; height: 3rem; } header .branding .clr-icon, header .branding cds-icon, header .branding clr-icon, .header .branding .clr-icon, .header .branding cds-icon, .header .branding clr-icon { flex-grow: 0; flex-shrink: 0; height: 1.8rem; width: 1.8rem; margin-right: 0.45rem; } ul:not([cds-list]), ol:not([cds-list]) { list-style-position: inside; margin-left: 0; margin-top: 0; margin-bottom: 0; padding-left: 0; } a { background-color: transparent; -webkit-text-decoration-skip: objects; } h1 { font-size: 2em; margin: 0.67em 0px; } img { border-style: none; } img { vertical-align: middle; } *, ::before, ::after { box-sizing: border-box; } *, ::before, ::after { box-sizing: inherit; } table { border-spacing: 0px; } pre { margin: 0.6rem 0px; } html { box-sizing: border-box; } html { -webkit-tap-highlight-color: transparent; } html { -ms-overflow-style: scrollbar; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } html { font-family: sans-serif; line-height: 1.15; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } .table tbody tr:first-child td { border-top: 0px none; } .table thead th:first-child { border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-top-left-radius: var(--clr-table-cornercellradius, 0.1rem); } .table thead th:last-child { border-top-left-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-top-right-radius: var(--clr-table-cornercellradius, 0.1rem); } .table tbody:last-child tr:last-child td:first-child { border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: var(--clr-table-cornercellradius, 0.1rem); } .table tbody:last-child tr:last-child td:last-child { border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-left-radius: 0px; border-bottom-right-radius: var(--clr-table-cornercellradius, 0.1rem); } @font-face {font-family: ClarityCityRegular;src: url(data:font/ttf;base64,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AvQAWwLlABkEKwAeBCsAHgQrAB4EKwAeBCsAHgKlABwCoQATAqEAEwKhABMCoQATAqEAEwJlACwCZQAsAmUALAJlACwCLQAkAi0AJAItACQCLQAkAi0AJAItACQCLQAkAi0AJAItACQCLQAkA7IAJAJ7AFcCBwApAgcAKQIHACkCBwApAnsALwJZACsCpQAvAnsALwJHACwCRwAsAkcALAJHACwCRwAsAkcALAJYACwCRwAsAkcALAJYACwCRwAsAT0AGAJ2ACoCdgAqAnYAKgJZAFcA+QBLAPkAVwD5AFcA+f/2APn/7wD5/9IA+f/iAPkAMADv/84A7//OAiUAVwIlAFcA+QBXAPkAVwEWAFcBJwAcA4kAVwJZAFcCWQBXAlkAVwJZAFcCWQBXAmkAKgJpACoCaQAqAmkAKgJpACoCaQAqAmkAKgKBADMCaQAqBCAAKgJ7AFcCfABYAnsALwGMAFcBjABXAYwAVgGMAFcB3gAhAd4AIQHeACEB3gAhAkMAVwFZABUBuQAVAVkAFQJZAEsCWQBLAlkASwJZAEsCWQBLAlkASwJZAEsCWQBLAlkASwIRAAoDHQAQAx0AEAMdABADHQAQAx0AEAH/AAwCGAAJAhgACQIYAAkCGAAJAhgACQHbACEB2wAhAdsAIQHbACECewAvAnsALwJ7AC8CewAvAnsALwJ7AC8CewAvAnsALwJ7AC8CewAvArMAPAFlABQCNQAiAkYAHgJMABsCUQA3AmgAPwIDABYCbQA7AmgAMgFEAGwBOgBlATMAYwE1AGMC1ABsAT0AaAE9AGkCCQAiAgkAMwFBAG0BrgBfAc8AVgKxAC8BtAAAAbQAAAFIADUBSAAaAWIAFgFiABsBcgBpAXIAHQGvAFYCfQBWA64AVgMAAFYB4ABUAeAAUAHgAFQBGABQARgAVAIBACICAAAsATsAIgE6ACwBygBdARYAXQPoAAAB9AAAAPoAAAETAAAApgAAAU0AAAJcAFECkgBGAzkAUAKcAFUC8QA6Ak8AQgLPAIICFgBOAlMARALRAIMCUQBPAlEAOwIbAG0B/AA/A0YAPwS6AD8DqABJAsIAPwKYAD0DrQBKApUAVANBAFMBQgCDAAD/UgAA/10BBwAeAZEANQFbACYBJQAwAVsAJgGxAEsA8QBLAQMAHQHOAB0B4gBWASYAQwFNAD8BxABC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) </style> </head> <body> <div class="main-container"> <header class="header header-6"> <div class="branding"> <a href=""> <cds-icon shape="vm-bug"> <img height="36px" width="36px" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzNiAzNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzYgMzY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojMDA5MURBO30KCS5zdDF7ZmlsbDojMUQ0MjhBO30KCS5zdDJ7ZmlsbDojMDBDMUQ1O30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI4LjIsMzAuNGMtMC4zLDAtMC41LTAuMi0wLjYtMC40Yy0wLjItMC40LDAtMC44LDAuMy0wLjljMy45LTEuOCw2LjQtNS43LDYuNC05LjljMC0yLjMtMC43LTQuNC0yLTYuMwoJYy0xLjMtMS44LTMtMy4yLTUuMS00Yy0wLjQtMC4xLTAuNi0wLjYtMC40LTAuOWMwLjEtMC40LDAuNi0wLjYsMC45LTAuNGMyLjMsMC45LDQuMywyLjQsNS44LDQuNWMxLjUsMi4xLDIuMyw0LjUsMi4zLDcuMQoJYzAsNC44LTIuOCw5LjItNy4yLDExLjJDMjguNCwzMC40LDI4LjMsMzAuNCwyOC4yLDMwLjR6Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0yMy4yLDExLjNjLTAuMSwwLTAuMiwwLTAuMywwYy0wLjUtMS44LTEuNi0zLjMtMy4xLTQuNWMtMS43LTEuMy0zLjctMi01LjgtMmMtNS4xLDAtOS4zLDQuMS05LjMsOS4yCgljMCwwLDAsMC4xLDAsMC4xYy0zLjUsMS4xLTUuNCw0LjktNC4zLDguNGMwLjYsMS44LDEuOSwzLjMsMy43LDQuMXYtMS42Yy0yLjUtMS41LTMuNC00LjctMS45LTcuMmMwLjctMS4zLDItMi4yLDMuNC0yLjVsMC42LTAuMQoJbDAtMC42YzAtMC4yLDAtMC40LDAtMC42YzAtNC4zLDMuNS03LjcsNy44LTcuN2MzLjYsMCw2LjgsMi41LDcuNiw2bDAuMSwwLjZsMC42LTAuMWMwLjIsMCwwLjUsMCwwLjcsMGMzLjYsMCw2LjUsMi45LDYuNSw2LjUKCWMwLDEuOS0wLjgsMy43LTIuMiw0Ljl2MS44YzIuMy0xLjQsMy43LTMuOSwzLjctNi42QzMxLjEsMTQuOCwyNy41LDExLjMsMjMuMiwxMS4zeiIvPgo8cGF0aCBjbGFzcz0ic3QyIiBkPSJNMS4yLDEyLjJjMCwwLTAuMSwwLTAuMSwwYy0wLjQtMC4xLTAuNi0wLjQtMC42LTAuOEMxLjcsNC45LDcuNCwwLjIsMTQsMC4yYzMuMSwwLDYuMiwxLjEsOC42LDMKCWMwLjksMC43LDEuNiwxLjUsMi4zLDIuM2MwLjIsMC4zLDAuMiwwLjgtMC4xLDFjLTAuMywwLjItMC44LDAuMi0xLTAuMWMtMC42LTAuOC0xLjMtMS41LTItMi4xYy0yLjItMS44LTUtMi43LTcuOC0yLjcKCWMtNiwwLTExLjEsNC4yLTEyLjIsMTBDMS44LDEyLDEuNSwxMi4yLDEuMiwxMi4yeiBNMTguMywxOGMtMC40LDAtMC43LDAuMy0wLjgsMC43YzAsMC40LDAuMywwLjcsMC43LDAuOGMwLDAsMCwwLDAuMSwwaDMuOQoJbC04LjUsOC4xYy0wLjMsMC4zLTAuMywwLjcsMCwxYzAuMywwLjMsMC43LDAuMywxLDBsOC41LTh2My45YzAsMC40LDAuNCwwLjcsMC44LDAuN2MwLjQsMCwwLjctMC4zLDAuNy0wLjdWMThMMTguMywxOEwxOC4zLDE4egoJIE0xNC43LDIzLjFWMThIOS42Yy0wLjQsMC0wLjcsMC40LTAuNywwLjhjMCwwLjQsMC4zLDAuNywwLjcsMC43aDIuNkw3LDI0LjJjLTAuMywwLjMtMC4zLDAuNywwLDFjMC4zLDAuMywwLjcsMC4zLDEsMGw1LjItNC44djIuNwoJYzAsMC40LDAuNCwwLjcsMC44LDAuN0MxNC40LDIzLjgsMTQuNywyMy41LDE0LjcsMjMuMUwxNC43LDIzLjF6IE0xOC44LDI4LjZjMCwwLjQsMC4zLDAuNywwLjcsMC43aDIuN2wtNC43LDUuMQoJYy0wLjMsMC4zLTAuMywwLjcsMCwxYzAuMywwLjMsMC43LDAuMywxLDBjMCwwLDAsMCwwLjEtMC4xbDQuNi01VjMzYzAsMC40LDAuMywwLjcsMC43LDAuOGMwLjQsMCwwLjctMC4zLDAuOC0wLjdjMCwwLDAsMCwwLTAuMQoJdi01LjFoLTUuMUMxOS4xLDI3LjksMTguOCwyOC4yLDE4LjgsMjguNkwxOC44LDI4LjZ6Ii8+Cjwvc3ZnPgo=" alt="VMware Cloud Foundation"/> </cds-icon> <span class="title">VMware Cloud Foundation</span> </a> </div> </header> ' $clarityCssHeader += $clarityCssShared $clarityCssHeader } Export-ModuleMember -Function Get-ClarityReportHeader Function Get-ClarityReportNavigation { Param ( [Parameter (Mandatory = $true)] [ValidateSet("health","alert","config","upgrade","overview")] [String]$reportType ) if ($reportType -eq "health") { # Define the Clarity Cascading Style Sheets (CSS) for a Health Report $clarityCssNavigation = ' <nav class="subnav"> <ul class="nav"> <li class="nav-item"> <a class="nav-link active" href="">Health Report</a> </li> </ul> </nav> <div class="content-container"> <nav class="sidenav"> <section class="sidenav-content"> <section class="nav-group collapsible"> <input id="general" type="checkbox"/> <label for="general">General</label> <ul class="nav-list"> <li><a class="nav-link" href="#general-service">Service Health</a></li> <li><a class="nav-link" href="#general-version">Version Health</a></li> <li><a class="nav-link" href="#general-connectivity">Connectivity</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="security" type="checkbox"/> <label for="security">Security</label> <ul class="nav-list"> <li><a class="nav-link" href="#security-password">Passwords</a></li> <li><a class="nav-link" href="#security-certificate">Certificates</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="infrastructure" type="checkbox"/> <label for="infrastructure">Infrastructure</label> <ul class="nav-list"> <li><a class="nav-link" href="#infra-backup">Backups</a></li> <li><a class="nav-link" href="#infra-snapshot">Snapshots</a></li> <li><a class="nav-link" href="#infra-dns-forward">DNS Forward Lookup</a></li> <li><a class="nav-link" href="#infra-dns-reverse">DNS Reverse Lookup</a></li> <li><a class="nav-link" href="#infra-ntp">Network Time</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="vcenter" type="checkbox"/> <label for="vcenter">vCenter Server</label> <ul class="nav-list"> <li><a class="nav-link" href="#vcenter-overall">Overall Health</a></li> <li><a class="nav-link" href="#vcenter-ring-topology">Single Sign-On Health</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="esxi" type="checkbox"/> <label for="esxi">ESXi</label> <ul class="nav-list"> <li><a class="nav-link" href="#esxi-overall">Overall Health</a></li> <li><a class="nav-link" href="#esxi-coredump">Core Dump Health</a></li> <li><a class="nav-link" href="#esxi-license">Licensing Health</a></li> <li><a class="nav-link" href="#esxi-disk">Disk Health</a></li> <li><a class="nav-link" href="#esxi-connection">Connection Health</a></li> <li><a class="nav-link" href="#esxi-free-pool">Free Pool Health</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="vsan" type="checkbox"/> <label for="vsan">vSAN</label> <ul class="nav-list"> <li><a class="nav-link" href="#vsan-overall">Overall Health</a></li> <li><a class="nav-link" href="#vsan-spbm">Storage Policy Health</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="nsx" type="checkbox"/> <label for="nsx">NSX</label> <ul class="nav-list"> <li><a class="nav-link" href="#nsx-local-manager">NSX Local Managers</a></li> <li><a class="nav-link" href="#nsx-edge-cluster">NSX Edge Cluster</a></li> <li><a class="nav-link" href="#nsx-edge">NSX Edge Nodes</a></li> <li><a class="nav-link" href="#nsx-tn">NSX Transport Nodes</a></li> <li><a class="nav-link" href="#nsx-tn-tunnel">NSX Transport Node Tunnels</a></li> <li><a class="nav-link" href="#nsx-t0-bgp">NSX Tier-0 Gateway BGP</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="storage" type="checkbox"/> <label for="storage">Storage</label> <ul class="nav-list"> <li><a class="nav-link" href="#storage-sddcmanager">SDDC Manager</a></li> <li><a class="nav-link" href="#storage-vcenter">vCenter Server</a></li> <li><a class="nav-link" href="#storage-esxi">ESXi</a></li> <li><a class="nav-link" href="#storage-datastore">Datastores</a></li> <li><a class="nav-link" href="#storage-vm-cdrom">Connected CD-ROMs</a></li> </ul> </section> </section> </nav> <div class="content-area"> <div class="content-area">' $clarityCssNavigation } if ($reportType -eq "alert") { # Define the Clarity Cascading Style Sheets (CSS) for a System Alert Report $clarityCssNavigation = ' <nav class="subnav"> <ul class="nav"> <li class="nav-item"> <a class="nav-link active" href="">Alert Report</a> </li> </ul> </nav> <div class="content-container"> <nav class="sidenav"> <section class="sidenav-content"> <a class="nav-link nav-icon" href="#alert-vcenter">vCenter Server</a> <a class="nav-link nav-icon" href="#alert-esxi">ESXi</a> <a class="nav-link nav-icon" href="#alert-vsan">vSAN</a> <a class="nav-link nav-icon" href="#alert-nsx">NSX</a> </section> </nav> <div class="content-area"> <div class="content-area">' $clarityCssNavigation } if ($reportType -eq "config") { # Define the Clarity Cascading Style Sheets (CSS) for a Configuration Report $clarityCssNavigation = ' <nav class="subnav"> <ul class="nav"> <li class="nav-item"> <a class="nav-link active" href="">Configuration Report</a> </li> </ul> </nav> <div class="content-container"> <nav class="sidenav"> <section class="sidenav-content"> <section class="nav-group collapsible"> <input id="vcenter" type="checkbox"/> <label for="vcenter">vCenter Server</label> <ul class="nav-list"> <li><a class="nav-link" href="#cluster-config">Cluster Configuration</a></li> <li><a class="nav-link" href="#cluster-drs-rules">vSphere DRS Rules</a></li> <li><a class="nav-link" href="#cluster-resource-pools">Resource Pools</a></li> <li><a class="nav-link" href="#cluster-overrides">VM Overrides</a></li> <li><a class="nav-link" href="#cluster-networks">Virtual Networks</a></li> </ul> </section> <section class="nav-group collapsible"> <input id="esxi" type="checkbox"/> <label for="esxi">ESXi Hosts</label> <ul class="nav-list"> <li><a class="nav-link" href="#esxi-security">Security Configuration</a></li> </ul> </section> </section> </nav> <div class="content-area"> <div class="content-area">' $clarityCssNavigation } if ($reportType -eq "upgrade") { # Define the Clarity Cascading Style Sheets (CSS) for a Upgrade Report $clarityCssNavigation = ' <nav class="subnav"> <ul class="nav"> <li class="nav-item"> <a class="nav-link active" href="">Upgrade Precheck Report</a> </li> </ul> </nav> <div class="content-container"> <div class="content-area">' $clarityCssNavigation } if ($reportType -eq "overview") { # Define the Clarity Cascading Style Sheets (CSS) for a System Overview Report $clarityCssNavigation = ' <nav class="subnav"> <ul class="nav"> <li class="nav-item"> <a class="nav-link active" href="">System Overview Report</a> </li> </ul> </nav> <div class="content-container"> <div class="content-area">' $clarityCssNavigation } } Export-ModuleMember -Function Get-ClarityReportNavigation Function Get-ClarityReportFooter { # Define the default Clarity Cascading Style Sheets (CSS) for the HTML report footer $clarityCssFooter = ' </div> </div> </div> </body> </html>' $clarityCssFooter } Export-ModuleMember -Function Get-ClarityReportFooter Function Format-DfStorageHealth { <# .SYNOPSIS Formats output from 'fd -h' command and sets alerts based on thresholds. .DESCRIPTION The Format-DfStorageHealth cmdlet formats and returns output from 'df -h' in html or plain text .EXAMPLE Format-DfStorageHealth -reportTitle '<h3>SDDC Manager Disk Health Status</h3>' -dfOutput $dfOutput -html -failureOnly -greenThreshold 20 -redThreshold 40 This example returns only failures (Alert is not GREEN), produces html report with title '<h3>SDDC Manager Disk Health Status</h3>' and overwrites the default thresholds. .PARAMETER dfOutput The output from 'df -h' command. .PARAMETER systemFqdn The fully qualified domain name of the system. .PARAMETER failureOnly Switch to only output issues to the report. .PARAMETER greenThreshold The threshold for a "Green" alert. Default is 70%. .PARAMETER redThreshold The threshold for a "Red" alert. Default is 85%. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] $dfOutput, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] $systemFqdn, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [Switch]$failureOnly, [Parameter (Mandatory = $false)] [ValidateRange(1, 100)] [int]$greenThreshold = 70, # Define default value for "Green" threshold [Parameter (Mandatory = $false)] [ValidateRange(1, 100)] [int]$redThreshold = 85 # Define default value for "Red" threshold ) Try { # Define object that will be returned and format input $customObject = New-Object System.Collections.ArrayList $formatOutput = ($dfOutput.ScriptOutput -split '\r?\n').Trim() -replace '(^\s+|\s+$)', '' -replace '\s+', ' ' # Set Alarms for each partition foreach ($partition in $formatOutput) { $usage = $partition.Split(" ")[4] # Make sure that only rows with calculated usage will be included if ( !$usage ) { continue } # Get the usage percentage as numeric value $usage = $usage.Substring(0, $usage.Length - 1) $usage = [int]$usage # Applying thresholds and creating collection from input Switch ($usage) { { $_ -le $greenThreshold } { $alert = 'GREEN' # Green if $usage is up to $greenThreshold $message = "Used space is less than $greenThreshold%." } { $_ -ge $redThreshold } { $alert = 'RED' # Red if $usage is equal or above $redThreshold $message = "Used space is above $redThreshold%. Please reclaim space on the partition." # In order to display usage, you could run as root in SDDC Manager 'du -Sh <mount-point> | sort -rh | head -10' " # As an alternative you could run PowerCLI commandlet: # 'Invoke-SddcCommand -server <SDDC_Manager_FQDN> -user <admin@local> -pass <admin@local_password> -GuestUser root -vmPass <SDDC_Manager_RootPassword> -command "du -Sh <mount-point> | sort -rh | head -10" ' } Default { $alert = 'YELLOW' # Yellow if above two are not matched $message = "Used space is between $greenThreshold% and $redThreshold%. Please consider reclaiming some space on the partition." } } # Skip population of object if "failureOnly" is selected and alert is "GREEN" if (($PsBoundParameters.ContainsKey("failureOnly")) -and ($alert -eq 'GREEN')) { continue } $userObject = New-Object -TypeName psobject $userObject | Add-Member -notepropertyname 'FQDN' -notepropertyvalue $systemFqdn $userObject | Add-Member -notepropertyname 'Filesystem' -notepropertyvalue $partition.Split(" ")[0] $userObject | Add-Member -notepropertyname 'Size' -notepropertyvalue $partition.Split(" ")[1] $userObject | Add-Member -notepropertyname 'Available' -notepropertyvalue $partition.Split(" ")[2] $userObject | Add-Member -notepropertyname 'Used %' -notepropertyvalue $partition.Split(" ")[4] $userObject | Add-Member -notepropertyname 'Mounted on' -notepropertyvalue $partition.Split(" ")[5] $userObject | Add-Member -notepropertyname 'Alert' -notepropertyvalue $alert $userObject | Add-Member -notepropertyname 'Message' -notepropertyvalue $message $customObject += $userObject # Creating collection to work with afterwords } $customObject | Sort-Object FQDN # Return $customObject in HTML or pain format } Catch { Debug-CatchWriter -object $_ } } Export-ModuleMember -Function Format-DfStorageHealth Function Convert-CssClass { Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [PSCustomObject]$htmlData ) # Function to replace Alerts with colour coded CSS Style $oldAlertOK = '<td>GREEN</td>' $newAlertOK = '<td class="alertOK">GREEN</td>' $oldAlertCritical = '<td>RED</td>' $newAlertCritical = '<td class="alertCritical">RED</td>' $oldAlertWarning = '<td>YELLOW</td>' $newAlertWarning = '<td class="alertWarning">YELLOW</td>' $oldAlertSkipped = '<td>SKIPPED/td>' $newAlertSkipped = '<td class="alertSkipped">SKIPPED</td>' $oldTable = '<table>' $newTable = '<table class="table">' $oldAddLine = ':-: ' $newNewLine = '<br/>' $htmlData = $htmlData -replace $oldAlertOK,$newAlertOK $htmlData = $htmlData -replace $oldAlertCritical,$newAlertCritical $htmlData = $htmlData -replace $oldAlertWarning,$newAlertWarning $htmlData = $htmlData -replace $oldTable,$newTable $htmlData = $htmlData -replace $oldAddLine,$newNewLine $htmlData } Export-ModuleMember -Function Convert-CssClass Function Format-StorageThreshold { <# .SYNOPSIS Calculate storage percentage. .DESCRIPTION The Format-StorageThreshold cmdlet converts the storage to a percentage and checks capacity . .EXAMPLE Format-StorageThreshold -size <size> -free <free> This example converts the storage to a percentage and checks capacity. .PARAMETER size The size of the datastore. .PARAMETER free The free space of the datastore. #> Param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$size, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [String]$free ) Try { # Define thresholds Green < Yellow < Red $greenThreshold = 80 $redThreshold = 90 # Calculate datastore usage and capacity [Int]$usage = [Math]::Round((($size - $free) / $size * 100)) # Applying thresholds and creating collection from input Switch ($usage) { { $_ -le $greenThreshold } { # Green if $usage is up to $greenThreshold $alert = 'GREEN' $message = "Used space is less than $greenThreshold%. " } { $_ -ge $redThreshold } { # Red if $usage is equal or above $redThreshold $alert = 'RED' $message = "Used space is above $redThreshold%. Please reclaim space on the volume." } Default { # Yellow if above two are not matched $alert = 'YELLOW' $message = "Used space is between $greenThreshold% and $redThreshold%. Please consider reclaiming some space on the volume." } } $thresholdObject = New-Object -TypeName psobject $thresholdObject | Add-Member -notepropertyname 'usage' -notepropertyvalue $usage $thresholdObject | Add-Member -notepropertyname 'alert' -notepropertyvalue $alert $thresholdObject | Add-Member -notepropertyname 'message' -notepropertyvalue $message $thresholdObject } Catch { Write-Error $_.Exception.Message } } ############################## End Supporting Functions ############################### ######################################################################################## |