Src/Public/Invoke-AsBuiltReport.VMware.vSphere.ps1
function Invoke-AsBuiltReport.VMware.vSphere { <# .SYNOPSIS PowerShell script to document the configuration of VMware vSphere infrastucture in Word/HTML/Text formats .DESCRIPTION Documents the configuration of VMware vSphere infrastucture in Word/HTML/Text formats using PScribo. .NOTES Version: 1.3.3 Author: Tim Carman Twitter: @tpcarman Github: tpcarman Credits: Iain Brighton (@iainbrighton) - PScribo module .LINK https://github.com/AsBuiltReport/AsBuiltReport.VMware.vSphere #> param ( [String[]] $Target, [PSCredential] $Credential ) # Check if the required version of VMware PowerCLI is installed Get-RequiredModule -Name 'VMware.PowerCLI' -Version '12.3' # Import Report Configuration $Report = $ReportConfig.Report $InfoLevel = $ReportConfig.InfoLevel $Options = $ReportConfig.Options # Used to set values to TitleCase where required $TextInfo = (Get-Culture).TextInfo #region Script Body #---------------------------------------------------------------------------------------------# # SCRIPT BODY # #---------------------------------------------------------------------------------------------# # Connect to vCenter Server using supplied credentials foreach ($VIServer in $Target) { try { Write-PScriboMessage "Connecting to vCenter Server '$VIServer'." $vCenter = Connect-VIServer $VIServer -Credential $Credential -ErrorAction Stop } catch { Write-Error $_ } #region Generate vSphere report if ($vCenter) { # Check logged in user has sufficient privileges to generate an As Built Report Write-PScriboMessage 'Checking vCenter user privileges.' Try { $UserPermission = Get-VIPermission | Where-Object {$_.Principal -eq $vCenter.User} $AuthMgr = Get-View $($vCenter.ExtensionData.Content.AuthorizationManager) $UserRole = $AuthMgr.RoleList | Where-Object {$_.Name -eq $($UserPermission.Role)} } Catch { Write-PScriboMessage 'Unable to obtain vCenter user privileges.' } # Create a lookup hashtable to quickly link VM MoRefs to Names # Exclude VMware Site Recovery Manager placeholder VMs Write-PScriboMessage 'Creating VM lookup hashtable.' $VMs = Get-VM -Server $vCenter | Where-Object { $_.ExtensionData.Config.ManagedBy.ExtensionKey -notlike 'com.vmware.vcDr*' } | Sort-Object Name $VMLookup = @{ } foreach ($VM in $VMs) { $VMLookup.($VM.Id) = $VM.Name } # Create a lookup hashtable to link Host MoRefs to Names # Exclude VMware HCX hosts and ESX/ESXi versions prior to vSphere 5.0 from VMHost lookup Write-PScriboMessage 'Creating VMHost lookup hashtable.' $VMHosts = Get-VMHost -Server $vCenter | Where-Object { $_.Model -notlike "*VMware Mobility Platform" -and $_.Version -gt 5 } | Sort-Object Name $VMHostLookup = @{ } foreach ($VMHost in $VMHosts) { $VMHostLookup.($VMHost.Id) = $VMHost.Name } # Create a lookup hashtable to link Datastore MoRefs to Names Write-PScriboMessage 'Creating Datastore lookup hashtable.' $Datastores = Get-Datastore -Server $vCenter | Where-Object { ($_.State -eq 'Available') -and ($_.CapacityGB -gt 0) } | Sort-Object Name $DatastoreLookup = @{ } foreach ($Datastore in $Datastores) { $DatastoreLookup.($Datastore.Id) = $Datastore.Name } # Create a lookup hashtable to link VDS Portgroups MoRefs to Names Write-PScriboMessage 'Creating VDPortGroup lookup hashtable.' $VDPortGroups = Get-VDPortgroup -Server $vCenter | Sort-Object Name $VDPortGroupLookup = @{ } foreach ($VDPortGroup in $VDPortGroups) { $VDPortGroupLookup.($VDPortGroup.Key) = $VDPortGroup.Name } # Create a lookup hashtable to link EVC Modes to Names Write-PScriboMessage 'Creating EVC lookup hashtable.' $SupportedEvcModes = $vCenter.ExtensionData.Capability.SupportedEVCMode $EvcModeLookup = @{ } foreach ($EvcMode in $SupportedEvcModes) { $EvcModeLookup.($EvcMode.Key) = $EvcMode.Label } $si = Get-View ServiceInstance -Server $vCenter $extMgr = Get-View -Id $si.Content.ExtensionManager -Server $vCenter #region VMware Update Manager Server Name Write-PScriboMessage 'Checking for VMware Update Manager Server.' $VumServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vcIntegrity' } | Select-Object @{ N = 'Name'; E = { ($_.Server | Where-Object { $_.Type -eq 'SOAP' -and $_.Company -eq 'VMware, Inc.' } | Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] } } #endregion VMware Update Manager Server Name #region VxRail Manager Server Name Write-PScriboMessage 'Checking for VxRail Manager Server.' $VxRailMgr = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vxrail' } | Select-Object @{ N = 'Name'; E = { ($_.Server | Where-Object { $_.Type -eq 'HTTPS' } | Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] } } #endregion VxRail Manager Server Name #region Site Recovery Manager Server Name Write-PScriboMessage 'Checking for VMware Site Recovery Manager Server.' $SrmServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.vcDr' } | Select-Object @{ N = 'Name'; E = { ($_.Server | Where-Object { $_.Company -eq 'VMware, Inc.' } | Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] } } #endregion Site Recovery Manager Server Name #region NSX-T Manager Server Name Write-PScriboMessage 'Checking for VMware NSX-T Manager Server.' $NsxtServer = $extMgr.ExtensionList | Where-Object { $_.Key -eq 'com.vmware.nsx.management.nsxt' } | Select-Object @{ N = 'Name'; E = { ($_.Server | Where-Object { ($_.Company -eq 'VMware') -and ($_.Type -eq 'VIP') } | Select-Object -ExpandProperty Url).Split('/')[2].Split(':')[0] } } #endregion NSX-T Manager Server Name #region Tag Information $TagAssignments = Get-TagAssignment -Server $vCenter $Tags = Get-Tag -Server $vCenter | Sort-Object Name, Category $TagCategories = Get-TagCategory -Server $vCenter | Sort-Object Name | Select-Object Name, Description, Cardinality -Unique #endregion Tag Information #region vCenter Advanced Settings Write-PScriboMessage "Collecting $vCenter advanced settings." $vCenterAdvSettings = Get-AdvancedSetting -Entity $vCenter $vCenterServerName = ($vCenterAdvSettings | Where-Object { $_.name -eq 'VirtualCenter.FQDN' }).Value $vCenterServerName = $vCenterServerName.ToString().ToLower() #endregion vCenter Advanced Settings #region vCenter Server Heading1 Section Section -Style Heading1 $vCenterServerName { #region vCenter Server Section Write-PScriboMessage "vCenter InfoLevel set at $($InfoLevel.vCenter)." if ($InfoLevel.vCenter -ge 1) { Section -Style Heading2 'vCenter Server' { Paragraph "The following sections detail the configuration of vCenter Server $vCenterServerName." BlankLine # Gather basic vCenter Server Information $vCenterServerInfo = [PSCustomObject]@{ 'vCenter Server' = $vCenterServerName 'IP Address' = ($vCenterAdvSettings | Where-Object { $_.name -like 'VirtualCenter.AutoManagedIPV4' }).Value 'Version' = $vCenter.Version 'Build' = $vCenter.Build 'OS Type' = $vCenter.ExtensionData.Content.About.OsType } #region vCenter Server Summary & Advanced Summary if ($InfoLevel.vCenter -le 2) { $TableParams = @{ Name = "vCenter Server Summary - $vCenterServerName" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterServerInfo | Table @TableParams } #endregion vCenter Server Summary & Advanced Summary #region vCenter Server Detailed Information if ($InfoLevel.vCenter -ge 3) { $MemberProps = @{ 'InputObject' = $vCenterServerInfo 'MemberType' = 'NoteProperty' } #region vCenter Server Detail if ($UserRole.Privilege -contains 'Global.Licenses') { $vCenterLicense = Get-License -vCenter $vCenter Add-Member @MemberProps -Name 'Product' -Value $vCenterLicense.Product Add-Member @MemberProps -Name 'License Key' -Value $vCenterLicense.LicenseKey Add-Member @MemberProps -Name 'License Expiration' -Value $vCenterLicense.Expiration } else { Write-PScriboMessage "Insufficient user privileges to report vCenter Server licensing. Please ensure the user account has the 'Global > Licenses' privilege assigned." } Add-Member @MemberProps -Name 'Instance ID' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'instance.id' }).Value if ($vCenter.Version -ge 6) { Add-Member @MemberProps -Name 'HTTP Port' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.rhttpproxy.httpport' }).Value Add-Member @MemberProps -Name 'HTTPS Port' -Value ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.rhttpproxy.httpsport' }).Value Add-Member @MemberProps -Name 'Platform Services Controller' -Value (($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.sso.admin.uri' }).Value -replace "^https://|/sso-adminserver/sdk/vsphere.local") } if ($VumServer.Name) { Add-Member @MemberProps -Name 'Update Manager Server' -Value $VumServer.Name } if ($SrmServer.Name) { Add-Member @MemberProps -Name 'Site Recovery Manager Server' -Value $SrmServer.Name } if ($NsxtServer.Name) { Add-Member @MemberProps -Name 'NSX-T Manager Server' -Value $NsxtServer.Name } if ($VxRailMgr.Name) { Add-Member @MemberProps -Name 'VxRail Manager Server' -Value $VxRailMgr.Name } if ($Healthcheck.vCenter.Licensing) { $vCenterServerInfo | Where-Object { $_.'Product' -like '*Evaluation*' } | Set-Style -Style Warning -Property 'Product' $vCenterServerInfo | Where-Object { $null -eq $_.'Product' } | Set-Style -Style Warning -Property 'Product' $vCenterServerInfo | Where-Object { $_.'License Key' -like '*-00000-00000' } | Set-Style -Style Warning -Property 'License Key' $vCenterServerInfo | Where-Object { $_.'License Expiration' -eq 'Expired' } | Set-Style -Style Critical -Property 'License Expiration' } $TableParams = @{ Name = "vCenter Server Configuration - $vCenterServerName" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterServerInfo | Table @TableParams #endregion vCenter Server Detail #region vCenter Server Database Settings Section -Style Heading3 'Database Settings' { $vCenterDbInfo = [PSCustomObject]@{ 'Database Type' = $TextInfo.ToTitleCase(($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.odbc.dbtype' }).Value) 'Data Source Name' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'config.vpxd.odbc.dsn' }).Value 'Maximum Database Connection' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'VirtualCenter.MaxDBConnection' }).Value } $TableParams = @{ Name = "Database Settings - $vCenterServerName" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterDbInfo | Table @TableParams } #endregion vCenter Server Database Settings #region vCenter Server Mail Settings Section -Style Heading3 'Mail Settings' { $vCenterMailInfo = [PSCustomObject]@{ 'SMTP Server' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.smtp.server' }).Value 'SMTP Port' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.smtp.port' }).Value 'Mail Sender' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'mail.sender' }).Value } if ($Healthcheck.vCenter.Mail) { $vCenterMailInfo | Where-Object { !($_.'SMTP Server') } | Set-Style -Style Critical -Property 'SMTP Server' $vCenterMailInfo | Where-Object { !($_.'SMTP Port') } | Set-Style -Style Critical -Property 'SMTP Port' $vCenterMailInfo | Where-Object { !($_.'Mail Sender') } | Set-Style -Style Critical -Property 'Mail Sender' } $TableParams = @{ Name = "Mail Settings - $vCenterServerName" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterMailInfo | Table @TableParams } #endregion vCenter Server Mail Settings #region vCenter Server Historical Statistics Section -Style Heading3 'Historical Statistics' { $vCenterHistoricalStats = Get-vCenterStats | Select-Object @{L = 'Interval Duration'; E = { $_.IntervalDuration } }, @{L = 'Interval Enabled'; E = { $_.IntervalEnabled } }, @{L = 'Save Duration'; E = { $_.SaveDuration } }, @{L = 'Statistics Level'; E = { $_.StatsLevel } } -Unique $TableParams = @{ Name = "Historical Statistics - $vCenterServerName" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterHistoricalStats | Table @TableParams } #endregion vCenter Server Historical Statistics #region vCenter Server Licensing if ($UserRole.Privilege -contains 'Global.Licenses') { Section -Style Heading3 'Licensing' { $Licenses = Get-License -Licenses | Select-Object Product, @{L = 'License Key'; E = { ($_.LicenseKey) } }, Total, Used, @{L = 'Available'; E = { ($_.total) - ($_.Used) } }, Expiration -Unique if ($Healthcheck.vCenter.Licensing) { $Licenses | Where-Object { $_.Product -eq 'Product Evaluation' } | Set-Style -Style Warning $Licenses | Where-Object { $_.Expiration -eq 'Expired' } | Set-Style -Style Critical } $TableParams = @{ Name = "Licensing - $vCenterServerName" ColumnWidths = 25, 25, 12, 12, 12, 14 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $Licenses | Sort-Object 'Product', 'License Key' | Table @TableParams } } else { Write-PScriboMessage "Insufficient user privileges to report vCenter Server licensing. Please ensure the user account has the 'Global > Licenses' privilege assigned." } #endregion vCenter Server Licensing #region vCenter Server Certificate if ($vCenter.Version -ge 6) { Section -Style Heading3 'Certificate' { $VcenterCertMgmt = [PSCustomObject]@{ 'Country' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.country' }).Value 'Email' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.email' }).Value 'Locality' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.localityName' }).Value 'State' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.state' }).Value 'Organization' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.organizationName' }).Value 'Organization Unit' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.cn.organizationalUnitName' }).Value 'Validity' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.daysValid'}).Value / 365) years" 'Mode' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.mode' }).Value 'Soft Threshold' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.softThreshold'}).Value) days" 'Hard Threshold' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.hardThreshold'}).Value) days" 'Minutes Before' = ($vCenterAdvSettings | Where-Object { $_.name -eq 'vpxd.certmgmt.certs.minutesBefore' }).Value 'Poll Interval' = "$(($vCenterAdvSettings | Where-Object {$_.name -eq 'vpxd.certmgmt.certs.pollIntervalDays'}).Value) days" } $TableParams = @{ Name = "Certificate - $vCenterServerName" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VcenterCertMgmt | Table @TableParams } } #endregion vCenter Server Certificate #region vCenter Server Roles Section -Style Heading3 'Roles' { $VIRoles = Get-VIRole -Server $vCenter | Where-Object {$null -ne $_.PrivilegeList} | Sort-Object Name $VIRoleInfo = foreach ($VIRole in $VIRoles) { [PSCustomObject]@{ 'Role' = $VIRole.Name 'System Role' = Switch ($VIRole.IsSystem) { $true { 'Yes' } $false { 'No' } } 'Privilege List' = ($VIRole.PrivilegeList).Replace("."," > ") | Select-Object -Unique } } if ($InfoLevel.vCenter -ge 4) { $VIRoleInfo | ForEach-Object { Section -Style Heading4 $($_.Role) { $TableParams = @{ Name = "Role $($_.Role) - $vCenterServerName" ColumnWidths = 35, 15, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $_ | Table @TableParams } } } else { $TableParams = @{ Name = "Roles - $vCenterServerName" Columns = 'Role','System Role' ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VIRoleInfo | Table @TableParams } } #endregion vCenter Server Roles #region vCenter Server Tags if ($Tags) { Section -Style Heading3 'Tags' { $TagInfo = foreach ($Tag in $Tags) { [PSCustomObject] @{ 'Tag' = $Tag.Name 'Description' = Switch ($Tag.Description) { '' { 'None' } default { $Tag.Description } } 'Category' = Switch ($Tag.Category) { '' { 'None' } default { $Tag.Category } } } } $TableParams = @{ Name = "Tags - $vCenterServerName" ColumnWidths = 30, 40, 30 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $TagInfo | Table @TableParams } } #endregion vCenter Server Tags #region vCenter Server Tag Categories if ($TagCategories) { Section -Style Heading3 'Tag Categories' { $TagCategoryInfo = foreach ($TagCategory in $TagCategories) { [PSCustomObject] @{ 'Category' = $TagCategory.Name 'Description' = Switch ($TagCategory.Description) { '' { 'None' } default { $TagCategory.Description } } 'Cardinality' = Switch ($TagCategory.Cardinality) { '' { 'None' } default { $TagCategory.Cardinality } } } } $TableParams = @{ Name = "Tag Categories - $vCenterServerName" ColumnWidths = 30, 40, 30 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $TagCategoryInfo | Table @TableParams } } #endregion vCenter Server Tag Categories #region vCenter Server Tag Assignments if ($TagAssignments) { Section -Style Heading3 'Tag Assignments' { $TagAssignmentInfo = foreach ($TagAssignment in $TagAssignments) { [PSCustomObject]@{ 'Entity' = $TagAssignment.Entity.Name 'Tag' = $TagAssignment.Tag.Name 'Category' = $TagAssignment.Tag.Category } } $TableParams = @{ Name = "Tag Assignments - $vCenterServerName" ColumnWidths = 30, 40, 30 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $TagAssignmentInfo | Sort-Object Entity | Table @TableParams } } #endregion vCenter Server Tag Assignments #region VM Storage Policies if ($UserRole.Privilege -contains 'StorageProfile.View') { $SpbmStoragePolicies = Get-SpbmStoragePolicy | Sort-Object Name if ($SpbmStoragePolicies) { Section -Style Heading3 'VM Storage Policies' { $VmStoragePolicies = foreach ($SpbmStoragePolicy in $SpbmStoragePolicies) { [PSCustomObject]@{ 'VM Storage Policy' = $SpbmStoragePolicy.Name 'Description' = $SpbmStoragePolicy.Description } } $TableParams = @{ Name = "VM Storage Policies - $vCenterServerName" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VmStoragePolicies | Table @TableParams } } } else { Write-PScriboMessage "Insufficient user privileges to report VM storage policies. Please ensure the user account has the 'Storage Profile > View' privilege assigned." } #endregion VM Storage Policies } #endregion vCenter Server Detailed Information #region vCenter Server Advanced Detail Information if ($InfoLevel.vCenter -ge 4) { #region vCenter Alarms Section -Style Heading3 'Alarms' { $Alarms = Get-AlarmDefinition -PipelineVariable alarm | ForEach-Object -Process { Get-AlarmAction -AlarmDefinition $_ -PipelineVariable action | ForEach-Object -Process { Get-AlarmActionTrigger -AlarmAction $_ | Select-Object @{N = 'Alarm'; E = { $alarm.Name } }, @{N = 'Description'; E = { $alarm.Description } }, @{N = 'Enabled'; E = { Switch ($alarm.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } } }, @{N = 'Entity'; E = { $alarm.Entity.Type } }, @{N = 'Trigger'; E = { "{0}:{1}->{2} (Repeat={3})" -f $action.ActionType, $_.StartStatus, $_.EndStatus, $_.Repeat } }, @{N = 'Trigger Info'; E = { Switch ($action.ActionType) { 'SendEmail' { "To: $($action.To -join ', ') ` Cc: $($action.Cc -join ', ') ` Subject: $($action.Subject) ` Body: $($action.Body)" } 'ExecuteScript' { "$($action.ScriptFilePath)" } default { '--' } } } } } } $Alarms = $Alarms | Sort-Object 'Alarm', 'Trigger' if ($Healthcheck.vCenter.Alarms) { $Alarms | Where-Object { $_.'Enabled' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Enabled' } if ($InfoLevel.vCenter -ge 5) { foreach ($Alarm in $Alarms) { Section -Style Heading4 $($Alarm.Alarm) { $TableParams = @{ Name = "$($Alarm.Alarm) - $vCenterServerName" List = $true ColumnWidths = 25, 75 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $Alarm | Table @TableParams } } } else { $TableParams = @{ Name = "Alarms - $vCenterServerName" Columns = 'Alarm', 'Description', 'Enabled', 'Entity', 'Trigger' ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $Alarms | Table @TableParams } } #endregion vCenter Alarms } #endregion vCenter Server Advanced Detail Information #region vCenter Server Comprehensive Information if ($InfoLevel.vCenter -ge 5) { #region vCenter Advanced System Settings Section -Style Heading3 'Advanced System Settings' { $TableParams = @{ Name = "vCenter Advanced System Settings - $vCenterServerName" Columns = 'Name', 'Value' ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vCenterAdvSettings | Sort-Object Name | Table @TableParams } #endregion vCenter Advanced System Settings } #endregion vCenter Server Comprehensive Information } } #endregion vCenter Server Section #region Clusters Write-PScriboMessage "Cluster InfoLevel set at $($InfoLevel.Cluster)." if ($InfoLevel.Cluster -ge 1) { $Clusters = Get-Cluster -Server $vCenter | Sort-Object Name if ($Clusters) { #region Cluster Section Section -Style Heading2 'Clusters' { Paragraph "The following sections detail the configuration of vSphere HA/DRS clusters managed by vCenter Server $vCenterServerName." #region Cluster Advanced Summary if ($InfoLevel.Cluster -le 2) { BlankLine $ClusterInfo = foreach ($Cluster in $Clusters) { [PSCustomObject]@{ 'Cluster' = $Cluster.Name 'Datacenter' = $Cluster | Get-Datacenter '# of Hosts' = $Cluster.ExtensionData.Host.Count '# of VMs' = $Cluster.ExtensionData.VM.Count 'vSphere HA' = Switch ($Cluster.HAEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'vSphere DRS' = Switch ($Cluster.DrsEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Virtual SAN' = Switch ($Cluster.VsanEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'EVC Mode' = Switch ($Cluster.EVCMode) { $null { 'Disabled' } default { $EvcModeLookup."$($Cluster.EVCMode)" } } 'VM Swap File Policy' = Switch ($Cluster.VMSwapfilePolicy) { 'WithVM' { 'With VM' } 'InHostDatastore' { 'In Host Datastore' } default { $Cluster.VMSwapfilePolicy } } } } if ($Healthcheck.Cluster.HAEnabled) { $ClusterInfo | Where-Object { $_.'vSphere HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere HA' } if ($Healthcheck.Cluster.DrsEnabled) { $ClusterInfo | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS' } if ($Healthcheck.Cluster.VsanEnabled) { $ClusterInfo | Where-Object { $_.'Virtual SAN' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Virtual SAN' } if ($Healthcheck.Cluster.EvcEnabled) { $ClusterInfo | Where-Object { $_.'EVC Mode' -eq 'Disabled' } | Set-Style -Style Warning -Property 'EVC Mode' } $TableParams = @{ Name = "Cluster Summary - $vCenterServerName" ColumnWidths = 15, 15, 7, 7, 11, 11, 11, 15, 8 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterInfo | Table @TableParams } #endregion Cluster Advanced Summary #region Cluster Detailed Information # TODO: Test Tags if ($InfoLevel.Cluster -ge 3) { foreach ($Cluster in $Clusters) { $ClusterDasConfig = $Cluster.ExtensionData.Configuration.DasConfig $ClusterDrsConfig = $Cluster.ExtensionData.Configuration.DrsConfig $ClusterConfigEx = $Cluster.ExtensionData.ConfigurationEx #region Cluster Section Section -Style Heading3 $Cluster { Paragraph "The following table details the configuration for cluster $Cluster." BlankLine #region Cluster Configuration $ClusterDetail = [PSCustomObject]@{ 'Cluster' = $Cluster.Name 'ID' = $Cluster.Id 'Datacenter' = $Cluster | Get-Datacenter 'Number of Hosts' = $Cluster.ExtensionData.Host.Count 'Number of VMs' = ($Cluster | Get-VM).Count 'vSphere HA' = Switch ($Cluster.HAEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'vSphere DRS' = Switch ($Cluster.DrsEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Virtual SAN' = Switch ($Cluster.VsanEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'EVC Mode' = Switch ($Cluster.EVCMode) { $null { 'Disabled' } default { $EvcModeLookup."$($Cluster.EVCMode)" } } 'VM Swap File Policy' = Switch ($Cluster.VMSwapfilePolicy) { 'WithVM' { 'Virtual machine directory' } 'InHostDatastore' { 'Datastore specified by host' } default { $Cluster.VMSwapfilePolicy } } } $MemberProps = @{ 'InputObject' = $ClusterDetail 'MemberType' = 'NoteProperty' } <# if ($TagAssignments | Where-Object {$_.entity -eq $Cluster}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $Cluster}).Tag -join ',') } #> if ($Healthcheck.Cluster.HAEnabled) { $ClusterDetail | Where-Object { $_.'vSphere HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere HA' } if ($Healthcheck.Cluster.DrsEnabled) { $ClusterDetail | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS' } if ($Healthcheck.Cluster.VsanEnabled) { $ClusterDetail | Where-Object { $_.'Virtual SAN' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Virtual SAN' } if ($Healthcheck.Cluster.EvcEnabled) { $ClusterDetail | Where-Object { $_.'EVC Mode' -eq 'Disabled' } | Set-Style -Style Warning -Property 'EVC Mode' } #region Cluster Advanced Detailed Information if ($InfoLevel.Cluster -ge 4) { $ClusterDetail | ForEach-Object { $ClusterHosts = $Cluster | Get-VMHost | Sort-Object Name Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Hosts' -Value ($ClusterHosts.Name -join ', ') $ClusterVMs = $Cluster | Get-VM | Sort-Object Name Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Virtual Machines' -Value ($ClusterVMs.Name -join ', ') } } #endregion Cluster Advanced Detailed Information $TableParams = @{ Name = "Cluster Configuration - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterDetail | Table @TableParams #endregion Cluster Configuration #region vSphere HA Cluster Configuration if ($Cluster.HAEnabled) { Section -Style Heading4 'vSphere HA Configuration' { Paragraph "The following section details the vSphere HA configuration for $Cluster cluster." #region vSphere HA Cluster Failures and Responses Section -Style Heading5 'Failures and Responses' { $HAClusterResponses = [PSCustomObject]@{ 'Host Monitoring' = $TextInfo.ToTitleCase($ClusterDasConfig.HostMonitoring) } if ($ClusterDasConfig.HostMonitoring -eq 'Enabled') { $MemberProps = @{ 'InputObject' = $HAClusterResponses 'MemberType' = 'NoteProperty' } if ($ClusterDasConfig.DefaultVmSettings.RestartPriority -eq 'Disabled') { Add-Member @MemberProps -Name 'Host Failure Response' -Value 'Disabled' } else { Add-Member @MemberProps -Name 'Host Failure Response' -Value 'Restart VMs' Switch ($Cluster.HAIsolationResponse) { 'DoNothing' { Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Disabled' } 'Shutdown' { Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Shutdown and restart VMs' } 'PowerOff' { Add-Member @MemberProps -Name 'Host Isolation Response' -Value 'Power off and restart VMs' } } Add-Member @MemberProps -Name 'VM Restart Priority' -Value $Cluster.HARestartPriority Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL) { 'disabled' { Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Disabled' } 'warning' { Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Issue events' } 'restartAggressive' { Add-Member @MemberProps -Name 'Datastore with Permanent Device Loss' -Value 'Power off and restart VMs' } } Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD) { 'disabled' { Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Disabled' } 'warning' { Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Issue events' } 'restartConservative' { Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Power off and restart VMs (conservative)' } 'restartAggressive' { Add-Member @MemberProps -Name 'Datastore with All Paths Down' -Value 'Power off and restart VMs (aggressive)' } } Switch ($ClusterDasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared) { 'none' { Add-Member @MemberProps -Name 'APD recovery after APD timeout' -Value 'Disabled' } 'reset' { Add-Member @MemberProps -Name 'APD recovery after APD timeout' -Value 'Reset VMs' } } } Switch ($ClusterDasConfig.VmMonitoring) { 'vmMonitoringDisabled' { Add-Member @MemberProps -Name 'VM Monitoring' -Value 'Disabled' } 'vmMonitoringOnly' { Add-Member @MemberProps -Name 'VM Monitoring' -Value 'VM monitoring only' } 'vmAndAppMonitoring' { Add-Member @MemberProps -Name 'VM Monitoring' -Value 'VM and application monitoring' } } } if ($Healthcheck.Cluster.HostFailureResponse) { $HAClusterResponses | Where-Object { $_.'Host Failure Response' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Host Failure Response' } if ($Healthcheck.Cluster.HostMonitoring) { $HAClusterResponses | Where-Object { $_.'Host Monitoring' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Host Monitoring' } if ($Healthcheck.Cluster.DatastoreOnPDL) { $HAClusterResponses | Where-Object { $_.'Datastore with Permanent Device Loss' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Datastore with Permanent Device Loss' } if ($Healthcheck.Cluster.DatastoreOnAPD) { $HAClusterResponses | Where-Object { $_.'Datastore with All Paths Down' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Datastore with All Paths Down' } if ($Healthcheck.Cluster.APDTimeout) { $HAClusterResponses | Where-Object { $_.'APD recovery after APD timeout' -eq 'Disabled' } | Set-Style -Style Warning -Property 'APD recovery after APD timeout' } if ($Healthcheck.Cluster.vmMonitoring) { $HAClusterResponses | Where-Object { $_.'VM Monitoring' -eq 'Disabled' } | Set-Style -Style Warning -Property 'VM Monitoring' } $TableParams = @{ Name = "vSphere HA Failures and Responses - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $HAClusterResponses | Table @TableParams } #endregion vSphere HA Cluster Failures and Responses #region vSphere HA Cluster Admission Control Section -Style Heading5 'Admission Control' { $HAAdmissionControl = [PSCustomObject]@{ 'Admission Control' = Switch ($Cluster.HAAdmissionControlEnabled) { $true { 'Enabled' } $false { 'Disabled' } } } if ($Cluster.HAAdmissionControlEnabled) { $MemberProps = @{ 'InputObject' = $HAAdmissionControl 'MemberType' = 'NoteProperty' } Add-Member @MemberProps -Name 'Host Failures Cluster Tolerates' -Value $Cluster.HAFailoverLevel Switch ($ClusterDasConfig.AdmissionControlPolicy.GetType().Name) { 'ClusterFailoverHostAdmissionControlPolicy' { Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Dedicated failover hosts' } 'ClusterFailoverResourcesAdmissionControlPolicy' { Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Cluster resource percentage' } 'ClusterFailoverLevelAdmissionControlPolicy' { Add-Member @MemberProps -Name 'Host Failover Capacity Policy' -Value 'Slot policy' } } Switch ($ClusterDasConfig.AdmissionControlPolicy.AutoComputePercentages) { $true { Add-Member @MemberProps -Name 'Override Calculated Failover Capacity' -Value 'No' } $false { Add-Member @MemberProps -Name 'Override Calculated Failover Capacity' -Value 'Yes' Add-Member @MemberProps -Name 'CPU %' -Value $ClusterDasConfig.AdmissionControlPolicy.CpuFailoverResourcesPercent Add-Member @MemberProps -Name 'Memory %' -Value $ClusterDasConfig.AdmissionControlPolicy.MemoryFailoverResourcesPercent } } if ($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy) { Add-Member @MemberProps -Name 'Slot Policy' -Value 'Fixed slot size' Add-Member @MemberProps -Name 'CPU Slot Size' -Value "$($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy.Cpu) MHz" Add-Member @MemberProps -Name 'Memory Slot Size' -Value "$($ClusterDasConfig.AdmissionControlPolicy.SlotPolicy.Memory) MB" } else { Add-Member @MemberProps -Name 'Slot Policy' -Value 'Cover all powered-on virtual machines' } if ($ClusterDasConfig.AdmissionControlPolicy.ResourceReductionToToleratePercent) { Add-Member @MemberProps -Name 'Performance Degradation VMs Tolerate' -Value "$($ClusterDasConfig.AdmissionControlPolicy.ResourceReductionToToleratePercent)%" } } if ($Healthcheck.Cluster.HAAdmissionControl) { $HAAdmissionControl | Where-Object { $_.'Admission Control' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Admission Control' } $TableParams = @{ Name = "vSphere HA Admission Control - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $HAAdmissionControl | Table @TableParams } #endregion vSphere HA Cluster Admission Control #region vSphere HA Cluster Heartbeat Datastores Section -Style Heading5 'Heartbeat Datastores' { $HeartbeatDatastores = [PSCustomObject]@{ 'Heartbeat Selection Policy' = Switch ($ClusterDasConfig.HBDatastoreCandidatePolicy) { 'allFeasibleDsWithUserPreference' { 'Use datastores from the specified list and complement automatically if needed' } 'allFeasibleDs' { 'Automatically select datastores accessible from the host' } 'userSelectedDs' { 'Use datastores only from the specified list' } default { $ClusterDasConfig.HBDatastoreCandidatePolicy } } 'Heartbeat Datastores' = try { (((Get-View -Id $ClusterDasConfig.HeartbeatDatastore -Property Name).Name | Sort-Object) -join ', ') } catch { 'None specified' } } $TableParams = @{ Name = "vSphere HA Heartbeat Datastores - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $HeartbeatDatastores | Table @TableParams } #endregion vSphere HA Cluster Heartbeat Datastores #region vSphere HA Cluster Advanced Options $HAAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterHA' } if ($HAAdvancedSettings) { Section -Style Heading5 'vSphere HA Advanced Options' { $HAAdvancedOptions = @() foreach ($HAAdvancedSetting in $HAAdvancedSettings) { $HAAdvancedOption = [PSCustomObject]@{ 'Option' = $HAAdvancedSetting.Name 'Value' = $HAAdvancedSetting.Value } $HAAdvancedOptions += $HAAdvancedOption } $TableParams = @{ Name = "vSphere HA Advanced Options - $Cluster" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $HAAdvancedOptions | Sort-Object Option | Table @TableParams } } #endregion vSphere HA Cluster Advanced Options } } #endregion vSphere HA Cluster Configuration #region Proactive HA Configuration # TODO: Proactive HA Providers # Proactive HA is only available in vSphere 6.5 and above if ($ClusterConfigEx.InfraUpdateHaConfig.Enabled -and $vCenter.Version -ge 6.5) { Section -Style Heading4 'Proactive HA' { Paragraph "The following section details the Proactive HA configuration for $Cluster cluster." #region Proactive HA Failures and Responses Section Section -Style Heading5 'Failures and Responses' { $ProactiveHa = [PSCustomObject]@{ 'Proactive HA' = Switch ($ClusterConfigEx.InfraUpdateHaConfig.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } } if ($ClusterConfigEx.InfraUpdateHaConfig.Enabled) { $ProactiveHaModerateRemediation = Switch ($ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation) { 'MaintenanceMode' { 'Maintenance Mode' } 'QuarantineMode' { 'Quarantine Mode' } default { $ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation } } $ProactiveHaSevereRemediation = Switch ($ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation) { 'MaintenanceMode' { 'Maintenance Mode' } 'QuarantineMode' { 'Quarantine Mode' } default { $ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation } } $MemberProps = @{ 'InputObject' = $ProactiveHa 'MemberType' = 'NoteProperty' } Add-Member @MemberProps -Name 'Automation Level' -Value $ClusterConfigEx.InfraUpdateHaConfig.Behavior if ($ClusterConfigEx.InfraUpdateHaConfig.ModerateRemediation -eq $ClusterConfigEx.InfraUpdateHaConfig.SevereRemediation) { Add-Member @MemberProps -Name 'Remediation' -Value $ProactiveHaModerateRemediation } else { Add-Member @MemberProps -Name 'Remediation' -Value 'Mixed Mode' Add-Member @MemberProps -Name 'Moderate Remediation' -Value $ProactiveHaModerateRemediation Add-Member @MemberProps -Name 'Severe Remediation' -Value $ProactiveHaSevereRemediation } } if ($Healthcheck.Cluster.ProactiveHA) { $ProactiveHa | Where-Object { $_.'Proactive HA' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Proactive HA' } $TableParams = @{ Name = "Proactive HA - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ProactiveHa | Table @TableParams } #endregion Proactive HA Failures and Responses Section } } #endregion Proactive HA Configuration #region vSphere DRS Cluster Configuration if ($Cluster.DrsEnabled) { Section -Style Heading4 'vSphere DRS Configuration' { Paragraph ("The following table details the vSphere DRS configuration " + "for cluster $Cluster.") BlankLine #region vSphere DRS Cluster Specifications $DrsCluster = [PSCustomObject]@{ 'vSphere DRS' = Switch ($Cluster.DrsEnabled) { $true { 'Enabled' } $false { 'Disabled' } } } $MemberProps = @{ 'InputObject' = $DrsCluster 'MemberType' = 'NoteProperty' } Switch ($Cluster.DrsAutomationLevel) { 'Manual' { Add-Member @MemberProps -Name 'Automation Level' -Value 'Manual' } 'PartiallyAutomated' { Add-Member @MemberProps -Name 'Automation Level' -Value 'Partially Automated' } 'FullyAutomated' { Add-Member @MemberProps -Name 'Automation Level' -Value 'Fully Automated' } } Add-Member @MemberProps -Name 'Migration Threshold' -Value $ClusterDrsConfig.VmotionRate Switch ($ClusterConfigEx.ProactiveDrsConfig.Enabled) { $false { Add-Member @MemberProps -Name 'Predictive DRS' -Value 'Disabled' } $true { Add-Member @MemberProps -Name 'Predictive DRS' -Value 'Enabled' } } Switch ($ClusterDrsConfig.EnableVmBehaviorOverrides) { $true { Add-Member @MemberProps -Name 'Virtual Machine Automation' -Value 'Enabled' } $false { Add-Member @MemberProps -Name 'Virtual Machine Automation' -Value 'Disabled' } } if ($Healthcheck.Cluster.DrsEnabled) { $DrsCluster | Where-Object { $_.'vSphere DRS' -eq 'Disabled' } | Set-Style -Style Warning -Property 'vSphere DRS' } if ($Healthcheck.Cluster.DrsAutomationLevelFullyAuto) { $DrsCluster | Where-Object { $_.'Automation Level' -ne 'Fully Automated' } | Set-Style -Style Warning -Property 'Automation Level' } $TableParams = @{ Name = "vSphere DRS Configuration - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsCluster | Table @TableParams #endregion vSphere DRS Cluster Specfications #region DRS Cluster Additional Options $DrsAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterDRS' } if ($DrsAdvancedSettings) { Section -Style Heading5 'Additional Options' { $DrsAdditionalOptions = [PSCustomObject] @{ 'VM Distribution' = Switch (($DrsAdvancedSettings | Where-Object { $_.name -eq 'TryBalanceVmsPerHost' }).Value) { '1' { 'Enabled' } $null { 'Disabled' } } 'Memory Metric for Load Balancing' = Switch (($DrsAdvancedSettings | Where-Object { $_.name -eq 'PercentIdleMBInMemDemand' }).Value) { '100' { 'Enabled' } $null { 'Disabled' } } 'CPU Over-Commitment' = if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerCore' }).Value) { 'Enabled' } else { 'Disabled' } } $MemberProps = @{ 'InputObject' = $DrsAdditionalOptions 'MemberType' = 'NoteProperty' } if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerCore' }).Value) { Add-Member @MemberProps -Name 'Over-Commitment Ratio' -Value "$(($DrsAdvancedSettings | Where-Object {$_.name -eq 'MaxVcpusPerCore'}).Value):1 (vCPU:pCPU)" } if (($DrsAdvancedSettings | Where-Object { $_.name -eq 'MaxVcpusPerClusterPct' }).Value) { Add-Member @MemberProps -Name 'Over-Commitment Ratio (% of cluster capacity)' -Value "$(($DrsAdvancedSettings | Where-Object {$_.name -eq 'MaxVcpusPerClusterPct'}).Value) %" } $TableParams = @{ Name = "DRS Additional Options - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsAdditionalOptions | Table @TableParams } } #endregion DRS Cluster Additional Options #region vSphere DPM Configuration if ($ClusterConfigEx.DpmConfigInfo.Enabled) { Section -Style Heading5 'Power Management' { $DpmConfig = [PSCustomObject]@{ 'DPM' = Switch ($ClusterConfigEx.DpmConfigInfo.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } } $MemberProps = @{ 'InputObject' = $DpmConfig 'MemberType' = 'NoteProperty' } Switch ($ClusterConfigEx.DpmConfigInfo.DefaultDpmBehavior) { 'manual' { Add-Member @MemberProps -Name 'Automation Level' -Value 'Manual' } 'automated' { Add-Member @MemberProps -Name 'Automation Level' -Value 'Automated' } } if ($ClusterConfigEx.DpmConfigInfo.DefaultDpmBehavior -eq 'automated') { Add-Member @MemberProps -Name 'DPM Threshold' -Value $ClusterConfigEx.DpmConfigInfo.HostPowerActionRate } $TableParams = @{ Name = "vSphere DPM - $Cluster" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DpmConfig | Table @TableParams } } #endregion vSphere DPM Configuration #region vSphere DRS Cluster Advanced Options $DrsAdvancedSettings = $Cluster | Get-AdvancedSetting | Where-Object { $_.Type -eq 'ClusterDRS' } if ($DrsAdvancedSettings) { Section -Style Heading5 'Advanced Options' { $DrsAdvancedOptions = @() foreach ($DrsAdvancedSetting in $DrsAdvancedSettings) { $DrsAdvancedOption = [PSCustomObject]@{ 'Option' = $DrsAdvancedSetting.Name 'Value' = $DrsAdvancedSetting.Value } $DrsAdvancedOptions += $DrsAdvancedOption } $TableParams = @{ Name = "vSphere DRS Advanced Options - $Cluster" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsAdvancedOptions | Sort-Object Option | Table @TableParams } } #endregion vSphere DRS Cluster Advanced Options #region vSphere DRS Cluster Group $DrsClusterGroups = $Cluster | Get-DrsClusterGroup if ($DrsClusterGroups) { #region vSphere DRS Cluster Group Section Section -Style Heading5 'DRS Cluster Groups' { $DrsGroups = foreach ($DrsClusterGroup in $DrsClusterGroups) { [PSCustomObject]@{ 'DRS Cluster Group' = $DrsClusterGroup.Name 'Type' = Switch ($DrsClusterGroup.GroupType) { 'VMGroup' { 'VM Group' } 'VMHostGroup' { 'Host Group' } default { $DrsClusterGroup.GroupType } } 'Members' = Switch (($DrsClusterGroup.Member).Count -gt 0) { $true { ($DrsClusterGroup.Member | Sort-Object) -join ', ' } $false { "None" } } } } $TableParams = @{ Name = "DRS Cluster Groups - $Cluster" ColumnWidths = 42, 16, 42 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsGroups | Sort-Object 'DRS Cluster Group', 'Type' | Table @TableParams } #endregion vSphere DRS Cluster Group Section #region vSphere DRS Cluster VM/Host Rules $DrsVMHostRules = $Cluster | Get-DrsVMHostRule if ($DrsVMHostRules) { Section -Style Heading5 'DRS VM/Host Rules' { $DrsVMHostRuleDetail = foreach ($DrsVMHostRule in $DrsVMHostRules) { [PSCustomObject]@{ 'DRS VM/Host Rule' = $DrsVMHostRule.Name 'Type' = Switch ($DrsVMHostRule.Type) { 'MustRunOn' { 'Must run on hosts in group' } 'ShouldRunOn' { 'Should run on hosts in group' } 'MustNotRunOn' { 'Must not run on hosts in group' } 'ShouldNotRunOn' { 'Should not run on hosts in group' } default { $DrsVMHostRule.Type } } 'Enabled' = Switch ($DrsVMHostRule.Enabled) { $true { 'Yes' } $False { 'No' } } 'VM Group' = $DrsVMHostRule.VMGroup 'Host Group' = $DrsVMHostRule.VMHostGroup } } if ($Healthcheck.Cluster.DrsVMHostRules) { $DrsVMHostRuleDetail | Where-Object { $_.Enabled -eq 'No' } | Set-Style -Style Warning -Property 'Enabled' } $TableParams = @{ Name = "DRS VM/Host Rules - $Cluster" ColumnWidths = 22, 22, 12, 22, 22 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsVMHostRuleDetail | Sort-Object 'DRS VM/Host Rule' | Table @TableParams } } #endregion vSphere DRS Cluster VM/Host Rules #region vSphere DRS Cluster Rules $DrsRules = $Cluster | Get-DrsRule if ($DrsRules) { #region vSphere DRS Cluster Rules Section Section -Style Heading5 'DRS Rules' { $DrsRuleDetail = foreach ($DrsRule in $DrsRules) { [PSCustomObject]@{ 'DRS Rule' = $DrsRule.Name 'Type' = Switch ($DrsRule.Type) { 'VMAffinity' { 'Keep Vitrual Machines Together' } 'VMAntiAffinity' { 'Separate Virtual Machines' } } 'Enabled' = Switch ($DrsRule.Enabled) { $true { 'Yes' } $False { 'No' } } 'Mandatory' = $DrsRule.Mandatory 'Virtual Machines' = ($DrsRule.VMIds | ForEach-Object { (Get-View -id $_).name }) -join ', ' } if ($Healthcheck.Cluster.DrsRules) { $DrsRuleDetail | Where-Object { $_.Enabled -eq 'No' } | Set-Style -Style Warning -Property 'Enabled' } } $TableParams = @{ Name = "DRS Rules - $Cluster" ColumnWidths = 26, 25, 12, 12, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsRuleDetail | Sort-Object Type | Table @TableParams } #endregion vSphere DRS Cluster Rules Section } #endregion vSphere DRS Cluster Rules } #endregion vSphere DRS Cluster Group #region Cluster VM Overrides $DrsVmOverrides = $Cluster.ExtensionData.Configuration.DrsVmConfig $DasVmOverrides = $Cluster.ExtensionData.Configuration.DasVmConfig if ($DrsVmOverrides -or $DasVmOverrides) { #region VM Overrides Section Section -Style Heading4 'VM Overrides' { #region vSphere DRS VM Overrides if ($DrsVmOverrides) { Section -Style Heading5 'vSphere DRS' { $DrsVmOverrideDetails = foreach ($DrsVmOverride in $DrsVmOverrides) { [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($DrsVmOverride.Key.Type)-$($DrsVmOverride.Key.Value)" 'vSphere DRS Automation Level' = if ($DrsVmOverride.Enabled -eq $false) { 'Disabled' } else { Switch ($DrsVmOverride.Behavior) { 'manual' { 'Manual' } 'partiallyAutomated' { 'Partially Automated' } 'fullyAutomated' { 'Fully Automated' } default { $DrsVmOverride.Behavior } } } } } $TableParams = @{ Name = "DRS VM Overrides - $Cluster" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DrsVmOverrideDetails | Sort-Object 'Virtual Machine' | Table @TableParams } } #endregion vSphere DRS VM Overrides #region vSphere HA VM Overrides if ($DasVmOverrides) { Section -Style Heading5 'vSphere HA' { $DasVmOverrideDetails = foreach ($DasVmOverride in $DasVmOverrides) { [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)" 'VM Restart Priority' = Switch ($DasVmOverride.DasSettings.RestartPriority) { $null { '--' } 'lowest' { 'Lowest' } 'low' { 'Low' } 'medium' { 'Medium' } 'high' { 'High' } 'highest' { 'Highest' } 'disabled' { 'Disabled' } 'clusterRestartPriority' { 'Cluster default' } } 'VM Dependency Restart Condition Timeout' = Switch ($DasVmOverride.DasSettings.RestartPriorityTimeout) { $null { '--' } '-1' { 'Disabled' } default { "$($DasVmOverride.DasSettings.RestartPriorityTimeout) seconds" } } 'Host Isolation Response' = Switch ($DasVmOverride.DasSettings.IsolationResponse) { $null { '--' } 'none' { 'Disabled' } 'powerOff' { 'Power off and restart VMs' } 'shutdown' { 'Shutdown and restart VMs' } 'clusterIsolationResponse' { 'Cluster default' } } } } $TableParams = @{ Name = "HA VM Overrides - $Cluster" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DasVmOverrideDetails | Sort-Object 'Virtual Machine' | Table @TableParams #region PDL/APD Protection Settings Section Section -Style Heading5 'PDL/APD Protection Settings' { $DasVmOverridePdlApd = foreach ($DasVmOverride in $DasVmOverrides) { $DasVmComponentProtection = $DasVmOverride.DasSettings.VmComponentProtectionSettings [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)" 'PDL Failure Response' = Switch ($DasVmComponentProtection.VmStorageProtectionForPDL) { $null { '--' } 'clusterDefault' { 'Cluster default' } 'warning' { 'Issue events' } 'restartAggressive' { 'Power off and restart VMs' } 'disabled' { 'Disabled' } } 'APD Failure Response' = Switch ($DasVmComponentProtection.VmStorageProtectionForAPD) { $null { '--' } 'clusterDefault' { 'Cluster default' } 'warning' { 'Issue events' } 'restartConservative' { 'Power off and restart VMs - Conservative restart policy' } 'restartAggressive' { 'Power off and restart VMs - Aggressive restart policy' } 'disabled' { 'Disabled' } } 'VM Failover Delay' = Switch ($DasVmComponentProtection.VmTerminateDelayForAPDSec) { $null { '--' } '-1' { 'Disabled' } default { "$(($DasVmComponentProtection.VmTerminateDelayForAPDSec)/60) minutes" } } 'Response Recovery' = Switch ($DasVmComponentProtection.VmReactionOnAPDCleared) { $null { '--' } 'reset' { 'Reset VMs' } 'disabled' { 'Disabled' } 'useClusterDefault' { 'Cluster default' } } } } $TableParams = @{ Name = "HA VM Overrides PDL/APD Settings - $Cluster" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DasVmOverridePdlApd | Sort-Object 'Virtual Machine' | Table @TableParams } #endregion PDL/APD Protection Settings Section #region VM Monitoring Section Section -Style Heading5 'VM Monitoring' { $DasVmOverrideVmMonitoring = foreach ($DasVmOverride in $DasVmOverrides) { $DasVmMonitoring = $DasVmOverride.DasSettings.VmToolsMonitoringSettings [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($DasVmOverride.Key.Type)-$($DasVmOverride.Key.Value)" 'VM Monitoring' = Switch ($DasVmMonitoring.VmMonitoring) { $null { '--' } 'vmMonitoringDisabled' { 'Disabled' } 'vmMonitoringOnly' { 'VM Monitoring Only' } 'vmAndAppMonitoring' { 'VM and App Monitoring' } } 'Failure Interval' = Switch ($DasVmMonitoring.FailureInterval) { $null { '--' } default { if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') { '--' } else { "$($DasVmMonitoring.FailureInterval) seconds" } } } 'Minimum Uptime' = Switch ($DasVmMonitoring.MinUptime) { $null { '--' } default { if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') { '--' } else { "$($DasVmMonitoring.MinUptime) seconds" } } } 'Maximum Per-VM Resets' = Switch ($DasVmMonitoring.MaxFailures) { $null { '--' } default { if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') { '--' } else { $DasVmMonitoring.MaxFailures } } } 'Maximum Resets Time Window' = Switch ($DasVmMonitoring.MaxFailureWindow) { $null { '--' } '-1' { 'No window' } default { if ($DasVmMonitoring.VmMonitoring -eq 'vmMonitoringDisabled') { '--' } else { "Within $(($DasVmMonitoring.MaxFailureWindow)/3600) hrs" } } } } } $TableParams = @{ Name = "HA VM Overrides VM Monitoring - $Cluster" ColumnWidths = 40, 12, 12, 12, 12, 12 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DasVmOverrideVmMonitoring | Sort-Object 'Virtual Machine' | Table @TableParams } #endregion VM Monitoring Section } } #endregion vSphere HA VM Overrides } #endregion VM Overrides Section } #endregion Cluster VM Overrides #region Cluster VUM Baselines if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') { if ($VUMConnection) { if ("Desktop" -eq $PSVersionTable.PsEdition) { $ClusterPatchBaselines = $Cluster | Get-PatchBaseline } else { Write-PScriboMessage 'Cluster VUM baseline information is not currently available with your version of PowerShell.' } if ($ClusterPatchBaselines) { Section -Style Heading4 'Update Manager Baselines' { $ClusterBaselines = foreach ($ClusterBaseline in $ClusterPatchBaselines) { [PSCustomObject]@{ 'Baseline' = $ClusterBaseline.Name 'Description' = $ClusterBaseline.Description 'Type' = $ClusterBaseline.BaselineType 'Target Type' = $ClusterBaseline.TargetType 'Last Update Time' = ($ClusterBaseline.LastUpdateTime).ToLocalTime() '# of Patches' = $ClusterBaseline.CurrentPatches.Count } } $TableParams = @{ Name = "Update Manager Baselines - $Cluster" ColumnWidths = 25, 25, 10, 10, 20, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterBaselines | Sort-Object 'Baseline' | Table @TableParams } } if ($Healthcheck.Cluster.VUMCompliance) { $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Unknown' } | Set-Style -Style Warning $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Not Compliant' -or $_.Status -eq 'Incompatible' } | Set-Style -Style Critical } $TableParams = @{ Name = "Update Manager Compliance - $Cluster" ColumnWidths = 25, 50, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterComplianceInfo | Sort-Object Name, Baseline | Table @TableParams } } else { Write-PScriboMessage "Insufficient user privileges to report Cluster baselines. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned." } #endregion Cluster VUM Baselines #region Cluster VUM Compliance (Advanced Detail Information) if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') { if ($InfoLevel.Cluster -ge 4 -and $VumServer.Name) { if ("Desktop" -eq $PSVersionTable.PsEdition) { $ClusterCompliances = $Cluster | Get-Compliance } else { Write-PScriboMessage 'Cluster VUM compliance information is not currently available with your version of PowerShell.' } if ($ClusterCompliances) { Section -Style Heading4 'Update Manager Compliance' { $ClusterComplianceInfo = foreach ($ClusterCompliance in $ClusterCompliances) { [PSCustomObject]@{ 'Entity' = $ClusterCompliance.Entity 'Baseline' = $ClusterCompliance.Baseline.Name 'Status' = Switch ($ClusterCompliance.Status) { 'NotCompliant' { 'Not Compliant' } default { $ClusterCompliance.Status } } } } if ($Healthcheck.Cluster.VUMCompliance) { $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Unknown' } | Set-Style -Style Warning $ClusterComplianceInfo | Where-Object { $_.Status -eq 'Not Compliant' -or $_.Status -eq 'Incompatible' } | Set-Style -Style Critical } $TableParams = @{ Name = "Update Manager Compliance - $Cluster" ColumnWidths = 25, 50, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterComplianceInfo | Sort-Object Entity, Baseline | Table @TableParams } } } } else { Write-PScriboMessage "Insufficient user privileges to report Cluster compliance. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned." } #endregion Cluster VUM Compliance (Advanced Detail Information) #region Cluster Permissions Section -Style Heading4 'Permissions' { Paragraph "The following table details the permissions assigned to cluster $Cluster." BlankLine $VIPermissions = $Cluster | Get-VIPermission $ClusterVIPermissions = foreach ($VIPermission in $VIPermissions) { [PSCustomObject]@{ 'User/Group' = $VIPermission.Principal 'Is Group?' = Switch ($VIPermission.IsGroup) { $true { 'Yes' } $false { 'No' } } 'Role' = $VIPermission.Role 'Defined In' = $VIPermission.Entity 'Propagate' = Switch ($VIPermission.Propagate) { $true { 'Yes' } $false { 'No' } } } } $TableParams = @{ Name = "Permissions - $Cluster" ColumnWidths = 42, 12, 20, 14, 12 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ClusterVIPermissions | Sort-Object 'User/Group' | Table @TableParams } #endregion Cluster Permissions } } #endregion vSphere DRS Cluster Configuration } #endregion Cluster Section } } #endregion Cluster Detailed Information } #endregion Cluster Section } } #endregion Clusters #region Resource Pool Section Write-PScriboMessage "ResourcePool InfoLevel set at $($InfoLevel.ResourcePool)." if ($InfoLevel.ResourcePool -ge 1) { $ResourcePools = Get-ResourcePool -Server $vCenter | Sort-Object Parent, Name if ($ResourcePools) { #region Resource Pools Section Section -Style Heading2 'Resource Pools' { Paragraph "The following sections detail the configuration of resource pools managed by vCenter Server $vCenterServerName." #region Resource Pool Advanced Summary if ($InfoLevel.ResourcePool -le 2) { BlankLine $ResourcePoolInfo = foreach ($ResourcePool in $ResourcePools) { [PSCustomObject]@{ 'Resource Pool' = $ResourcePool.Name 'Parent' = $ResourcePool.Parent 'CPU Shares Level' = $ResourcePool.CpuSharesLevel 'CPU Reservation MHz' = $ResourcePool.CpuReservationMHz 'CPU Limit MHz' = Switch ($ResourcePool.CpuLimitMHz) { '-1' { 'Unlimited' } default { $ResourcePool.CpuLimitMHz } } 'Memory Shares Level' = $ResourcePool.MemSharesLevel 'Memory Reservation' = [math]::Round($ResourcePool.MemReservationGB, 2) 'Memory Limit GB' = Switch ($ResourcePool.MemLimitGB) { '-1' { 'Unlimited' } default { [math]::Round($ResourcePool.MemLimitGB, 2) } } } } $TableParams = @{ Name = "Resource Pool Summary - $($vCenterServerName)" ColumnWidths = 20, 20, 10, 10, 10, 10, 10, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ResourcePoolInfo | Sort-Object Name | Table @TableParams } #endregion Resource Pool Advanced Summary #region Resource Pool Detailed Information # TODO: Test Tags if ($InfoLevel.ResourcePool -ge 3) { foreach ($ResourcePool in $ResourcePools) { Section -Style Heading3 $ResourcePool.Name { $ResourcePoolDetail = [PSCustomObject]@{ 'Resource Pool' = $ResourcePool.Name 'ID' = $ResourcePool.Id 'Parent' = $ResourcePool.Parent 'CPU Shares Level' = $ResourcePool.CpuSharesLevel 'Number of CPU Shares' = $ResourcePool.NumCpuShares 'CPU Reservation' = "$($ResourcePool.CpuReservationMHz) MHz" 'CPU Expandable Reservation' = Switch ($ResourcePool.CpuExpandableReservation) { $true { 'Enabled' } $false { 'Disabled' } } 'CPU Limit MHz' = Switch ($ResourcePool.CpuLimitMHz) { '-1' { 'Unlimited' } default { "$($ResourcePool.CpuLimitMHz) MHz" } } 'Memory Shares Level' = $ResourcePool.MemSharesLevel 'Number of Memory Shares' = $ResourcePool.NumMemShares 'Memory Reservation' = "$([math]::Round($ResourcePool.MemReservationGB, 2)) GB" 'Memory Expandable Reservation' = Switch ($ResourcePool.MemExpandableReservation) { $true { 'Enabled' } $false { 'Disabled' } } 'Memory Limit' = Switch ($ResourcePool.MemLimitGB) { '-1' { 'Unlimited' } default { "$([math]::Round($ResourcePool.MemLimitGB, 2)) GB" } } 'Number of VMs' = $ResourcePool.ExtensionData.VM.Count } <# $MemberProps = @{ 'InputObject' = $ResourcePoolDetail 'MemberType' = 'NoteProperty' } if ($TagAssignments | Where-Object {$_.entity -eq $ResourcePool}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $ResourcePool}).Tag -join ',') } #> #region Resource Pool Advanced Detail Information if ($InfoLevel.ResourcePool -ge 4) { $ResourcePoolDetail | ForEach-Object { # Query for VMs by resource pool Id $ResourcePoolId = $_.Id $ResourcePoolVMs = $VMs | Where-Object { $_.ResourcePoolId -eq $ResourcePoolId } | Sort-Object Name Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Virtual Machines' -Value ($ResourcePoolVMs.Name -join ', ') } } #endregion Resource Pool Advanced Detail Information $TableParams = @{ Name = "Resource Pool Configuration - $($ResourcePool.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ResourcePoolDetail | Table @TableParams } } } #endregion Resource Pool Detailed Information } #endregion Resource Pools Section } } #endregion Resource Pool Section #region ESXi VMHost Section Write-PScriboMessage "VMHost InfoLevel set at $($InfoLevel.VMHost)." if ($InfoLevel.VMHost -ge 1) { if ($VMHosts) { #region Hosts Section Section -Style Heading2 'Hosts' { Paragraph "The following sections detail the configuration of VMware ESXi hosts managed by vCenter Server $vCenterServerName." #region ESXi Host Advanced Summary if ($InfoLevel.VMHost -le 2) { BlankLine $VMHostInfo = foreach ($VMHost in $VMHosts) { [PSCustomObject]@{ 'Host' = $VMHost.Name 'Version' = $VMHost.Version 'Build' = $VMHost.Build 'Parent' = $VMHost.Parent 'Connection State' = Switch ($VMHost.ConnectionState) { 'NotResponding' { 'Not Responding' } default { $TextInfo.ToTitleCase($VMHost.ConnectionState) } } 'CPU Sockets' = $VMHost.ExtensionData.Hardware.CpuInfo.NumCpuPackages 'CPU Cores' = $VMHost.ExtensionData.Hardware.CpuInfo.NumCpuCores 'Memory GB' = [math]::Round($VMHost.MemoryTotalGB, 0) '# of VMs' = $VMHost.ExtensionData.Vm.Count } } if ($Healthcheck.VMHost.ConnectionState) { $VMHostInfo | Where-Object { $_.'Connection State' -eq 'Maintenance' } | Set-Style -Style Warning $VMHostInfo | Where-Object { $_.'Connection State' -eq 'Not Responding' } | Set-Style -Style Critical $VMHostInfo | Where-Object { $_.'Connection State' -eq 'Disconnected' } | Set-Style -Style Critical } $TableParams = @{ Name = "Host Summary - $($vCenterServerName)" ColumnWidths = 17, 9, 11, 15, 13, 9, 9, 9, 8 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostInfo | Table @TableParams } #endregion ESXi Host Advanced Summary #region ESXi Host Detailed Information if ($InfoLevel.VMHost -ge 3) { #region foreach VMHost Detailed Information loop foreach ($VMHost in ($VMHosts | Where-Object { $_.ConnectionState -eq 'Connected' -or $_.ConnectionState -eq 'Maintenance' })) { #region VMHost Section Section -Style Heading3 $VMHost { # TODO: Host Certificate, Swap File Location # TODO: Test Tags #region ESXi Host Hardware Section Section -Style Heading4 'Hardware' { Paragraph "The following section details the host hardware configuration for $VMHost." BlankLine #region ESXi Host Specifications $VMHostUptime = Get-Uptime -VMHost $VMHost $esxcli = Get-EsxCli -VMHost $VMHost -V2 -Server $vCenter $ScratchLocation = Get-AdvancedSetting -Entity $VMHost | Where-Object { $_.Name -eq 'ScratchConfig.CurrentScratchLocation' } $VMHostDetail = [PSCustomObject]@{ 'Host' = $VMHost.Name 'Connection State' = Switch ($VMHost.ConnectionState) { 'NotResponding' { 'Not Responding' } default { $TextInfo.ToTitleCase($VMHost.ConnectionState) } } 'ID' = $VMHost.Id 'Parent' = $VMHost.Parent 'Manufacturer' = $VMHost.Manufacturer 'Model' = $VMHost.Model 'Serial Number' = Switch ($VMHost.ExtensionData.Hardware.SystemInfo.SerialNumber) { $null { '--' } default { $VMHost.ExtensionData.Hardware.SystemInfo.SerialNumber } } 'Asset Tag' = Switch ($VMHost.ExtensionData.Summary.Hardware.OtherIdentifyingInfo[0].IdentifierValue) { '' { 'Unknown' } $null { 'Unknown' } default { $VMHost.ExtensionData.Summary.Hardware.OtherIdentifyingInfo[0].IdentifierValue } } 'Processor Type' = $VMHost.Processortype 'HyperThreading' = Switch ($VMHost.HyperthreadingActive) { $true { 'Enabled' } $false { 'Disabled' } } 'Number of CPU Sockets' = $VMHost.ExtensionData.Hardware.CpuInfo.NumCpuPackages 'Number of CPU Cores' = $VMHost.ExtensionData.Hardware.CpuInfo.NumCpuCores 'Number of CPU Threads' = $VMHost.ExtensionData.Hardware.CpuInfo.NumCpuThreads 'CPU Total / Used' = "$([math]::Round(($VMHost.CpuTotalMhz) / 1000, 2)) GHz / $([math]::Round(($VMHost.CpuUsageMhz) / 1000, 2)) GHz" 'Memory Total / Used' = "$([math]::Round($VMHost.MemoryTotalGB, 2)) GB / $([math]::Round($VMHost.MemoryUsageGB, 2)) GB" 'NUMA Nodes' = $VMHost.ExtensionData.Hardware.NumaInfo.NumNodes 'Number of NICs' = $VMHost.ExtensionData.Summary.Hardware.NumNics 'Number of HBAs' = $VMHost.ExtensionData.Summary.Hardware.NumHBAs 'Number of Datastores' = ($VMHost.ExtensionData.Datastore).Count 'Number of VMs' = $VMHost.ExtensionData.VM.Count 'Maximum EVC Mode' = $EvcModeLookup."$($VMHost.MaxEVCMode)" 'EVC Graphics Mode' = Switch ($VMHost.ExtensionData.Summary.CurrentEVCGraphicsModeKey) { $null { 'Not applicable'} default { $VMHost.ExtensionData.Summary.CurrentEVCGraphicsModeKey } } 'Power Management Policy' = $VMHost.ExtensionData.Hardware.CpuPowerManagementInfo.CurrentPolicy 'Scratch Location' = $ScratchLocation.Value 'Bios Version' = $VMHost.ExtensionData.Hardware.BiosInfo.BiosVersion 'Bios Release Date' = $VMHost.ExtensionData.Hardware.BiosInfo.ReleaseDate 'ESXi Version' = $VMHost.Version 'ESXi Build' = $VMHost.build 'Boot Time' = ($VMHost.ExtensionData.Runtime.Boottime).ToLocalTime() 'Uptime Days' = $VMHostUptime.UptimeDays } $MemberProps = @{ 'InputObject' = $VMHostDetail 'MemberType' = 'NoteProperty' } if ($UserRole.Privilege -contains 'Global.Licenses') { $VMHostLicense = Get-License -VMHost $VMHost Add-Member @MemberProps -Name 'Product' -Value $VMHostLicense.Product Add-Member @MemberProps -Name 'License Key' -Value $VMHostLicense.LicenseKey Add-Member @MemberProps -Name 'License Expiration' -Value $VMHostLicense.Expiration } else { Write-PScriboMessage "Insufficient user privileges to report ESXi host licensing. Please ensure the user account has the 'Global > Licenses' privilege assigned." } <# if ($TagAssignments | Where-Object {$_.entity -eq $VMHost}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $VMHost}).Tag -join ',') } #> if ($Healthcheck.VMHost.ConnectionState) { $VMHostDetail | Where-Object { $_.'Connection State' -eq 'Maintenance' } | Set-Style -Style Warning -Property 'Connection State' } if ($Healthcheck.VMHost.HyperThreading) { $VMHostDetail | Where-Object { $_.'HyperThreading' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Disabled' } if ($Healthcheck.VMHost.Licensing) { $VMHostDetail | Where-Object { $_.'Product' -like '*Evaluation*' } | Set-Style -Style Warning -Property 'Product' $VMHostDetail | Where-Object { $_.'License Key' -like '*-00000-00000' } | Set-Style -Style Warning -Property 'License Key' $VMHostDetail | Where-Object { $_.'License Expiration' -eq 'Expired' } | Set-Style -Style Critical -Property 'License Expiration' } if ($Healthcheck.VMHost.ScratchLocation) { $VMHostDetail | Where-Object { $_.'Scratch Location' -eq '/tmp/scratch' } | Set-Style -Style Warning -Property 'Scratch Location' } if ($Healthcheck.VMHost.UpTimeDays) { $VMHostDetail | Where-Object { $_.'Uptime Days' -ge 275 -and $_.'Uptime Days' -lt 365 } | Set-Style -Style Warning -Property 'Uptime Days' $VMHostDetail | Where-Object { $_.'Uptime Days' -ge 365 } | Set-Style -Style Critical -Property 'Uptime Days' } $TableParams = @{ Name = "Hardware Configuration - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostDetail | Table @TableParams #endregion ESXi Host Specifications #region ESXi IPMI/BMC Settings Try { $VMHostIPMI = $esxcli.hardware.ipmi.bmc.get.invoke() } Catch { Write-PScriboMessage -IsWarning "Unable to collect IPMI / BMC configuration from $VMHost" } if ($VMHostIPMI) { Section -Style Heading5 'IPMI / BMC' { $VMHostIPMIInfo = [PSCustomObject]@{ 'Manufacturer' = $VMHostIPMI.Manufacturer 'MAC Address' = $VMHostIPMI.MacAddress 'IP Address' = $VMHostIPMI.IPv4Address 'Subnet Mask' = $VMHostIPMI.IPv4Subnet 'Gateway' = $VMHostIPMI.IPv4Gateway 'Firmware Version' = $VMHostIPMI.BMCFirmwareVersion } $TableParams = @{ Name = "IPMI / BMC - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostIPMIInfo | Table @TableParams } } #endregion ESXi IPMI/BMC Settings #region ESXi Host Boot Device Section -Style Heading5 'Boot Device' { $ESXiBootDevice = Get-ESXiBootDevice -VMHost $VMHost $VMHostBootDevice = [PSCustomObject]@{ 'Host' = $ESXiBootDevice.Host 'Device' = $ESXiBootDevice.Device 'Boot Type' = $ESXiBootDevice.BootType 'Vendor' = $ESXiBootDevice.Vendor 'Model' = $ESXiBootDevice.Model 'Size' = Switch ($ESXiBootDevice.SizeMB) { 'N/A' { 'N/A' } default { "$([math]::Round($ESXiBootDevice.SizeMB / 1024, 2)) GB" } } 'Is SAS' = $ESXiBootDevice.IsSAS 'Is SSD' = $ESXiBootDevice.IsSSD 'Is USB' = $ESXiBootDevice.IsUSB } $TableParams = @{ Name = "Boot Device - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostBootDevice | Table @TableParams } #endregion ESXi Host Boot Devices #region ESXi Host PCI Devices Section -Style Heading5 'PCI Devices' { $PciHardwareDevices = $esxcli.hardware.pci.list.Invoke() | Where-Object { $_.VMkernelName -match 'vmhba|vmnic|vmgfx' -and $_.ModuleName -ne 'None'} | Sort-Object -Property VMkernelName $VMHostPciDevices = foreach ($PciHardwareDevice in $PciHardwareDevices) { [PSCustomObject]@{ 'Device' = $PciHardwareDevice.VMkernelName 'PCI Address' = $PciHardwareDevice.Address 'Device Class' = $PciHardwareDevice.DeviceClassName 'Device Name' = $PciHardwareDevice.DeviceName 'Vendor Name' = $PciHardwareDevice.VendorName 'Slot Description' = $PciHardwareDevice.SlotDescription } } $TableParams = @{ Name = "PCI Devices - $VMHost" ColumnWidths = 12, 13, 15, 25, 20, 15 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostPciDevices | Table @TableParams } #endregion ESXi Host PCI Devices #region ESXi Host PCI Devices Drivers & Firmware Section -Style Heading5 'PCI Devices Drivers & Firmware' { $VMHostPciDevicesDetails = Get-PciDeviceDetail -Server $vCenter -esxcli $esxcli | Sort-Object 'Device' $TableParams = @{ Name = "PCI Devices Drivers & Firmware - $VMHost" ColumnWidths = 12, 20, 11, 19, 11, 11, 16 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostPciDevicesDetails | Table @TableParams } #endregion ESXi Host PCI Devices Drivers & Firmware } #endregion ESXi Host Hardware Section #region ESXi Host System Section Section -Style Heading4 'System' { Paragraph "The following section details the host system configuration for $VMHost." #region ESXi Host Profile Information if ($VMHost | Get-VMHostProfile) { Section -Style Heading5 'Host Profile' { $VMHostProfile = $VMHost | Get-VMHostProfile | Select-Object Name, Description $TableParams = @{ Name = "Host Profile - $VMHost" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostProfile | Sort-Object Name | Table @TableParams } } #endregion ESXi Host Profile Information #region ESXi Host Image Profile Information if ($UserRole.Privilege -contains 'Host.Config.Settings') { Section -Style Heading5 'Image Profile' { $installdate = Get-InstallDate $esxcli = Get-EsxCli -VMHost $VMHost -V2 -Server $vCenter $ImageProfile = $esxcli.software.profile.get.Invoke() $SecurityProfile = [PSCustomObject]@{ 'Image Profile' = $ImageProfile.Name 'Vendor' = $ImageProfile.Vendor 'Installation Date' = $InstallDate.InstallDate } $TableParams = @{ Name = "Image Profile - $VMHost" #ColumnWidths = 50, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $SecurityProfile | Table @TableParams } } else { Write-PScriboMessage "Insufficient user privileges to report ESXi host image profiles. Please ensure the user account has the 'Host > Configuration > Change settings' privilege assigned." } #endregion ESXi Host Image Profile Information #region ESXi Host Time Configuration Section -Style Heading5 'Time Configuration' { $VMHostTimeSettings = [PSCustomObject]@{ 'Time Zone' = $VMHost.timezone 'NTP Service' = Switch ((Get-VMHostService -VMHost $VMHost | Where-Object { $_.key -eq 'ntpd' }).Running) { $true { 'Running' } $false { 'Stopped' } } 'NTP Server(s)' = (Get-VMHostNtpServer -VMHost $VMHost | Sort-Object) -join ', ' } if ($Healthcheck.VMHost.NTP) { $VMHostTimeSettings | Where-Object { $_.'NTP Service' -eq 'Stopped' } | Set-Style -Style Critical -Property 'NTP Service' } $TableParams = @{ Name = "Time Configuration - $VMHost" ColumnWidths = 30, 30, 40 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostTimeSettings | Table @TableParams } #endregion ESXi Host Time Configuration #region ESXi Host Syslog Configuration $SyslogConfig = $VMHost | Get-VMHostSysLogServer if ($SyslogConfig) { Section -Style Heading5 'Syslog Configuration' { # TODO: Syslog Rotate & Size, Log Directory (Adv Settings) $SyslogConfig = $SyslogConfig | Select-Object @{L = 'SysLog Server'; E = { $_.Host } }, Port $TableParams = @{ Name = "Syslog Configuration - $VMHost" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $SyslogConfig | Table @TableParams } } #endregion ESXi Host Syslog Configuration #region ESXi Update Manager Baseline Information if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') { if ($VumServer.Name) { if ("Desktop" -eq $PSVersionTable.PsEdition) { $VMHostPatchBaselines = $VMHost | Get-PatchBaseline } else { Write-PScriboMessage 'ESXi VUM baseline information is not currently available with your version of PowerShell.' } if ($VMHostPatchBaselines) { Section -Style Heading5 'Update Manager Baselines' { $VMHostBaselines = foreach ($VMHostBaseline in $VMHostPatchBaselines) { [PSCustomObject]@{ 'Baseline' = $VMHostBaseline.Name 'Description' = $VMHostBaseline.Description 'Type' = $VMHostBaseline.BaselineType 'Target Type' = $VMHostBaseline.TargetType 'Last Update Time' = $VMHostBaseline.LastUpdateTime '# of Patches' = $VMHostBaseline.CurrentPatches.Count } } $TableParams = @{ Name = "Update Manager Baselines - $VMHost" ColumnWidths = 25, 25, 10, 10, 20, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostBaselines | Sort-Object 'Baseline' | Table @TableParams } } } } else { Write-PScriboMessage "Insufficient user privileges to report ESXi host baselines. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned." } #endregion ESXi Update Manager Baseline Information #region ESXi Update Manager Compliance Information if ($UserRole.Privilege -contains 'VcIntegrity.Updates.com.vmware.vcIntegrity.ViewStatus') { if ($VumServer.Name) { if ("Desktop" -eq $PSVersionTable.PsEdition) { $VMHostCompliances = $VMHost | Get-Compliance } else { Write-PScriboMessage 'ESXi VUM compliance information is not currently available with your version of PowerShell.' } if ($VMHostCompliances) { Section -Style Heading5 'Update Manager Compliance' { $VMHostComplianceInfo = foreach ($VMHostCompliance in $VMHostCompliances) { [PSCustomObject]@{ 'Baseline' = $VMHostCompliance.Baseline.Name 'Status' = Switch ($VMHostCompliance.Status) { 'NotCompliant' { 'Not Compliant' } default { $VMHostCompliance.Status } } } } if ($Healthcheck.VMHost.VUMCompliance) { $VMHostComplianceInfo | Where-Object { $_.Status -eq 'Unknown' } | Set-Style -Style Warning $VMHostComplianceInfo | Where-Object { $_.Status -eq 'Not Compliant' -or $_.Status -eq 'Incompatible' } | Set-Style -Style Critical } $TableParams = @{ Name = "Update Manager Compliance - $VMHost" ColumnWidths = 75, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostComplianceInfo | Sort-Object Baseline | Table @TableParams } } } } else { Write-PScriboMessage "Insufficient user privileges to report ESXi host compliance. Please ensure the user account has the 'VMware Update Manager / VMware vSphere Lifecycle Manager > Manage Patches and Upgrades > View Compliance Status' privilege assigned." } #endregion ESXi Update Manager Compliance Information #region ESXi Host Comprehensive Information Section if ($InfoLevel.VMHost -ge 5) { #region ESXi Host Advanced System Settings Section -Style Heading5 'Advanced System Settings' { $AdvSettings = $VMHost | Get-AdvancedSetting | Select-Object Name, Value $TableParams = @{ Name = "Advanced System Settings - $VMHost" ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $AdvSettings | Sort-Object Name | Table @TableParams } #endregion ESXi Host Advanced System Settings #region ESXi Host Software VIBs Section -Style Heading5 'Software VIBs' { $esxcli = Get-EsxCli -VMHost $VMHost -V2 -Server $vCenter $VMHostVibs = $esxcli.software.vib.list.Invoke() $VMHostVibs = foreach ($VMHostVib in $VMHostVibs) { [PSCustomObject]@{ 'VIB' = $VMHostVib.Name 'ID' = $VMHostVib.Id 'Version' = $VMHostVib.Version 'Acceptance Level' = $VMHostVib.AcceptanceLevel 'Creation Date' = $VMHostVib.CreationDate 'Install Date' = $VMHostVib.InstallDate } } $TableParams = @{ Name = "Software VIBs - $VMHost" ColumnWidths = 15, 25, 15, 15, 15, 15 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostVibs | Sort-Object 'Install Date' -Descending | Table @TableParams } #endregion ESXi Host Software VIBs } #endregion ESXi Host Comprehensive Information Section } #endregion ESXi Host System Section #region ESXi Host Storage Section Section -Style Heading4 'Storage' { Paragraph "The following section details the host storage configuration for $VMHost." #region ESXi Host Datastore Specifications $VMHostDatastores = $VMHost | Get-Datastore | Where-Object { ($_.State -eq 'Available') -and ($_.CapacityGB -gt 0) } | Sort-Object Name if ($VMHostDatastores) { Section -Style Heading5 'Datastores' { $VMHostDsSpecs = foreach ($VMHostDatastore in $VMHostDatastores) { [PSCustomObject]@{ 'Datastore' = $VMHostDatastore.Name 'Type' = $VMHostDatastore.Type 'Version' = Switch ($VMHostDatastore.FileSystemVersion) { $null { '--' } default { $VMHostDatastore.FileSystemVersion } } '# of VMs' = $VMHostDatastore.ExtensionData.VM.Count 'Total Capacity GB' = [math]::Round($VMHostDatastore.CapacityGB, 2) 'Used Capacity GB' = [math]::Round((($VMHostDatastore.CapacityGB) - ($VMHostDatastore.FreeSpaceGB)), 2) 'Free Space GB' = [math]::Round($VMHostDatastore.FreeSpaceGB, 2) '% Used' = [math]::Round((100 - (($VMHostDatastore.FreeSpaceGB) / ($VMHostDatastore.CapacityGB) * 100)), 2) } } if ($Healthcheck.Datastore.CapacityUtilization) { $VMHostDsSpecs | Where-Object { $_.'% Used' -ge 90 } | Set-Style -Style Critical $VMHostDsSpecs | Where-Object { $_.'% Used' -ge 75 -and $_.'% Used' -lt 90 } | Set-Style -Style Warning } $TableParams = @{ Name = "Datastores - $VMHost" ColumnWidths = 20, 8, 9, 8, 15, 15, 15, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostDsSpecs | Sort-Object 'Datastore' | Table @TableParams } } #endregion ESXi Host Datastore Specifications #region ESXi Host Storage Adapter Information $VMHostHbas = $VMHost | Get-VMHostHba | Sort-Object Device if ($VMHostHbas) { #region ESXi Host Storage Adapters Section Section -Style Heading5 'Storage Adapters' { Paragraph "The following section details the storage adapter configuration for $VMHost." foreach ($VMHostHba in $VMHostHbas) { $Target = ((Get-View $VMHostHba.VMhost).Config.StorageDevice.ScsiTopology.Adapter | Where-Object { $_.Adapter -eq $VMHostHba.Key }).Target $LUNs = Get-ScsiLun -Hba $VMHostHba -LunType "disk" -ErrorAction SilentlyContinue $Paths = ($Target | foreach { $_.Lun.Count } | Measure-Object -Sum) Section -Style Heading5 "$($VMHostHba.Device)" { $VMHostStorageAdapter = [PSCustomObject]@{ 'Adapter' = $VMHostHba.Device 'Type' = Switch ($VMHostHba.Type) { 'FibreChannel' { 'Fibre Channel' } 'IScsi' { 'iSCSI' } 'ParallelScsi' { 'Parallel SCSI' } default { $TextInfo.ToTitleCase($VMHostHba.Type) } } 'Model' = $VMHostHba.Model 'Status' = $TextInfo.ToTitleCase($VMHostHba.Status) 'Targets' = $Target.Count 'Devices' = $LUNs.Count 'Paths' = $Paths.Sum } $MemberProps = @{ 'InputObject' = $VMHostStorageAdapter 'MemberType' = 'NoteProperty' } if ($VMHostStorageAdapter.Type -eq 'iSCSI') { $iScsiAuthenticationMethod = Switch ($VMHostHba.ExtensionData.AuthenticationProperties.ChapAuthenticationType) { 'chapProhibited' { 'None' } 'chapPreferred' { 'Use unidirectional CHAP unless prohibited by target' } 'chapDiscouraged' { 'Use unidirectional CHAP if required by target' } 'chapRequired' { Switch ($VMHostHba.ExtensionData.AuthenticationProperties.MutualChapAuthenticationType) { 'chapProhibited' { 'Use unidirectional CHAP' } 'chapRequired' { 'Use bidirectional CHAP' } } } default { $VMHostHba.ExtensionData.AuthenticationProperties.ChapAuthenticationType } } Add-Member @MemberProps -Name 'iSCSI Name' -Value $VMHostHba.IScsiName if ($VMHostHba.IScsiAlias) { Add-Member @MemberProps -Name 'iSCSI Alias' -Value $VMHostHba.IScsiAlias } else { Add-Member @MemberProps -Name 'iSCSI Alias' -Value '--' } if ($VMHostHba.CurrentSpeedMb) { Add-Member @MemberProps -Name 'Speed' -Value "$($VMHostHba.CurrentSpeedMb) Mb" } else { Add-Member @MemberProps -Name 'Speed' -Value '--' } if ($VMHostHba.ExtensionData.ConfiguredSendTarget) { Add-Member @MemberProps -Name 'Dynamic Discovery' -Value (($VMHostHba.ExtensionData.ConfiguredSendTarget | ForEach-Object { "$($_.Address)" + ":" + "$($_.Port)" }) -join [Environment]::NewLine) } else { Add-Member @MemberProps -Name 'Dynamic Discovery' -Value '--' } if ($VMHostHba.ExtensionData.ConfiguredStaticTarget) { Add-Member @MemberProps -Name 'Static Discovery' -Value (($VMHostHba.ExtensionData.ConfiguredStaticTarget | ForEach-Object { "$($_.Address)" + ":" + "$($_.Port)" + " " + "$($_.IScsiName)" }) -join [Environment]::NewLine) } else { Add-Member @MemberProps -Name 'Static Discovery' -Value '--' } if ($iScsiAuthenticationMethod -eq 'None') { Add-Member @MemberProps -Name 'Authentication Method' -Value $iScsiAuthenticationMethod } elseif ($iScsiAuthenticationMethod -eq 'Use bidirectional CHAP') { Add-Member @MemberProps -Name 'Authentication Method' -Value $iScsiAuthenticationMethod Add-Member @MemberProps -Name 'Outgoing CHAP Name' -Value $VMHostHba.ExtensionData.AuthenticationProperties.ChapName Add-Member @MemberProps -Name 'Incoming CHAP Name' -Value $VMHostHba.ExtensionData.AuthenticationProperties.MutualChapName } else { Add-Member @MemberProps -Name 'Authentication Method' -Value $iScsiAuthenticationMethod Add-Member @MemberProps -Name 'Outgoing CHAP Name' -Value $VMHostHba.ExtensionData.AuthenticationProperties.ChapName } if ($InfoLevel.VMHost -ge 4) { Add-Member @MemberProps -Name 'Advanced Options' -Value (($VMHostHba.ExtensionData.AdvancedOptions | ForEach-Object { "$($_.Key) = $($_.Value)" }) -join [Environment]::NewLine) } } if ($VMHostStorageAdapter.Type -eq 'Fibre Channel') { Add-Member @MemberProps -Name 'Node WWN' -Value (([String]::Format("{0:X}", $VMHostHba.NodeWorldWideName) -split "(\w{2})" | Where-Object { $_ -ne "" }) -join ":") Add-Member @MemberProps -Name 'Port WWN' -Value (([String]::Format("{0:X}", $VMHostHba.PortWorldWideName) -split "(\w{2})" | Where-Object { $_ -ne "" }) -join ":") Add-Member @MemberProps -Name 'Speed' -Value $VMHostHba.Speed } if ($Healthcheck.VMHost.StorageAdapter) { $VMHostStorageAdapter | Where-Object { $_.'Status' -ne 'Online' } | Set-Style -Style Warning -Property 'Status' $VMHostStorageAdapter | Where-Object { $_.'Status' -eq 'Offline' } | Set-Style -Style Critical -Property 'Status' } $TableParams = @{ Name = "Storage Adapter $($VMHostStorageAdapter.Adapter) - $VMHost" List = $true ColumnWidths = 25, 75 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostStorageAdapter | Table @TableParams } } } #endregion ESXi Host Storage Adapters Section } #endregion ESXi Host Storage Adapter Information } #endregion ESXi Host Storage Section #region ESXi Host Network Section Section -Style Heading4 'Network' { Paragraph "The following section details the host network configuration for $VMHost." BlankLine #region ESXi Host Network Configuration $VMHostNetwork = $VMHost.ExtensionData.Config.Network $VMHostVirtualSwitch = @() $VMHostVss = foreach ($vSwitch in $VMHost.ExtensionData.Config.Network.Vswitch) { $VMHostVirtualSwitch += $vSwitch.Name } $VMHostDvs = foreach ($dvSwitch in $VMHost.ExtensionData.Config.Network.ProxySwitch) { $VMHostVirtualSwitch += $dvSwitch.DvsName } $VMHostNetworkDetail = [PSCustomObject]@{ 'Host' = $VMHost.Name 'Virtual Switches' = ($VMHostVirtualSwitch | Sort-Object) -join ', ' 'VMkernel Adapters' = ($VMHostNetwork.Vnic.Device | Sort-Object) -join ', ' 'Physical Adapters' = ($VMHostNetwork.Pnic.Device | Sort-Object) -join ', ' 'VMkernel Gateway' = $VMHostNetwork.IpRouteConfig.DefaultGateway 'IPv6' = Switch ($VMHostNetwork.IPv6Enabled) { $true { 'Enabled' } $false { 'Disabled' } } 'VMkernel IPv6 Gateway' = Switch ($VMHostNetwork.IpRouteConfig.IpV6DefaultGateway) { $null { '--' } default { $VMHostNetwork.IpRouteConfig.IpV6DefaultGateway } } 'DNS Servers' = ($VMHostNetwork.DnsConfig.Address | Sort-Object) -join ', ' 'Host Name' = $VMHostNetwork.DnsConfig.HostName 'Domain Name' = $VMHostNetwork.DnsConfig.DomainName 'Search Domain' = ($VMHostNetwork.DnsConfig.SearchDomain | Sort-Object) -join ', ' } if ($Healthcheck.VMHost.IPv6) { $VMHostNetworkDetail | Where-Object { $_.'IPv6' -eq $false } | Set-Style -Style Warning -Property 'IPv6' } $TableParams = @{ Name = "Network Configuration - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostNetworkDetail | Table @TableParams #endregion ESXi Host Network Configuration #region ESXi Host Physical Adapters Section -Style Heading5 'Physical Adapters' { Paragraph "The following section details the physical network adapter configuration for $VMHost." $PhysicalNetAdapters = $VMHost.ExtensionData.Config.Network.Pnic | Sort-Object Device $VMHostPhysicalNetAdapters = foreach ($PhysicalNetAdapter in $PhysicalNetAdapters) { [PSCustomObject]@{ 'Adapter' = $PhysicalNetAdapter.Device 'Status' = Switch ($PhysicalNetAdapter.Linkspeed) { $null { 'Disconnected' } default { 'Connected' } } 'Virtual Switch' = $( if ($VMHost.ExtensionData.Config.Network.Vswitch.Pnic -contains $PhysicalNetAdapter.Key) { ($VMHost.ExtensionData.Config.Network.Vswitch | Where-Object { $_.Pnic -eq $PhysicalNetAdapter.Key }).Name } elseif ($VMHost.ExtensionData.Config.Network.ProxySwitch.Pnic -contains $PhysicalNetAdapter.Key) { ($VMHost.ExtensionData.Config.Network.ProxySwitch | Where-Object { $_.Pnic -eq $PhysicalNetAdapter.Key }).DvsName } else { '--' } ) 'MAC Address' = $PhysicalNetAdapter.Mac 'Actual Speed, Duplex' = Switch ($PhysicalNetAdapter.LinkSpeed.SpeedMb) { $null { 'Down' } default { if ($PhysicalNetAdapter.LinkSpeed.Duplex) { "$($PhysicalNetAdapter.LinkSpeed.SpeedMb) Mbps, Full Duplex" } else { 'Auto negotiate' } } } 'Configured Speed, Duplex' = Switch ($PhysicalNetAdapter.Spec.LinkSpeed) { $null { 'Auto negotiate' } default { if ($PhysicalNetAdapter.Spec.LinkSpeed.Duplex) { "$($PhysicalNetAdapter.Spec.LinkSpeed.SpeedMb) Mbps, Full Duplex" } else { "$($PhysicalNetAdapter.Spec.LinkSpeed.SpeedMb) Mbps" } } } 'Wake on LAN' = Switch ($PhysicalNetAdapter.WakeOnLanSupported) { $true { 'Supported' } $false { 'Not Supported' } } } } if ($Healthcheck.VMHost.NetworkAdapter) { $VMHostPhysicalNetAdapters | Where-Object { $_.'Status' -ne 'Connected' } | Set-Style -Style Critical -Property 'Status' $VMHostPhysicalNetAdapters | Where-Object { $_.'Actual Speed, Duplex' -eq 'Down' } | Set-Style -Style Critical -Property 'Actual Speed, Duplex' } if ($InfoLevel.VMHost -ge 4) { foreach ($VMHostPhysicalNetAdapter in $VMHostPhysicalNetAdapters) { Section -Style Heading5 "$($VMHostPhysicalNetAdapter.Adapter)" { $TableParams = @{ Name = "Physical Adapter $($VMHostPhysicalNetAdapter.Adapter) - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostPhysicalNetAdapter | Table @TableParams } } } else { BlankLine $TableParams = @{ Name = "Physical Adapters - $VMHost" ColumnWidths = 11, 13, 15, 19, 14, 14, 14 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostPhysicalNetAdapters | Table @TableParams } } #endregion ESXi Host Physical Adapters #region ESXi Host Cisco Discovery Protocol $VMHostNetworkAdapterCDP = $VMHost | Get-VMHostNetworkAdapterDP | Where-Object { $_.Status -eq 'Connected' } | Sort-Object Device if ($VMHostNetworkAdapterCDP) { Section -Style Heading5 'Cisco Discovery Protocol' { Paragraph "The following section details the CDP information for $VMHost." if ($InfoLevel.VMHost -ge 4) { foreach ($VMHostNetworkAdapter in $VMHostNetworkAdapterCDP) { Section -Style Heading5 "$($VMHostNetworkAdapter.Device)" { $VMHostCDP = [PSCustomObject]@{ 'Status' = $VMHostNetworkAdapter.Status 'System Name' = $VMHostNetworkAdapter.SystemName 'Hardware Platform' = $VMHostNetworkAdapter.HardwarePlatform 'Switch ID' = $VMHostNetworkAdapter.SwitchId 'Software Version' = $VMHostNetworkAdapter.SoftwareVersion 'Management Address' = $VMHostNetworkAdapter.ManagementAddress 'Address' = $VMHostNetworkAdapter.Address 'Port ID' = $VMHostNetworkAdapter.PortId 'VLAN' = $VMHostNetworkAdapter.Vlan 'MTU' = $VMHostNetworkAdapter.Mtu } $TableParams = @{ Name = "Network Adapter $($VMHostNetworkAdapter.Device) CDP Information - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostCDP | Table @TableParams } } } else { BlankLine $VMHostCDP = foreach ($VMHostNetworkAdapter in $VMHostNetworkAdapterCDP) { [PSCustomObject]@{ 'Adapter' = $VMHostNetworkAdapter.Device 'Status' = $VMHostNetworkAdapter.Status 'Hardware Platform' = $VMHostNetworkAdapter.HardwarePlatform 'Switch ID' = $VMHostNetworkAdapter.SwitchId 'Address' = $VMHostNetworkAdapter.Address 'Port ID' = $VMHostNetworkAdapter.PortId } } $TableParams = @{ Name = "Network Adapter CDP Information - $VMHost" ColumnWidths = 11, 13, 26, 22, 17, 11 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostCDP | Table @TableParams } } } #endregion ESXi Host Cisco Discovery Protocol #region ESXi Host Link Layer Discovery Protocol $VMHostNetworkAdapterLLDP = $VMHost | Get-VMHostNetworkAdapterDP | Where-Object { $null -ne $_.ChassisId } | Sort-Object Device if ($VMHostNetworkAdapterLLDP) { Section -Style Heading5 'Link Layer Discovery Protocol' { Paragraph "The following section details the LLDP information for $VMHost." if ($InfoLevel.VMHost -ge 4) { foreach ($VMHostNetworkAdapter in $VMHostNetworkAdapterLLDP) { Section -Style Heading5 "$($VMHostNetworkAdapter.Device)" { $VMHostLLDP = [PSCustomObject]@{ 'Chassis ID' = $VMHostNetworkAdapter.ChassisId 'Port ID' = $VMHostNetworkAdapter.PortId 'Time to live' = $VMHostNetworkAdapter.TimeToLive 'TimeOut' = $VMHostNetworkAdapter.TimeOut 'Samples' = $VMHostNetworkAdapter.Samples 'Management Address' = $VMHostNetworkAdapter.ManagementAddress 'Port Description' = $VMHostNetworkAdapter.PortDescription 'System Description' = $VMHostNetworkAdapter.SystemDescription 'System Name' = $VMHostNetworkAdapter.SystemName } $TableParams = @{ Name = "Network Adapter $($VMHostNetworkAdapter.Device) LLDP Information - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostLLDP | Table @TableParams } } } else { BlankLine $VMHostLLDP = foreach ($VMHostNetworkAdapter in $VMHostNetworkAdapterLLDP) { [PSCustomObject]@{ 'Adapter' = $VMHostNetworkAdapter.Device 'Chassis ID' = $VMHostNetworkAdapter.ChassisId 'Port ID' = $VMHostNetworkAdapter.PortId 'Management Address' = $VMHostNetworkAdapter.ManagementAddress 'Port Description' = $VMHostNetworkAdapter.PortDescription 'System Name' = $VMHostNetworkAdapter.SystemName } } $TableParams = @{ Name = "Network Adapter LLDP Information - $VMHost" ColumnWidths = 11, 19, 16, 19, 18, 17 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostLLDP | Table @TableParams } } } #endregion ESXi Host Link Layer Discovery Protocol #region ESXi Host VMkernel Adapaters Section -Style Heading5 'VMkernel Adapters' { Paragraph "The following section details the VMkernel adapter configuration for $VMHost" $VMkernelAdapters = $VMHost | Get-View | ForEach-Object -Process { $esx = $_ $netSys = Get-View -Id $_.ConfigManager.NetworkSystem $vnicMgr = Get-View -Id $_.ConfigManager.VirtualNicManager $netSys.NetworkInfo.Vnic | ForEach-Object -Process { $device = $_.Device [PSCustomObject]@{ 'Adapter' = $_.Device 'Network Label' = & { if ($_.Spec.Portgroup) { $script:pg = $_.Spec.Portgroup } else { $script:pg = Get-View -ViewType DistributedVirtualPortgroup -Property Name, Key -Filter @{'Key' = "$($_.Spec.DistributedVirtualPort.PortgroupKey)" } | Select-Object -ExpandProperty Name } $script:pg } 'Virtual Switch' = & { if ($_.Spec.Portgroup) { (Get-VirtualPortGroup -Standard -Name $script:pg -VMHost $VMHost).VirtualSwitchName } else { (Get-VDPortgroup -Name $script:pg).VDSwitch.Name | Select-Object -Unique } } 'TCP/IP Stack' = Switch ($_.Spec.NetstackInstanceKey) { 'defaultTcpipStack' { 'Default' } 'vSphereProvisioning' { 'Provisioning' } 'vmotion' { 'vMotion' } 'vxlan' { 'nsx-overlay' } 'hyperbus' { 'nsx-hyperbus' } $null { 'Not Applicable' } default { $_.Spec.NetstackInstanceKey } } 'MTU' = $_.Spec.Mtu 'MAC Address' = $_.Spec.Mac 'DHCP' = Switch ($_.Spec.Ip.Dhcp) { $true { 'Enabled' } $false { 'Disabled' } } 'IP Address' = & { if ($_.Spec.IP.IPAddress) { $script:ip = $_.Spec.IP.IPAddress } else { $script:ip = '--' } $script:ip } 'Subnet Mask' = & { if ($_.Spec.IP.SubnetMask) { $script:netmask = $_.Spec.IP.SubnetMask } else { $script:netmask = '--' } $script:netmask } 'Default Gateway' = Switch ($_.Spec.IpRouteSpec.IpRouteConfig.DefaultGateway) { $null { '--' } default { $_.Spec.IpRouteSpec.IpRouteConfig.DefaultGateway } } 'vMotion' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vmotion' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'Provisioning' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vSphereProvisioning' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'FT Logging' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'faultToleranceLogging' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'Management' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'management' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'vSphere Replication' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vSphereReplication' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'vSphere Replication NFC' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vSphereReplicationNFC' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'vSAN' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vsan' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'vSAN Witness' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vsanWitness' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } 'vSphere Backup NFC' = Switch ((($vnicMgr.Info.NetConfig | Where-Object { $_.NicType -eq 'vSphereBackupnNFC' }).SelectedVnic | ForEach-Object { $_ -match $device } ) -contains $true) { $true { 'Enabled' } $false { 'Disabled' } } } } } foreach ($VMkernelAdapter in ($VMkernelAdapters | Sort-Object 'Adapter')) { Section -Style Heading5 "$($VMkernelAdapter.Adapter)" { $TableParams = @{ Name = "VMkernel Adapter $($VMkernelAdapter.Adapter) - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMkernelAdapter | Table @TableParams } } } #endregion ESXi Host VMkernel Adapaters #region ESXi Host Standard Virtual Switches $VSSwitches = $VMHost | Get-VirtualSwitch -Standard | Sort-Object Name if ($VSSwitches) { #region Section Standard Virtual Switches Section -Style Heading5 'Standard Virtual Switches' { Paragraph "The following section details the standard virtual switch configuration for $VMHost." BlankLine $VSSwitchNicTeaming = $VSSwitches | Get-NicTeamingPolicy #region ESXi Host Standard Virtual Switch Properties $VSSProperties = foreach ($VSSwitchNicTeam in $VSSwitchNicTeaming) { [PSCustomObject]@{ 'Virtual Switch' = $VSSwitchNicTeam.VirtualSwitch 'MTU' = $VSSwitchNicTeam.VirtualSwitch.Mtu 'Number of Ports' = $VSSwitchNicTeam.VirtualSwitch.NumPorts 'Number of Ports Available' = $VSSwitchNicTeam.VirtualSwitch.NumPortsAvailable } } $TableParams = @{ Name = "Standard Virtual Switches - $VMHost" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VSSProperties | Table @TableParams #endregion ESXi Host Standard Virtual Switch Properties #region ESXi Host Virtual Switch Security Policy $VssSecurity = $VSSwitches | Get-SecurityPolicy if ($VssSecurity) { #region Virtual Switch Security Policy Section -Style Heading5 'Virtual Switch Security' { $VssSecurity = foreach ($VssSec in $VssSecurity) { [PSCustomObject]@{ 'Virtual Switch' = $VssSec.VirtualSwitch 'Promiscuous Mode' = Switch ($VssSec.AllowPromiscuous) { $true { 'Accept' } $false { 'Reject' } } 'MAC Address Changes' = Switch ($VssSec.MacChanges) { $true { 'Accept' } $false { 'Reject' } } 'Forged Transmits' = Switch ($VssSec.ForgedTransmits) { $true { 'Accept' } $false { 'Reject' } } } } $TableParams = @{ Name = "Virtual Switch Security Policy - $VMHost" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssSecurity | Sort-Object 'Virtual Switch' | Table @TableParams } #endregion Virtual Switch Security Policy } #endregion ESXi Host Virtual Switch Security Policy #region ESXi Host Virtual Switch Traffic Shaping Policy Section -Style Heading5 'Virtual Switch Traffic Shaping' { $VssTrafficShapingPolicy = foreach ($VSSwitch in $VSSwitches) { [PSCustomObject]@{ 'Virtual Switch' = $VSSwitch.Name 'Status' = Switch ($VSSwitch.ExtensionData.Spec.Policy.ShapingPolicy.Enabled) { $True { 'Enabled' } $False { 'Disabled' } } 'Average Bandwidth (kbit/s)' = $VSSwitch.ExtensionData.Spec.Policy.ShapingPolicy.AverageBandwidth 'Peak Bandwidth (kbit/s)' = $VSSwitch.ExtensionData.Spec.Policy.ShapingPolicy.PeakBandwidth 'Burst Size (KB)' = $VSSwitch.ExtensionData.Spec.Policy.ShapingPolicy.BurstSize } } $TableParams = @{ Name = "Virtual Switch Traffic Shaping Policy - $VMHost" ColumnWidths = 25, 15, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssTrafficShapingPolicy | Sort-Object 'Virtual Switch' | Table @TableParams } #endregion ESXi Host Virtual Switch Traffic Shaping Policy #region ESXi Host Virtual Switch Teaming & Failover $VssNicTeamingPolicy = $VSSwitches | Get-NicTeamingPolicy if ($VssNicTeamingPolicy) { #region Virtual Switch Teaming & Failover Section Section -Style Heading5 'Virtual Switch Teaming & Failover' { $VssNicTeaming = foreach ($VssNicTeam in $VssNicTeamingPolicy) { [PSCustomObject]@{ 'Virtual Switch' = $VssNicTeam.VirtualSwitch 'Load Balancing' = Switch ($VssNicTeam.LoadBalancingPolicy) { 'LoadbalanceSrcId' { 'Route based on the originating port ID' } 'LoadbalanceSrcMac' { 'Route based on source MAC hash' } 'LoadbalanceIP' { 'Route based on IP hash' } 'ExplicitFailover' { 'Explicit Failover' } default { $VssNicTeam.LoadBalancingPolicy } } 'Network Failure Detection' = Switch ($VssNicTeam.NetworkFailoverDetectionPolicy) { 'LinkStatus' { 'Link status only' } 'BeaconProbing' { 'Beacon probing' } default { $VssNicTeam.NetworkFailoverDetectionPolicy } } 'Notify Switches' = Switch ($VssNicTeam.NotifySwitches) { $true { 'Yes' } $false { 'No' } } 'Failback' = Switch ($VssNicTeam.FailbackEnabled) { $true { 'Yes' } $false { 'No' } } 'Active NICs' = ($VssNicTeam.ActiveNic | Sort-Object) -join [Environment]::NewLine 'Standby NICs' = ($VssNicTeam.StandbyNic | Sort-Object) -join [Environment]::NewLine 'Unused NICs' = ($VssNicTeam.UnusedNic | Sort-Object) -join [Environment]::NewLine } } $TableParams = @{ Name = "Virtual Switch Teaming & Failover - $VMHost" ColumnWidths = 20, 17, 12, 11, 10, 10, 10, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssNicTeaming | Sort-Object 'Virtual Switch' | Table @TableParams } #endregion Virtual Switch Teaming & Failover Section } #endregion ESXi Host Virtual Switch Teaming & Failover #region ESXi Host Virtual Switch Port Groups $VssPortgroups = $VSSwitches | Get-VirtualPortGroup -Standard if ($VssPortgroups) { Section -Style Heading5 'Virtual Switch Port Groups' { $VssPortgroups = foreach ($VssPortgroup in $VssPortgroups) { [PSCustomObject]@{ 'Port Group' = $VssPortgroup.Name 'VLAN ID' = $VssPortgroup.VLanId 'Virtual Switch' = $VssPortgroup.VirtualSwitchName '# of VMs' = ($VssPortgroup | Get-VM).Count } } $TableParams = @{ Name = "Virtual Switch Port Groups - $VMHost" ColumnWidths = 40, 10, 40, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssPortgroups | Sort-Object 'Port Group', 'VLAN ID', 'Virtual Switch' | Table @TableParams } #endregion ESXi Host Virtual Switch Port Groups #region ESXi Host Virtual Switch Port Group Security Policy $VssPortgroupSecurity = $VSSwitches | Get-VirtualPortGroup | Get-SecurityPolicy if ($VssPortgroupSecurity) { #region Virtual Port Group Security Policy Section Section -Style Heading5 'Virtual Switch Port Group Security' { $VssPortgroupSecurity = foreach ($VssPortgroupSec in $VssPortgroupSecurity) { [PSCustomObject]@{ 'Port Group' = $VssPortgroupSec.VirtualPortGroup 'Virtual Switch' = $VssPortgroupSec.virtualportgroup.virtualswitchname 'Promiscuous Mode' = Switch ($VssPortgroupSec.AllowPromiscuous) { $true { 'Accept' } $false { 'Reject' } } 'MAC Changes' = Switch ($VssPortgroupSec.MacChanges) { $true { 'Accept' } $false { 'Reject' } } 'Forged Transmits' = Switch ($VssPortgroupSec.ForgedTransmits) { $true { 'Accept' } $false { 'Reject' } } } } $TableParams = @{ Name = "Virtual Switch Port Group Security Policy - $VMHost" ColumnWidths = 27, 25, 16, 16, 16 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssPortgroupSecurity | Sort-Object 'Port Group', 'Virtual Switch' | Table @TableParams } #endregion Virtual Port Group Security Policy Section } #endregion ESXi Host Virtual Switch Port Group Security Policy #region ESXi Host Virtual Switch Port Group Traffic Shaping Policy Section -Style Heading5 'Virtual Switch Port Group Traffic Shaping' { $VssPortgroupTrafficShapingPolicy = foreach ($VssPortgroup in $VssPortgroups) { [PSCustomObject]@{ 'Port Group' = $VssPortgroup.Name 'Virtual Switch' = $VssPortgroup.VirtualSwitchName 'Status' = Switch ($VssPortgroup.ExtensionData.Spec.Policy.ShapingPolicy.Enabled) { $True { 'Enabled' } $False { 'Disabled' } $null { 'Inherited' } } 'Average Bandwidth (kbit/s)' = $VssPortgroup.ExtensionData.Spec.Policy.ShapingPolicy.AverageBandwidth 'Peak Bandwidth (kbit/s)' = $VssPortgroup.ExtensionData.Spec.Policy.ShapingPolicy.PeakBandwidth 'Burst Size (KB)' = $VssPortgroup.ExtensionData.Spec.Policy.ShapingPolicy.BurstSize } } $TableParams = @{ Name = "Virtual Switch Port Group Traffic Shaping Policy - $VMHost" ColumnWidths = 19, 19, 11, 17, 17, 17 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssPortgroupTrafficShapingPolicy | Sort-Object 'Port Group', 'Virtual Switch' | Table @TableParams } #endregion ESXi Host Virtual Switch Port Group Traffic Shaping Policy #region ESXi Host Virtual Switch Port Group Teaming & Failover $VssPortgroupNicTeaming = $VSSwitches | Get-VirtualPortGroup | Get-NicTeamingPolicy if ($VssPortgroupNicTeaming) { #region Virtual Switch Port Group Teaming & Failover Section Section -Style Heading5 'Virtual Switch Port Group Teaming & Failover' { $VssPortgroupNicTeaming = foreach ($VssPortgroupNicTeam in $VssPortgroupNicTeaming) { [PSCustomObject]@{ 'Port Group' = $VssPortgroupNicTeam.VirtualPortGroup 'Virtual Switch' = $VssPortgroupNicTeam.virtualportgroup.virtualswitchname 'Load Balancing' = Switch ($VssPortgroupNicTeam.LoadBalancingPolicy) { 'LoadbalanceSrcId' { 'Route based on the originating port ID' } 'LoadbalanceSrcMac' { 'Route based on source MAC hash' } 'LoadbalanceIP' { 'Route based on IP hash' } 'ExplicitFailover' { 'Explicit Failover' } default { $VssPortgroupNicTeam.LoadBalancingPolicy } } 'Network Failure Detection' = Switch ($VssPortgroupNicTeam.NetworkFailoverDetectionPolicy) { 'LinkStatus' { 'Link status only' } 'BeaconProbing' { 'Beacon probing' } default { $VssPortgroupNicTeam.NetworkFailoverDetectionPolicy } } 'Notify Switches' = Switch ($VssPortgroupNicTeam.NotifySwitches) { $true { 'Yes' } $false { 'No' } } 'Failback' = Switch ($VssPortgroupNicTeam.FailbackEnabled) { $true { 'Yes' } $false { 'No' } } 'Active NICs' = ($VssPortgroupNicTeam.ActiveNic | Sort-Object) -join [Environment]::NewLine 'Standby NICs' = ($VssPortgroupNicTeam.StandbyNic | Sort-Object) -join [Environment]::NewLine 'Unused NICs' = ($VssPortgroupNicTeam.UnusedNic | Sort-Object) -join [Environment]::NewLine } } $TableParams = @{ Name = "Virtual Switch Port Group Teaming & Failover - $VMHost" ColumnWidths = 12, 11, 11, 11, 11, 11, 11, 11, 11 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VssPortgroupNicTeaming | Sort-Object 'Port Group', 'Virtual Switch' | Table @TableParams } #endregion Virtual Switch Port Group Teaming & Failover Section } #endregion ESXi Host Virtual Switch Port Group Teaming & Failover } } #endregion Section Standard Virtual Switches } #endregion ESXi Host Standard Virtual Switches } #endregion ESXi Host Network Section #region ESXi Host Security Section Section -Style Heading4 'Security' { Paragraph "The following section details the host security configuration for $VMHost." #region ESXi Host Lockdown Mode if ($null -ne $VMHost.ExtensionData.Config.LockdownMode) { Section -Style Heading5 'Lockdown Mode' { $LockdownMode = [PSCustomObject]@{ 'Lockdown Mode' = Switch ($VMHost.ExtensionData.Config.LockdownMode) { 'lockdownDisabled' { 'Disabled' } 'lockdownNormal' { 'Enabled (Normal)' } 'lockdownStrict' { 'Enabled (Strict)' } default { $VMHost.ExtensionData.Config.LockdownMode } } } if ($Healthcheck.VMHost.LockdownMode) { $LockdownMode | Where-Object { $_.'Lockdown Mode' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Lockdown Mode' } $TableParams = @{ Name = "Lockdown Mode - $VMHost" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $LockdownMode | Table @TableParams } } #endregion ESXi Host Lockdown Mode #region ESXi Host Services Section -Style Heading5 'Services' { $VMHostServices = $VMHost | Get-VMHostService $Services = foreach ($VMHostService in $VMHostServices) { [PSCustomObject]@{ 'Service' = $VMHostService.Label 'Daemon' = Switch ($VMHostService.Running) { $true { 'Running' } $false { 'Stopped' } } 'Startup Policy' = Switch ($VMHostService.Policy) { 'automatic' { 'Start and stop with port usage' } 'on' { 'Start and stop with host' } 'off' { 'Start and stop manually' } default { $VMHostService.Policy } } } } if ($Healthcheck.VMHost.NTP) { $Services | Where-Object { ($_.'Service' -eq 'NTP Daemon') -and ($_.Daemon -eq 'Stopped') } | Set-Style -Style Critical -Property 'Daemon' $Services | Where-Object { ($_.'Service' -eq 'NTP Daemon') -and ($_.'Startup Policy' -ne 'Start and stop with host') } | Set-Style -Style Critical -Property 'Startup Policy' } if ($Healthcheck.VMHost.SSH) { $Services | Where-Object { ($_.'Service' -eq 'SSH') -and ($_.Daemon -eq 'Running') } | Set-Style -Style Warning -Property 'Daemon' $Services | Where-Object { ($_.'Service' -eq 'SSH') -and ($_.'Startup Policy' -ne 'Start and stop manually') } | Set-Style -Style Warning -Property 'Startup Policy' } if ($Healthcheck.VMHost.ESXiShell) { $Services | Where-Object { ($_.'Service' -eq 'ESXi Shell') -and ($_.Daemon -eq 'Running') } | Set-Style -Style Warning -Property 'Daemon' $Services | Where-Object { ($_.'Service' -eq 'ESXi Shell') -and ($_.'Startup Policy' -ne 'Start and stop manually') } | Set-Style -Style Warning -Property 'Startup Policy' } $TableParams = @{ Name = "Services - $VMHost" ColumnWidths = 40, 20, 40 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $Services | Sort-Object 'Service' | Table @TableParams } #endregion ESXi Host Services #region ESXi Host Advanced Detail Information if ($InfoLevel.VMHost -ge 4) { #region ESXi Host Firewall $VMHostFirewallExceptions = $VMHost | Get-VMHostFirewallException if ($VMHostFirewallExceptions) { #region Friewall Section Section -Style Heading5 'Firewall' { $VMHostFirewall = foreach ($VMHostFirewallException in $VMHostFirewallExceptions) { [PScustomObject]@{ 'Service' = $VMHostFirewallException.Name 'Status' = Switch ($VMHostFirewallException.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Incoming Ports' = $VMHostFirewallException.IncomingPorts 'Outgoing Ports' = $VMHostFirewallException.OutgoingPorts 'Protocols' = $VMHostFirewallException.Protocols 'Daemon' = Switch ($VMHostFirewallException.ServiceRunning) { $true { 'Running' } $false { 'Stopped' } $null { 'N/A' } default { $VMHostFirewallException.ServiceRunning } } } } $TableParams = @{ Name = "Firewall Configuration - $VMHost" ColumnWidths = 22, 12, 21, 21, 12, 12 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostFirewall | Sort-Object 'Service' | Table @TableParams } #endregion Friewall Section } #endregion ESXi Host Firewall #region ESXi Host Authentication $AuthServices = $VMHost | Get-VMHostAuthentication if ($AuthServices.DomainMembershipStatus) { Section -Style Heading5 'Authentication Services' { $AuthServices = $AuthServices | Select-Object Domain, @{L = 'Domain Membership'; E = { $_.DomainMembershipStatus } }, @{L = 'Trusted Domains'; E = { $_.TrustedDomains } } $TableParams = @{ Name = "Authentication Services - $VMHost" ColumnWidths = 25, 25, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $AuthServices | Table @TableParams } } #endregion ESXi Host Authentication } #endregion ESXi Host Advanced Detail Information } #endregion ESXi Host Security Section #region ESXi Host Virtual Machines Advanced Detail Information if ($InfoLevel.VMHost -ge 4) { $VMHostVMs = $VMHost | Get-VM | Sort-Object Name if ($VMHostVMs) { #region Virtual Machines Section Section -Style Heading4 'Virtual Machines' { Paragraph "The following section details the virtual machine configuration for $VMHost." BlankLine #region ESXi Host Virtual Machine Information $VMHostVMInfo = foreach ($VMHostVM in $VMHostVMs) { $VMHostVMView = $VMHostVM | Get-View [PSCustomObject]@{ 'Virtual Machine' = $VMHostVM.Name 'Power State' = Switch ($VMHostVM.PowerState) { 'PoweredOn' { 'On' } 'PoweredOff' { 'Off' } default { $VMHostVM.PowerState } } 'IP Address' = Switch ($VMHostVMView.Guest.IpAddress) { $null { '--' } default { $VMHostVMView.Guest.IpAddress } } 'CPUs' = $VMHostVM.NumCpu #'Cores per Socket' = $VMHostVM.CoresPerSocket 'Memory GB' = [math]::Round(($VMHostVM.memoryGB), 2) 'Provisioned GB' = [math]::Round(($VMHostVM.ProvisionedSpaceGB), 2) 'Used GB' = [math]::Round(($VMHostVM.UsedSpaceGB), 2) 'HW Version' = ($VMHostVM.HardwareVersion).Replace('vmx-', 'v') 'VM Tools Status' = Switch ($VMHostVM.ExtensionData.Guest.ToolsStatus) { 'toolsOld' { 'Old' } 'toolsOK' { 'OK' } 'toolsNotRunning' { 'Not Running' } 'toolsNotInstalled' { 'Not Installed' } default { $VMHostVM.ExtensionData.Guest.ToolsStatus } } } } if ($Healthcheck.VM.VMToolsStatus) { $VMHostVMInfo | Where-Object { $_.'VM Tools Status' -ne 'OK' } | Set-Style -Style Warning -Property 'VM Tools Status' } if ($Healthcheck.VM.PowerState) { $VMHostVMInfo | Where-Object { $_.'Power State' -ne 'On' } | Set-Style -Style Warning -Property 'Power State' } $TableParams = @{ Name = "Virtual Machines - $VMHost" ColumnWidths = 21, 8, 16, 9, 9, 9, 9, 9, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHostVMInfo | Table @TableParams #endregion ESXi Host Virtual Machine Information #region ESXi Host VM Startup/Shutdown Information $VMStartPolicy = $VMHost | Get-VMStartPolicy | Where-Object { $_.StartAction -ne 'None' } if ($VMStartPolicy) { #region VM Startup/Shutdown Section Section -Style Heading5 'VM Startup/Shutdown' { $VMStartPolicies = foreach ($VMStartPol in $VMStartPolicy) { [PSCustomObject]@{ 'Start Order' = $VMStartPol.StartOrder 'VM Name' = $VMStartPol.VirtualMachineName 'Startup' = Switch ($VMStartPol.StartAction) { 'PowerOn' { 'Enabled' } 'None' { 'Disabled' } default { $VMStartPol.StartAction } } 'Startup Delay' = "$($VMStartPol.StartDelay) seconds" 'VMware Tools' = Switch ($VMStartPol.WaitForHeartbeat) { $true { 'Continue if VMware Tools is started' } $false { 'Wait for startup delay' } } 'Shutdown Behavior' = Switch ($VMStartPol.StopAction) { 'PowerOff' { 'Power Off' } 'GuestShutdown' { 'Guest Shutdown' } default { $VMStartPol.StopAction } } 'Shutdown Delay' = "$($VMStartPol.StopDelay) seconds" } } $TableParams = @{ Name = "VM Startup/Shutdown Policy - $VMHost" ColumnWidths = 11, 34, 11, 11, 11, 11, 11 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMStartPolicies | Table @TableParams } #endregion VM Startup/Shutdown Section } #endregion ESXi Host VM Startup/Shutdown Information } #endregion Virtual Machines Section } } #endregion ESXi Host Virtual Machines Advanced Detail Information } #endregion VMHost Section } #endregion foreach VMHost Detailed Information loop } #endregion ESXi Host Detailed Information } #endregion Hosts Section } } #endregion ESXi VMHost Section #region Distributed Switch Section Write-PScriboMessage "Network InfoLevel set at $($InfoLevel.Network)." if ($InfoLevel.Network -ge 1) { # Create Distributed Switch Section if they exist $VDSwitches = Get-VDSwitch -Server $vCenter | Sort-Object Name if ($VDSwitches) { Section -Style Heading2 'Distributed Switches' { Paragraph "The following sections detail the configuration of distributed switches managed by vCenter Server $vCenterServerName." #region Distributed Switch Advanced Summary if ($InfoLevel.Network -le 2) { BlankLine $VDSInfo = foreach ($VDS in $VDSwitches) { [PSCustomObject]@{ 'VDSwitch' = $VDS.Name 'Datacenter' = $VDS.Datacenter 'Manufacturer' = $VDS.Vendor 'Version' = $VDS.Version '# of Uplinks' = $VDS.NumUplinkPorts '# of Ports' = $VDS.NumPorts '# of Hosts' = $VDS.ExtensionData.Summary.HostMember.Count '# of VMs' = $VDS.ExtensionData.Summary.VM.Count } } $TableParams = @{ Name = "Distributed Switch Summary - $($vCenterServerName)" ColumnWidths = 20, 18, 18, 10, 10, 8, 8, 8 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSInfo | Table @TableParams } #endregion Distributed Switch Advanced Summary #region Distributed Switch Detailed Information if ($InfoLevel.Network -ge 3) { # TODO: LACP, NetFlow, NIOC # TODO: Test Tags foreach ($VDS in ($VDSwitches)) { #region VDS Section Section -Style Heading3 $VDS { #region Distributed Switch General Properties $VDSwitchDetail = [PSCustomObject]@{ 'Distributed Switch' = $VDS.Name 'ID' = $VDS.Id 'Datacenter' = $VDS.Datacenter 'Manufacturer' = $VDS.Vendor 'Version' = $VDS.Version 'Number of Uplinks' = $VDS.NumUplinkPorts 'Number of Ports' = $VDS.NumPorts 'Number of Port Groups' = $VDS.ExtensionData.Summary.PortGroupName.Count 'Number of Hosts' = $VDS.ExtensionData.Summary.HostMember.Count 'Number of VMs' = $VDS.ExtensionData.Summary.VM.Count 'MTU' = $VDS.Mtu 'Network I/O Control' = Switch ($VDS.ExtensionData.Config.NetworkResourceManagementEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Discovery Protocol' = $VDS.LinkDiscoveryProtocol 'Discovery Protocol Operation' = $VDS.LinkDiscoveryProtocolOperation } <# $MemberProps = @{ 'InputObject' = $VDSwitchDetail 'MemberType' = 'NoteProperty' } if ($TagAssignments | Where-Object {$_.entity -eq $VDS}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $VDS}).Tag -join ',') } #> #region Network Advanced Detail Information if ($InfoLevel.Network -ge 4) { $VDSwitchDetail | ForEach-Object { $VDSwitchHosts = $VDS | Get-VMHost | Sort-Object Name Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Hosts' -Value ($VDSwitchHosts.Name -join ', ') $VDSwitchVMs = $VDS | Get-VM | Sort-Object Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Virtual Machines' -Value ($VDSwitchVMs.Name -join ', ') } } #endregion Network Advanced Detail Information $TableParams = @{ Name = "Distributed Switch General Properties - $VDS" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSwitchDetail | Table @TableParams #endregion Distributed Switch General Properties #region Distributed Switch Uplink Ports $VdsUplinks = $VDS | Get-VDPortgroup | Where-Object { $_.IsUplink -eq $true } | Get-VDPort if ($VdsUplinks) { Section -Style Heading4 'Distributed Switch Uplink Ports' { $VdsUplinkDetail = foreach ($VdsUplink in $VdsUplinks) { [PSCustomObject]@{ 'Distributed Switch' = $VdsUplink.Switch 'Host' = $VdsUplink.ProxyHost 'Uplink Name' = $VdsUplink.Name 'Physical Network Adapter' = $VdsUplink.ConnectedEntity 'Uplink Port Group' = $VdsUplink.Portgroup } } $TableParams = @{ Name = "Distributed Switch Uplink Ports - $VDS" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VdsUplinkDetail | Sort-Object 'Distributed Switch', 'Host', 'Uplink Name' | Table @TableParams } } #endregion Distributed Switch Uplink Ports #region Distributed Switch Security $VDSecurityPolicy = $VDS | Get-VDSecurityPolicy if ($VDSecurityPolicy) { Section -Style Heading4 'Distributed Switch Security' { $VDSecurityPolicyDetail = [PSCustomObject]@{ 'Distributed Switch' = $VDSecurityPolicy.VDSwitch 'Allow Promiscuous' = Switch ($VDSecurityPolicy.AllowPromiscuous) { $true { 'Accept' } $false { 'Reject' } } 'Forged Transmits' = Switch ($VDSecurityPolicy.ForgedTransmits) { $true { 'Accept' } $false { 'Reject' } } 'MAC Address Changes' = Switch ($VDSecurityPolicy.MacChanges) { $true { 'Accept' } $false { 'Reject' } } } $TableParams = @{ Name = "Distributed Switch Security - $VDS" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSecurityPolicyDetail | Table @TableParams } } #endregion Distributed Switch Security #region Distributed Switch Traffic Shaping $VDSTrafficShaping = @() $VDSTrafficShapingIn = $VDS | Get-VDTrafficShapingPolicy -Direction In $VDSTrafficShapingOut = $VDS | Get-VDTrafficShapingPolicy -Direction Out $VDSTrafficShaping += $VDSTrafficShapingIn $VDSTrafficShaping += $VDSTrafficShapingOut if ($VDSTrafficShapingIn -or $VDSTrafficShapingOut) { Section -Style Heading4 'Distributed Switch Traffic Shaping' { $VDSTrafficShapingDetail = foreach ($VDSTrafficShape in $VDSTrafficShaping) { [PSCustomObject]@{ 'Distributed Switch' = $VDSTrafficShape.VDSwitch 'Direction' = $VDSTrafficShape.Direction 'Status' = Switch ($VDSTrafficShape.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Average Bandwidth (kbit/s)' = $VDSTrafficShape.AverageBandwidth 'Peak Bandwidth (kbit/s)' = $VDSTrafficShape.PeakBandwidth 'Burst Size (KB)' = $VDSTrafficShape.BurstSize } } $TableParams = @{ Name = "Distributed Switch Traffic Shaping - $VDS" ColumnWidths = 25, 13, 11, 17, 17, 17 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSTrafficShapingDetail | Sort-Object 'Direction' | Table @TableParams } } #endregion Distributed Switch Traffic Shaping #region Distributed Switch Port Groups # TODO: Test Tags $VDSPortgroups = $VDS | Get-VDPortgroup if ($VDSPortgroups) { Section -Style Heading4 'Distributed Switch Port Groups' { $VDSPortgroupDetail = foreach ($VDSPortgroup in $VDSPortgroups) { [PSCustomObject]@{ 'Port Group' = $VDSPortgroup.Name 'Distributed Switch' = $VDSPortgroup.VDSwitch 'Datacenter' = $VDSPortgroup.Datacenter 'VLAN Configuration' = Switch ($VDSPortgroup.VlanConfiguration) { $null { '--' } default { $VDSPortgroup.VlanConfiguration } } 'Port Binding' = $VDSPortgroup.PortBinding '# of Ports' = $VDSPortgroup.NumPorts <# # Tags on portgroups cause Get-TagAssignments to error 'Tags' = & { if ($TagAssignments | Where-Object {$_.entity -eq $VDSPortgroup}) { ($TagAssignments | Where-Object {$_.entity -eq $VDSPortgroup}).Tag -join ',' } else { '--' } } #> } } $TableParams = @{ Name = "Distributed Switch Port Groups - $VDS" ColumnWidths = 20, 20, 20, 15, 15, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSPortgroupDetail | Sort-Object 'Port Group' | Table @TableParams } } #endregion Distributed Switch Port Groups #region Distributed Switch Port Group Security $VDSPortgroupSecurity = $VDS | Get-VDPortgroup | Get-VDSecurityPolicy if ($VDSPortgroupSecurity) { Section -Style Heading5 "Distributed Switch Port Group Security" { $VDSSecurityPolicies = foreach ($VDSSecurityPolicy in $VDSPortgroupSecurity) { [PSCustomObject]@{ 'Port Group' = $VDSSecurityPolicy.VDPortgroup 'Distributed Switch' = $VDS.Name 'Allow Promiscuous' = Switch ($VDSSecurityPolicy.AllowPromiscuous) { $true { 'Accept' } $false { 'Reject' } } 'Forged Transmits' = Switch ($VDSSecurityPolicy.ForgedTransmits) { $true { 'Accept' } $false { 'Reject' } } 'MAC Address Changes' = Switch ($VDSSecurityPolicy.MacChanges) { $true { 'Accept' } $false { 'Reject' } } } } $TableParams = @{ Name = "Distributed Switch Port Group Security - $VDS" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSSecurityPolicies | Sort-Object 'Port Group' | Table @TableParams } } #endregion Distributed Switch Port Group Security #region Distributed Switch Port Group Traffic Shaping $VDSPortgroupTrafficShaping = @() $VDSPortgroupTrafficShapingIn = $VDS | Get-VDPortgroup | Get-VDTrafficShapingPolicy -Direction In $VDSPortgroupTrafficShapingOut = $VDS | Get-VDPortgroup | Get-VDTrafficShapingPolicy -Direction Out $VDSPortgroupTrafficShaping += $VDSPortgroupTrafficShapingIn $VDSPortgroupTrafficShaping += $VDSPortgroupTrafficShapingOut if ($VDSPortgroupTrafficShaping) { Section -Style Heading5 "Distributed Switch Port Group Traffic Shaping" { $VDSPortgroupTrafficShapingDetail = foreach ($VDSPortgroupTrafficShape in $VDSPortgroupTrafficShaping) { [PSCustomObject]@{ 'Port Group' = $VDSPortgroupTrafficShape.VDPortgroup 'Distributed Switch' = $VDS.Name 'Direction' = $VDSPortgroupTrafficShape.Direction 'Status' = Switch ($VDSPortgroupTrafficShape.Enabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Average Bandwidth (kbit/s)' = $VDSPortgroupTrafficShape.AverageBandwidth 'Peak Bandwidth (kbit/s)' = $VDSPortgroupTrafficShape.PeakBandwidth 'Burst Size (KB)' = $VDSPortgroupTrafficShape.BurstSize } } $TableParams = @{ Name = "Distributed Switch Port Group Traffic Shaping - $VDS" ColumnWidths = 16, 16, 10, 10, 16, 16, 16 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSPortgroupTrafficShapingDetail | Sort-Object 'Port Group', 'Direction', 'Port Group' | Table @TableParams } } #endregion Distributed Switch Port Group Traffic Shaping #region Distributed Switch Port Group Teaming & Failover $VDUplinkTeamingPolicy = $VDS | Get-VDPortgroup | Get-VDUplinkTeamingPolicy if ($VDUplinkTeamingPolicy) { Section -Style Heading5 "Distributed Switch Port Group Teaming & Failover" { $VDSPortgroupNICTeaming = foreach ($VDUplink in $VDUplinkTeamingPolicy) { [PSCustomObject]@{ 'Port Group' = $VDUplink.VDPortgroup 'Distributed Switch' = $VDS.Name 'Load Balancing' = Switch ($VDUplink.LoadBalancingPolicy) { 'LoadbalanceSrcId' { 'Route based on the originating port ID' } 'LoadbalanceSrcMac' { 'Route based on source MAC hash' } 'LoadbalanceIP' { 'Route based on IP hash' } 'ExplicitFailover' { 'Explicit Failover' } default { $VDUplink.LoadBalancingPolicy } } 'Network Failure Detection' = Switch ($VDUplink.FailoverDetectionPolicy) { 'LinkStatus' { 'Link status only' } 'BeaconProbing' { 'Beacon probing' } default { $VDUplink.FailoverDetectionPolicy } } 'Notify Switches' = Switch ($VDUplink.NotifySwitches) { $true { 'Yes' } $false { 'No' } } 'Failback Enabled' = Switch ($VDUplink.EnableFailback) { $true { 'Yes' } $false { 'No' } } 'Active Uplinks' = $VDUplink.ActiveUplinkPort -join [Environment]::NewLine 'Standby Uplinks' = $VDUplink.StandbyUplinkPort -join [Environment]::NewLine 'Unused Uplinks' = $VDUplink.UnusedUplinkPort -join [Environment]::NewLine } } $TableParams = @{ Name = "Distributed Switch Port Group Teaming & Failover - $VDS" ColumnWidths = 12, 12, 12, 11, 10, 10, 11, 11, 11 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSPortgroupNICTeaming | Sort-Object 'Port Group' | Table @TableParams } } #endregion Distributed Switch Port Group Teaming & Failover #region Distributed Switch Private VLANs $VDSwitchPrivateVLANs = $VDS | Get-VDSwitchPrivateVlan if ($VDSwitchPrivateVLANs) { Section -Style Heading4 'Distributed Switch Private VLANs' { $VDSPvlan = foreach ($VDSwitchPrivateVLAN in $VDSwitchPrivateVLANs) { [PSCustomObject]@{ 'Primary VLAN ID' = $VDSwitchPrivateVLAN.PrimaryVlanId 'Private VLAN Type' = $VDSwitchPrivateVLAN.PrivateVlanType 'Secondary VLAN ID' = $VDSwitchPrivateVLAN.SecondaryVlanId } } $TableParams = @{ Name = "Distributed Switch Private VLANs - $VDS" ColumnWidths = 33, 34, 33 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VDSPvlan | Sort-Object 'Primary VLAN ID', 'Secondary VLAN ID' | Table @TableParams } } #endregion Distributed Switch Private VLANs } #endregion VDS Section } } #endregion Distributed Switch Detailed Information } } } #endregion Distributed Switch Section #region vSAN Section Write-PScriboMessage "vSAN InfoLevel set at $($InfoLevel.vSAN)." if (($InfoLevel.vSAN -ge 1) -and ($vCenter.Version -gt 6)) { $VsanClusters = Get-VsanClusterConfiguration -Server $vCenter | Where-Object { $_.vsanenabled -eq $true } | Sort-Object Name if ($VsanClusters) { Section -Style Heading2 'vSAN' { Paragraph "The following sections detail the configuration of vSAN managed by vCenter Server $vCenterServerName." #region vSAN Cluster Advanced Summary if ($InfoLevel.vSAN -le 2) { BlankLine $VsanClusterInfo = foreach ($VsanCluster in $VsanClusters) { [PSCustomObject]@{ 'Cluster' = $VsanCluster.Name 'vSAN Enabled' = $VsanCluster.VsanEnabled 'Stretched Cluster' = Switch ($VsanCluster.StretchedClusterEnabled) { $true { 'Yes' } $false { 'No' } } # TODO: Update for vSphere 7.0 U1 and higher - Space Efficiency: Deduplication & Compression, Compression Only, None 'Deduplication & Compression' = Switch ($VsanCluster.SpaceEfficiencyEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Encryption' = Switch ($VsanCluster.EncryptionEnabled) { $true { 'Enabled' } $false { 'Disabled' } $null { 'Disabled' } } 'Health Check' = Switch ($VsanCluster.HealthCheckEnabled) { $true { 'Enabled' } $false { 'Disabled' } } } } $TableParams = @{ Name = "vSAN Cluster Summary - $($vCenterServerName)" ColumnWidths = 25, 15, 15, 15, 15, 15 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanClusterInfo | Table @TableParams } #endregion vSAN Cluster Advanced Summary #region vSAN Cluster Detailed Information if ($InfoLevel.vSAN -ge 3) { foreach ($VsanCluster in $VsanClusters) { #region vSAN Cluster Section Section -Style Heading3 $VsanCluster.Name { $VsanDiskGroup = Get-VsanDiskGroup -Cluster $VsanCluster.Cluster $NumVsanDiskGroup = $VsanDiskGroup.Count $VsanDisk = Get-VsanDisk -VsanDiskGroup $VsanDiskGroup $VsanDiskFormat = $VsanDisk.DiskFormatVersion | Select-Object -First 1 -Unique $NumVsanSsd = ($VsanDisk | Where-Object { $_.IsSsd -eq $true }).Count $NumVsanHdd = ($VsanDisk | Where-Object { $_.IsSsd -eq $false }).Count if ($NumVsanHdd -gt 0) { $VsanClusterType = "Hybrid" } else { $VsanClusterType = "All Flash" } $VsanClusterDetail = [PSCustomObject]@{ 'Cluster' = $VsanCluster.Name 'ID' = $VsanCluster.Id 'Type' = $VsanClusterType 'Stretched Cluster' = Switch ($VsanCluster.StretchedClusterEnabled) { $true { 'Yes' } $false { 'No' } } 'Number of Hosts' = $VsanCluster.Cluster.ExtensionData.Host.Count 'Disk Format Version' = $VsanDiskFormat 'Total Number of Disks' = $NumVsanSsd + $NumVsanHdd 'Total Number of Disk Groups' = $NumVsanDiskGroup 'Disk Claim Mode' = $VsanCluster.VsanDiskClaimMode 'Deduplication & Compression' = Switch ($VsanCluster.SpaceEfficiencyEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Encryption' = Switch ($VsanCluster.EncryptionEnabled) { $true { 'Enabled' } $false { 'Disabled' } $null { 'Disabled' } } 'Health Check' = Switch ($VsanCluster.HealthCheckEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'HCL Last Updated' = $VsanCluster.TimeOfHclUpdate } if ($InfoLevel.vSAN -ge 4) { $VsanClusterDetail | Add-Member -MemberType NoteProperty -Name 'Hosts' -Value (($VsanDiskGroup.VMHost | Select-Object -Unique | Sort-Object Name) -join ', ') } $TableParams = @{ Name = "vSAN Configuration - $($VsanCluster.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanClusterDetail | Table @TableParams # TODO: vSAN Services Section -Style Heading4 'Disk Groups' { $VsanDiskGroups = foreach ($DiskGroup in $VsanDiskGroup) { $Disks = $DiskGroup | Get-VsanDisk [PSCustomObject]@{ 'Disk Group' = $DiskGroup.Uuid 'Host' = $Diskgroup.VMHost '# of Disks' = $Disks.Count 'State' = Switch ($DiskGroup.IsMounted) { $true { 'Mounted' } $False { 'Unmounted' } } 'Type' = Switch ($DiskGroup.DiskGroupType) { 'AllFlash' { 'All Flash' } default { $DiskGroup.DiskGroupType } } 'Disk Format Version' = $DiskGroup.DiskFormatVersion } } $TableParams = @{ Name = "vSAN Disk Groups - $($VsanCluster.Name)" ColumnWidths = 35, 28, 7, 10, 10, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanDiskGroups | Sort-Object Host | Table @TableParams } Section -Style Heading4 'Disks' { $vDisks = foreach ($Disk in $VsanDisk) { [PSCustomObject]@{ 'Disk' = $Disk.Name 'Name' = $Disk.ExtensionData.DisplayName 'Drive Type' = Switch ($Disk.IsSsd) { $true { 'Flash' } $false { 'HDD' } } 'Host' = $Disk.VsanDiskGroup.VMHost.Name 'Claimed As' = Switch ($Disk.IsCacheDisk) { $true { 'Cache' } $false { 'Capacity' } } 'Capacity' = "$([math]::Round($Disk.CapacityGB, 2)) GB" 'Capacity GB' = [math]::Round($Disk.CapacityGB, 2) 'Serial Number' = $Disk.ExtensionData.SerialNumber 'Vendor' = $Disk.ExtensionData.Vendor 'Model' = $Disk.ExtensionData.Model 'Disk Group' = $Disk.VsanDiskGroup.Uuid 'Disk Format Version' = $Disk.DiskFormatVersion } } if ($InfoLevel.vSAN -ge 4) { $vDisks | Sort-Object Host | ForEach-Object { Section -Style Heading4 "$($_.Disk) - $($_.Host)" { $TableParams = @{ Name = "Disk $($_.Disk) - $($_.Host)" List = $true Columns = 'Name', 'Drive Type', 'Claimed As', 'Capacity', 'Host', 'Disk Group', 'Serial Number', 'Vendor', 'Model', 'Disk Format Version' ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $_ | Table @TableParams } } } else { $TableParams = @{ Name = "vSAN Disks - $($VsanCluster.Name)" Columns = 'Disk', 'Drive Type', 'Claimed As', 'Capacity GB', 'Host', 'Disk Group' ColumnWidths = 21, 10, 10, 10, 21, 28 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $vDisks | Sort-Object Host | Table @TableParams } } $VsanIscsiTargets = Get-VsanIscsiTarget -Cluster $VsanCluster.Cluster -ErrorAction SilentlyContinue if ($VsanIscsiTargets) { Section -Style Heading4 'iSCSI Targets' { $VsanIscsiTargetInfo = foreach ($VsanIscsiTarget in $VsanIscsiTargets) { [PSCustomObject]@{ 'IQN' = $VsanIscsiTarget.IscsiQualifiedName 'Alias' = $VsanIscsiTarget.Name 'LUNs' = $VsanIscsiTarget.NumLuns 'Network Interface' = $VsanIscsiTarget.NetworkInterface 'I/O Owner Host' = $VsanIscsiTarget.IoOwnerVMHost 'TCP Port' = $VsanIscsiTarget.TcpPort 'Health' = $TextInfo.ToTitleCase($VsanIscsiTarget.VsanHealth) 'Storage Policy' = Switch ($VsanIscsiTarget.StoragePolicy.Name) { $null { '--' } default { $VsanIscsiTarget.StoragePolicy.Name } } 'Compliance Status' = $TextInfo.ToTitleCase($VsanIscsiTarget.SpbmComplianceStatus) 'Authentication' = $VsanIscsiTarget.AuthenticationType } } $TableParams = @{ Name = "vSAN iSCSI Targets - $($VsanCluster.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanIscsiTargetInfo | Table @TableParams } } $VsanIscsiLuns = Get-VsanIscsiLun -Cluster $VsanCluster.Cluster -ErrorAction SilentlyContinue | Sort-Object Name, LunId if ($VsanIscsiLuns) { Section -Style Heading4 'iSCSI LUNs' { $VsanIscsiLunInfo = foreach ($VsanIscsiLun in $VsanIscsiLuns) { [PSCustomobject]@{ 'LUN' = $VsanIscsiLun.Name 'LUN ID' = $VsanIscsiLun.LunId 'Capacity GB' = [math]::Round($VsanIscsiLun.CapacityGB, 2) 'Used Capacity GB' = [math]::Round($VsanIscsiLun.UsedCapacityGB, 2) 'State' = Switch ($VsanIscsiLun.IsOnline) { $true { 'Online' } $false { 'Offline' } } 'Health' = $TextInfo.ToTitleCase($VsanIscsiLun.VsanHealth) 'Storage Policy' = Switch ($VsanIscsiLun.StoragePolicy.Name) { $null { '--' } default { $VsanIscsiLun.StoragePolicy.Name } } 'Compliance Status' = $TextInfo.ToTitleCase($VsanIscsiLun.SpbmComplianceStatus) } } if ($InfoLevel.vSAN -ge 4) { $TableParams = @{ Name = "vSAN iSCSI LUNs - $($VsanCluster.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanIscsiLunInfo | Table @TableParams } else { $TableParams = @{ Name = "vSAN iSCSI LUNs - $($VsanCluster.Name)" ColumnWidths = 28 , 18, 18, 18, 18 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VsanIscsiLunInfo | Select-Object 'LUN', 'LUN ID', 'Capacity GB', 'Used Capacity GB', 'State' | Table @TableParams } } } } #endregion vSAN Cluster Section } } #endregion vSAN Cluster Detailed Information } } } #endregion vSAN Section #region Datastore Section Write-PScriboMessage "Datastore InfoLevel set at $($InfoLevel.Datastore)." if ($InfoLevel.Datastore -ge 1) { if ($Datastores) { Section -Style Heading2 'Datastores' { Paragraph "The following sections detail the configuration of datastores managed by vCenter Server $vCenterServerName." #region Datastore Infomative Information if ($InfoLevel.Datastore -le 2) { BlankLine $DatastoreInfo = foreach ($Datastore in $Datastores) { [PSCustomObject]@{ 'Datastore' = $Datastore.Name 'Type' = $Datastore.Type 'Version' = Switch ($Datastore.FileSystemVersion) { $null { '--' } default { $Datastore.FileSystemVersion } } '# of Hosts' = $Datastore.ExtensionData.Host.Count '# of VMs' = $Datastore.ExtensionData.VM.Count 'Total Capacity GB' = [math]::Round($Datastore.CapacityGB, 2) 'Used Capacity GB' = [math]::Round((($Datastore.CapacityGB) - ($Datastore.FreeSpaceGB)), 2) 'Free Space GB' = [math]::Round($Datastore.FreeSpaceGB, 2) '% Used' = [math]::Round((100 - (($Datastore.FreeSpaceGB) / ($Datastore.CapacityGB) * 100)), 2) } } if ($Healthcheck.Datastore.CapacityUtilization) { $DatastoreInfo | Where-Object { $_.'% Used' -ge 90 } | Set-Style -Style Critical -Property '% Used' $DatastoreInfo | Where-Object { $_.'% Used' -ge 75 -and $_.'% Used' -lt 90 } | Set-Style -Style Warning -Property '% Used' } $TableParams = @{ Name = "Datastore Summary - $($vCenterServerName)" ColumnWidths = 20, 9, 9, 9, 9, 11, 11, 11, 11 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DatastoreInfo | Sort-Object Datastore | Table @TableParams } #endregion Datastore Advanced Summary #region Datastore Detailed Information if ($InfoLevel.Datastore -ge 3) { foreach ($Datastore in $Datastores) { # TODO: Test Tags #region Datastore Section Section -Style Heading3 $Datastore.Name { $DatastoreDetail = [PSCustomObject]@{ 'Datastore' = $Datastore.Name 'ID' = $Datastore.Id 'Datacenter' = $Datastore.Datacenter 'Type' = $Datastore.Type 'Version' = Switch ($Datastore.FileSystemVersion) { $null { '--' } default { $Datastore.FileSystemVersion } } 'State' = $Datastore.State 'Number of Hosts' = $Datastore.ExtensionData.Host.Count 'Number of VMs' = $Datastore.ExtensionData.VM.Count 'Storage I/O Control' = Switch ($Datastore.StorageIOControlEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Congestion Threshold' = Switch ($Datastore.CongestionThresholdMillisecond) { $null { '--' } default { "$($Datastore.CongestionThresholdMillisecond) ms" } } 'Total Capacity' = "$([math]::Round($Datastore.CapacityGB, 2)) GB" 'Used Capacity' = "$([math]::Round((($Datastore.CapacityGB) - ($Datastore.FreeSpaceGB)), 2)) GB" 'Free Space' = "$([math]::Round($Datastore.FreeSpaceGB, 2)) GB" '% Used' = [math]::Round((100 - (($Datastore.FreeSpaceGB) / ($Datastore.CapacityGB) * 100)), 2) } if ($Healthcheck.Datastore.CapacityUtilization) { $DatastoreDetail | Where-Object { $_.'% Used' -ge 90 } | Set-Style -Style Critical -Property '% Used' $DatastoreDetail | Where-Object { $_.'% Used' -ge 75 -and $_.'% Used' -lt 90 } | Set-Style -Style Warning -Property '% Used' } $MemberProps = @{ 'InputObject' = $DatastoreDetail 'MemberType' = 'NoteProperty' } <# if ($TagAssignments | Where-Object {$_.entity -eq $Datastore}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $Datastore}).Tag -join ',') } #> #region Datastore Advanced Detailed Information if ($InfoLevel.Datastore -ge 4) { $DatastoreHosts = foreach ($DatastoreHost in $Datastore.ExtensionData.Host.Key) { $VMHostLookup."$($DatastoreHost.Type)-$($DatastoreHost.Value)" } Add-Member @MemberProps -Name 'Hosts' -Value (($DatastoreHosts | Sort-Object) -join ', ') $DatastoreVMs = foreach ($DatastoreVM in $Datastore.ExtensionData.VM) { $VMLookup."$($DatastoreVM.Type)-$($DatastoreVM.Value)" } Add-Member @MemberProps -Name 'Virtual Machines' -Value (($DatastoreVMs | Sort-Object) -join ', ') } #endregion Datastore Advanced Detailed Information $TableParams = @{ Name = "Datastore Configuration - $($Datastore.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DatastoreDetail | Sort-Object Datacenter, Name | Table @TableParams # Get VMFS volumes. Ignore local SCSILuns. if (($Datastore.Type -eq 'VMFS') -and ($Datastore.ExtensionData.Info.Vmfs.Local -eq $false)) { #region SCSI LUN Information Section Section -Style Heading4 'SCSI LUN Information' { $ScsiLuns = foreach ($DatastoreHost in $Datastore.ExtensionData.Host.Key) { $DiskName = $Datastore.ExtensionData.Info.Vmfs.Extent.DiskName $ScsiDeviceDetailProps = @{ 'VMHosts' = $VMHosts 'VMHostMoRef' = "$($DatastoreHost.Type)-$($DatastoreHost.Value)" 'DatastoreDiskName' = $DiskName } $ScsiDeviceDetail = Get-ScsiDeviceDetail @ScsiDeviceDetailProps [PSCustomObject]@{ 'Host' = $VMHostLookup."$($DatastoreHost.Type)-$($DatastoreHost.Value)" 'Canonical Name' = $DiskName 'Capacity GB' = $ScsiDeviceDetail.CapacityGB 'Vendor' = $ScsiDeviceDetail.Vendor 'Model' = $ScsiDeviceDetail.Model 'Is SSD' = $ScsiDeviceDetail.Ssd 'Multipath Policy' = $ScsiDeviceDetail.MultipathPolicy 'Paths' = $ScsiDeviceDetail.Paths } } $TableParams = @{ Name = "SCSI LUN Information - $($vCenterServerName)" ColumnWidths = 19, 19, 10, 10, 10, 10, 14, 8 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ScsiLuns | Sort-Object Host | Table @TableParams } #endregion SCSI LUN Information Section } } #endregion Datastore Section } } #endregion Datastore Detailed Information } } } #endregion Datastore Section #region Datastore Clusters Write-PScriboMessage "DSCluster InfoLevel set at $($InfoLevel.DSCluster)." if ($InfoLevel.DSCluster -ge 1) { $DSClusters = Get-DatastoreCluster -Server $vCenter if ($DSClusters) { #region Datastore Clusters Section Section -Style Heading2 'Datastore Clusters' { Paragraph "The following sections detail the configuration of datastore clusters managed by vCenter Server $vCenterServerName." #region Datastore Cluster Advanced Summary if ($InfoLevel.DSCluster -le 2) { BlankLine $DSClusterInfo = foreach ($DSCluster in $DSClusters) { [PSCustomObject]@{ 'Datastore Cluster' = $DSCluster.Name 'SDRS Automation Level' = Switch ($DSCluster.SdrsAutomationLevel) { 'FullyAutomated' { 'Fully Automated' } 'Manual' { 'Manual' } default { $DSCluster.SdrsAutomationLevel } } 'Space Utilization Threshold' = "$($DSCluster.SpaceUtilizationThresholdPercent)%" 'I/O Load Balance' = Switch ($DSCluster.IOLoadBalanceEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'I/O Latency Threshold' = "$($DSCluster.IOLatencyThresholdMillisecond) ms" } } if ($Healthcheck.DSCluster.SDRSAutomationLevelFullyAuto) { $DSClusterInfo | Where-Object { $_.'SDRS Automation Level' -ne 'Fully Automated' } | Set-Style -Style Warning -Property 'SDRS Automation Level' } $TableParams = @{ Name = "Datastore Cluster Configuration - $($DSCluster.Name)" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DSClusterInfo | Sort-Object Name | Table @TableParams } #endregion Datastore Cluster Advanced Summary #region Datastore Cluster Detailed Information if ($InfoLevel.DSCluster -ge 3) { foreach ($DSCluster in $DSClusters) { # TODO: Space Load Balance Config, IO Load Balance Config, Rules # TODO: Test Tags Section -Style Heading3 $DSCluster.Name { Paragraph ("The following table details the configuration " + "for datastore cluster $DSCluster.") BlankLine $DSClusterDetail = [PSCustomObject]@{ 'Datastore Cluster' = $DSCluster.Name 'ID' = $DSCluster.Id 'SDRS Automation Level' = Switch ($DSCluster.SdrsAutomationLevel) { 'FullyAutomated' { 'Fully Automated' } 'Manual' { 'Manual' } default { $DSCluster.SdrsAutomationLevel } } 'Space Utilization Threshold' = "$($DSCluster.SpaceUtilizationThresholdPercent)%" 'I/O Load Balance' = Switch ($DSCluster.IOLoadBalanceEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'I/O Latency Threshold' = "$($DSCluster.IOLatencyThresholdMillisecond) ms" 'Capacity' = "$([math]::Round($DSCluster.CapacityGB, 2)) GB" 'Free Space' = "$([math]::Round($DSCluster.FreeSpaceGB, 2)) GB" '% Used' = Switch ($DSCluster.CapacityGB -gt 0) { $true { [math]::Round((100 - (($DSCluster.FreeSpaceGB) / ($DSCluster.CapacityGB) * 100)), 2) } $false { '0' } } } $MemberProps = @{ 'InputObject' = $DSClusterDetail 'MemberType' = 'NoteProperty' } <# if ($TagAssignments | Where-Object {$_.entity -eq $DSCluster}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $DSCluster}).Tag -join ',') } #> if ($Healthcheck.DSCluster.CapacityUtilization) { $DSClusterDetail | Where-Object { $_.'% Used' -ge 90 } | Set-Style -Style Critical -Property '% Used' $DSClusterDetail | Where-Object { $_.'% Used' -ge 75 -and $_.'% Used' -lt 90 } | Set-Style -Style Critical -Property '% Used' } if ($Healthcheck.DSCluster.SDRSAutomationLevel) { $DSClusterDetail | Where-Object { $_.'SDRS Automation Level' -ne 'Fully Automated' } | Set-Style -Style Warning -Property 'SDRS Automation Level' } $TableParams = @{ Name = "Datastore Cluster Configuration - $($DSCluster.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $DSClusterDetail | Table @TableParams #region SDRS VM Overrides $StoragePodProps = @{ 'ViewType' = 'StoragePod' 'Filter' = @{'Name' = $DSCluster.Name } } $StoragePod = Get-View @StoragePodProps if ($StoragePod) { $PodConfig = $StoragePod.PodStorageDrsEntry.StorageDrsConfig.PodConfig # Set default automation value variables Switch ($PodConfig.DefaultVmBehavior) { "automated" { $DefaultVmBehavior = "Default (Fully Automated)" } "manual" { $DefaultVmBehavior = "Default (No Automation (Manual Mode))" } } Switch ($PodConfig.DefaultIntraVmAffinity) { $true { $DefaultIntraVmAffinity = "Default (Yes)" } $false { $DefaultIntraVmAffinity = "Default (No)" } } $VMOverrides = $StoragePod.PodStorageDrsEntry.StorageDrsConfig.VmConfig | Where-Object { -not ( ($null -eq $_.Enabled) -and ($null -eq $_.IntraVmAffinity) ) } } if ($VMOverrides) { $VMOverrideDetails = foreach ($Override in $VMOverrides) { [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($Override.Vm.Type)-$($Override.Vm.Value)" 'SDRS Automation Level' = Switch ($Override.Enabled) { $true { 'Fully Automated' } $false { 'Disabled' } $null { $DefaultVmBehavior } } 'Keep VMDKs Together' = Switch ($Override.IntraVmAffinity) { $true { 'Yes' } $false { 'No' } $null { $DefaultIntraVmAffinity } } } } Section -Style Heading4 'SDRS VM Overrides' { $TableParams = @{ Name = "SDRS VM Overrides - $($DSCluster.Name)" ColumnWidths = 50, 30, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMOverrideDetails | Sort-Object 'Virtual Machine' | Table @TableParams } } #endregion SDRS VM Overrides } } } #endregion Datastore Cluster Detailed Information } #endregion Datastore Clusters Section } } #endregion Datastore Clusters #region Virtual Machine Section Write-PScriboMessage "VM InfoLevel set at $($InfoLevel.VM)." if ($InfoLevel.VM -ge 1) { if ($VMs) { Section -Style Heading2 'Virtual Machines' { Paragraph "The following sections detail the configuration of virtual machines managed by vCenter Server $vCenterServerName." #region Virtual Machine Summary Information if ($InfoLevel.VM -eq 1) { BlankLine $VMSummary = [PSCustomObject]@{ 'Total VMs' = $VMs.Count 'Total vCPUs' = ($VMs | Measure-Object -Property NumCpu -Sum).Sum 'Total Memory' = "$([math]::Round(($VMs | Measure-Object -Property MemoryGB -Sum).Sum, 2)) GB" 'Total Provisioned Space' = "$([math]::Round(($VMs | Measure-Object -Property ProvisionedSpaceGB -Sum).Sum, 2)) GB" 'Total Used Space' = "$([math]::Round(($VMs | Measure-Object -Property UsedSpaceGB -Sum).Sum, 2)) GB" 'VMs Powered On' = ($VMs | Where-Object { $_.PowerState -eq 'PoweredOn' }).Count 'VMs Powered Off' = ($VMs | Where-Object { $_.PowerState -eq 'PoweredOff' }).Count 'VMs Orphaned' = ($VMs | Where-Object { $_.ExtensionData.Runtime.ConnectionState -eq 'Orphaned' }).Count 'VMs Inaccessible' = ($VMs | Where-Object { $_.ExtensionData.Runtime.ConnectionState -eq 'Inaccessible' }).Count 'VMs Suspended' = ($VMs | Where-Object { $_.PowerState -eq 'Suspended' }).Count 'VMs with Snapshots' = ($VMs | Where-Object { $_.ExtensionData.Snapshot }).Count 'Guest Operating System Types' = (($VMs | Get-View).Summary.Config.GuestFullName | Select-Object -Unique).Count 'VM Tools OK' = ($VMs | Where-Object { $_.ExtensionData.Guest.ToolsStatus -eq 'toolsOK' }).Count 'VM Tools Old' = ($VMs | Where-Object { $_.ExtensionData.Guest.ToolsStatus -eq 'toolsOld' }).Count 'VM Tools Not Running' = ($VMs | Where-Object { $_.ExtensionData.Guest.ToolsStatus -eq 'toolsNotRunning' }).Count 'VM Tools Not Installed' = ($VMs | Where-Object { $_.ExtensionData.Guest.ToolsStatus -eq 'toolsNotInstalled' }).Count } $TableParams = @{ Name = "VM Summary - $($vCenterServerName)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMSummary | Table @TableParams } #endregion Virtual Machine Summary Information #region Virtual Machine Advanced Summary if ($InfoLevel.VM -eq 2) { BlankLine $VMSnapshotList = $VMs.Extensiondata.Snapshot.RootSnapshotList $VMInfo = foreach ($VM in $VMs) { $VMView = $VM | Get-View [PSCustomObject]@{ 'Virtual Machine' = $VM.Name 'Power State' = Switch ($VM.PowerState) { 'PoweredOn' { 'On' } 'PoweredOff' { 'Off' } default { $VM.PowerState } } 'IP Address' = Switch ($VMView.Guest.IpAddress) { $null { '--' } default { $VMView.Guest.IpAddress } } 'vCPUs' = $VM.NumCpu 'Memory GB' = [math]::Round(($VM.MemoryGB), 0) 'Provisioned GB' = [math]::Round(($VM.ProvisionedSpaceGB), 2) 'Used GB' = [math]::Round(($VM.UsedSpaceGB), 2) 'HW Version' = ($VM.HardwareVersion).Replace('vmx-', 'v') 'VM Tools Status' = Switch ($VMView.Guest.ToolsStatus) { 'toolsOld' { 'Old' } 'toolsOK' { 'OK' } 'toolsNotRunning' { 'Not Running' } 'toolsNotInstalled' { 'Not Installed' } default { $VMView.Guest.ToolsStatus } } } } if ($Healthcheck.VM.VMToolsStatus) { $VMInfo | Where-Object { $_.'VM Tools Status' -ne 'OK' } | Set-Style -Style Warning -Property 'VM Tools Status' } if ($Healthcheck.VM.PowerState) { $VMInfo | Where-Object { $_.'Power State' -ne 'On' } | Set-Style -Style Warning -Property 'Power State' } $TableParams = @{ Name = "VM Advanced Summary - $($vCenterServerName)" ColumnWidths = 21, 8, 16, 9, 9, 9, 9, 9, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMInfo | Table @TableParams #region VM Snapshot Information if ($VMSnapshotList -and $Options.ShowVMSnapshots) { Section -Style Heading3 'Snapshots' { $VMSnapshotInfo = foreach ($VMSnapshot in $VMSnapshotList) { [PSCustomObject]@{ 'Virtual Machine' = $VMLookup."$($VMSnapshot.VM)" 'Snapshot Name' = $VMSnapshot.Name 'Description' = $VMSnapshot.Description 'Days Old' = ((Get-Date).ToUniversalTime() - $VMSnapshot.CreateTime).Days } } if ($Healthcheck.VM.VMSnapshots) { $VMSnapshotInfo | Where-Object { $_.'Days Old' -ge 7 } | Set-Style -Style Warning $VMSnapshotInfo | Where-Object { $_.'Days Old' -ge 14 } | Set-Style -Style Critical } $TableParams = @{ Name = "VM Snapshot Summary - $($vCenterServerName)" ColumnWidths = 30, 30, 30, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMSnapshotInfo | Table @TableParams } } #endregion VM Snapshot Information } #endregion Virtual Machine Advanced Summary #region Virtual Machine Detailed Information # TODO: Test Tags if ($InfoLevel.VM -ge 3) { if ($UserRole.Privilege -contains 'StorageProfile.View') { $VMSpbmConfig = Get-SpbmEntityConfiguration -VM ($VMs) | Where-Object { $null -ne $_.StoragePolicy } } else { Write-PScriboMessage "Insufficient user privileges to report VM storage policies. Please ensure the user account has the 'Storage Profile > View' privilege assigned." } if ($InfoLevel.VM -ge 4) { $VMHardDisks = Get-HardDisk -VM ($VMs) -Server $vCenter } foreach ($VM in $VMs) { Section -Style Heading3 $VM.name { $VMUptime = Get-Uptime -VM $VM $VMSpbmPolicy = $VMSpbmConfig | Where-Object { $_.entity -eq $vm } $VMView = $VM | Get-View $VMSnapshotList = $vmview.Snapshot.RootSnapshotList $VMDetail = [PSCustomObject]@{ 'Virtual Machine' = $VM.Name 'ID' = $VM.Id 'Operating System' = $VMView.Summary.Config.GuestFullName 'Hardware Version' = ($VM.HardwareVersion).Replace('vmx-', 'v') 'Power State' = Switch ($VM.PowerState) { 'PoweredOn' { 'On' } 'PoweredOff' { 'Off' } default { $TextInfo.ToTitleCase($VM.PowerState) } } 'Connection State' = $TextInfo.ToTitleCase($VM.ExtensionData.Runtime.ConnectionState) 'VM Tools Status' = Switch ($VMView.Guest.ToolsStatus) { 'toolsOld' { 'Old' } 'toolsOK' { 'OK' } 'toolsNotRunning' { 'Not Running' } 'toolsNotInstalled' { 'Not Installed' } default { $TextInfo.ToTitleCase($VMView.Guest.ToolsStatus) } } 'Fault Tolerance State' = Switch ($VMView.Runtime.FaultToleranceState) { 'notConfigured' { 'Not Configured' } 'needsSecondary' { 'Needs Secondary' } 'running' { 'Running' } 'disabled' { 'Disabled' } 'starting' { 'Starting' } 'enabled' { 'Enabled' } default { $TextInfo.ToTitleCase($VMview.Runtime.FaultToleranceState) } } 'Host' = $VM.VMHost.Name 'Parent' = $VM.VMHost.Parent.Name 'Parent Folder' = $VM.Folder.Name 'Parent Resource Pool' = $VM.ResourcePool.Name 'vCPUs' = $VM.NumCpu 'Cores per Socket' = $VM.CoresPerSocket 'CPU Shares' = "$($VM.VMResourceConfiguration.CpuSharesLevel) / $($VM.VMResourceConfiguration.NumCpuShares)" 'CPU Reservation' = $VM.VMResourceConfiguration.CpuReservationMhz 'CPU Limit' = "$($VM.VMResourceConfiguration.CpuReservationMhz) MHz" 'CPU Hot Add' = Switch ($VMView.Config.CpuHotAddEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'CPU Hot Remove' = Switch ($VMView.Config.CpuHotRemoveEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Memory Allocation' = "$([math]::Round(($VM.memoryGB), 2)) GB" 'Memory Shares' = "$($VM.VMResourceConfiguration.MemSharesLevel) / $($VM.VMResourceConfiguration.NumMemShares)" 'Memory Hot Add' = Switch ($VMView.Config.MemoryHotAddEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'vNICs' = $VMView.Summary.Config.NumEthernetCards 'DNS Name' = if ($VMView.Guest.HostName) { $VMView.Guest.HostName } else { '--' } 'Networks' = if ($VMView.Guest.Net.Network) { (($VMView.Guest.Net | Where-Object { $null -ne $_.Network } | Select-Object Network | Sort-Object Network).Network -join ', ') } else { '--' } 'IP Address' = if ($VMView.Guest.Net.IpAddress) { (($VMView.Guest.Net | Where-Object { ($null -ne $_.Network) -and ($null -ne $_.IpAddress) } | Select-Object IpAddress | Sort-Object IpAddress).IpAddress -join ', ') } else { '--' } 'MAC Address' = if ($VMView.Guest.Net.MacAddress) { (($VMView.Guest.Net | Where-Object { $null -ne $_.Network } | Select-Object -Property MacAddress).MacAddress -join ', ') } else { '--' } 'vDisks' = $VMView.Summary.Config.NumVirtualDisks 'Provisioned Space' = "$([math]::Round(($VM.ProvisionedSpaceGB), 2)) GB" 'Used Space' = "$([math]::Round(($VM.UsedSpaceGB), 2)) GB" 'Changed Block Tracking' = Switch ($VMView.Config.ChangeTrackingEnabled) { $true { 'Enabled' } $false { 'Disabled' } } 'Storage Based Policy' = Switch ($VMSpbmPolicy.StoragePolicy.Name) { $null { '--' } default { $TextInfo.ToTitleCase($VMSpbmPolicy.StoragePolicy.Name) } } 'Storage Based Policy Compliance' = Switch ($VMSpbmPolicy.ComplianceStatus) { $null { '--' } 'compliant' { 'Compliant' } 'nonCompliant' { 'Non Compliant' } 'unknown' { 'Unknown' } default { $TextInfo.ToTitleCase($VMSpbmPolicy.ComplianceStatus) } } } $MemberProps = @{ 'InputObject' = $VMDetail 'MemberType' = 'NoteProperty' } #if ($VMView.Config.CreateDate) { # Add-Member @MemberProps -Name 'Creation Date' -Value ($VMView.Config.CreateDate).ToLocalTime() #} <# if ($TagAssignments | Where-Object {$_.entity -eq $VM}) { Add-Member @MemberProps -Name 'Tags' -Value $(($TagAssignments | Where-Object {$_.entity -eq $VM}).Tag -join ',') } #> if ($VM.Notes) { Add-Member @MemberProps -Name 'Notes' -Value $VM.Notes } if ($VMView.Runtime.BootTime) { Add-Member @MemberProps -Name 'Boot Time' -Value ($VMView.Runtime.BootTime).ToLocalTime() } if ($VMUptime.UptimeDays) { Add-Member @MemberProps -Name 'Uptime Days' -Value $VMUptime.UptimeDays } #region VM Health Checks if ($Healthcheck.VM.VMToolsStatus) { $VMDetail | Where-Object { $_.'VM Tools Status' -ne 'OK' } | Set-Style -Style Warning -Property 'VM Tools Status' } if ($Healthcheck.VM.PowerState) { $VMDetail | Where-Object { $_.'Power State' -ne 'On' } | Set-Style -Style Warning -Property 'Power State' } if ($Healthcheck.VM.ConnectionState) { $VMDetail | Where-Object { $_.'Connection State' -ne 'Connected' } | Set-Style -Style Critical -Property 'Connection State' } if ($Healthcheck.VM.CpuHotAdd) { $VMDetail | Where-Object { $_.'CPU Hot Add' -eq 'Enabled' } | Set-Style -Style Warning -Property 'CPU Hot Add' } if ($Healthcheck.VM.CpuHotRemove) { $VMDetail | Where-Object { $_.'CPU Hot Remove' -eq 'Enabled' } | Set-Style -Style Warning -Property 'CPU Hot Remove' } if ($Healthcheck.VM.MemoryHotAdd) { $VMDetail | Where-Object { $_.'Memory Hot Add' -eq 'Enabled' } | Set-Style -Style Warning -Property 'Memory Hot Add' } if ($Healthcheck.VM.ChangeBlockTracking) { $VMDetail | Where-Object { $_.'Changed Block Tracking' -eq 'Disabled' } | Set-Style -Style Warning -Property 'Changed Block Tracking' } if ($Healthcheck.VM.SpbmPolicyCompliance) { $VMDetail | Where-Object { $_.'Storage Based Policy Compliance' -eq 'Unknown' } | Set-Style -Style Warning -Property 'Storage Based Policy Compliance' $VMDetail | Where-Object { $_.'Storage Based Policy Compliance' -eq 'Non Compliant' } | Set-Style -Style Critical -Property 'Storage Based Policy Compliance' } #endregion VM Health Checks $TableParams = @{ Name = "VM Configuration - $($VM.Name)" List = $true ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMDetail | Table @TableParams if ($InfoLevel.VM -ge 4) { $VMnics = $VM.Guest.Nics | Where-Object { $null -ne $_.Device } | Sort-Object Device $VMHdds = $VMHardDisks | Where-Object { $_.ParentId -eq $VM.Id } | Sort-Object Name $SCSIControllers = $VMView.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -match "SCSI Controller" } $VMGuestVols = $VM.Guest.Disks | Sort-Object Path if ($VMnics) { Section -Style Heading4 "Network Adapters" { $VMnicInfo = foreach ($VMnic in $VMnics) { [PSCustomObject]@{ 'Adapter' = $VMnic.Device 'Connected' = $VMnic.Connected 'Network Name' = Switch -wildcard ($VMnic.Device.NetworkName) { 'dvportgroup*' { $VDPortgroupLookup."$($VMnic.Device.NetworkName)" } default { $VMnic.Device.NetworkName } } 'Adapter Type' = $VMnic.Device.Type 'IP Address' = $VMnic.IpAddress -join [Environment]::NewLine 'MAC Address' = $VMnic.Device.MacAddress } } $TableParams = @{ Name = "Network Adapters - $($VM.Name)" ColumnWidths = 20, 12, 16, 12, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMnicInfo | Table @TableParams } } if ($SCSIControllers) { Section -Style Heading4 "SCSI Controllers" { $VMScsiControllers = foreach ($VMSCSIController in $SCSIControllers) { [PSCustomObject]@{ 'Device' = $VMSCSIController.DeviceInfo.Label 'Controller Type' = $VMSCSIController.DeviceInfo.Summary 'Bus Sharing' = Switch ($VMSCSIController.SharedBus) { 'noSharing' { 'None' } default { $VMSCSIController.SharedBus } } } } $TableParams = @{ Name = "SCSI Controllers - $($VM.Name)" ColumnWidths = 33, 34, 33 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMScsiControllers | Sort-Object 'Device' | Table @TableParams } } if ($VMHdds) { Section -Style Heading4 "Hard Disks" { If ($InfoLevel.VM -eq 4) { $VMHardDiskInfo = foreach ($VMHdd in $VMHdds) { $SCSIDevice = $VMView.Config.Hardware.Device | Where-Object { $_.Key -eq $VMHdd.ExtensionData.Key -and $_.Backing.FileName -eq $VMHdd.FileName } $SCSIController = $SCSIControllers | Where-Object { $SCSIDevice.ControllerKey -eq $_.Key } [PSCustomObject]@{ 'Disk' = $VMHdd.Name 'Datastore' = $VMHdd.FileName.Substring($VMHdd.Filename.IndexOf("[") + 1, $VMHdd.Filename.IndexOf("]") - 1) 'Capacity' = "$([math]::Round(($VMHdd.CapacityGB), 2)) GB" 'Disk Provisioning' = Switch ($VMHdd.StorageFormat) { 'EagerZeroedThick' { 'Thick Eager Zeroed' } 'LazyZeroedThick' { 'Thick Lazy Zeroed' } $null { '--' } default { $VMHdd.StorageFormat } } 'Disk Type' = Switch ($VMHdd.DiskType) { 'RawPhysical' { 'Physical RDM' } 'RawVirtual' { "Virtual RDM" } 'Flat' { 'VMDK' } default { $VMHdd.DiskType } } 'Disk Mode' = Switch ($VMHdd.Persistence) { 'IndependentPersistent' { 'Independent - Persistent' } 'IndependentNonPersistent' { 'Independent - Nonpersistent' } 'Persistent' { 'Dependent' } default { $VMHdd.Persistence } } } } $TableParams = @{ Name = "Hard Disk Configuration - $($VM.Name)" ColumnWidths = 15, 25, 15, 15, 15, 15 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHardDiskInfo | Table @TableParams } else { foreach ($VMHdd in $VMHdds) { Section -Style Heading5 "$($VMHdd.Name)" { $SCSIDevice = $VMView.Config.Hardware.Device | Where-Object { $_.Key -eq $VMHdd.ExtensionData.Key -and $_.Backing.FileName -eq $VMHdd.FileName } $SCSIController = $SCSIControllers | Where-Object { $SCSIDevice.ControllerKey -eq $_.Key } $VMHardDiskInfo = [PSCustomObject]@{ 'Datastore' = $VMHdd.FileName.Substring($VMHdd.Filename.IndexOf("[") + 1, $VMHdd.Filename.IndexOf("]") - 1) 'Capacity' = "$([math]::Round(($VMHdd.CapacityGB), 2)) GB" 'Disk Path' = $VMHdd.Filename.Substring($VMHdd.Filename.IndexOf("]") + 2) 'Disk Shares' = "$($TextInfo.ToTitleCase($VMHdd.ExtensionData.Shares.Level)) / $($VMHdd.ExtensionData.Shares.Shares)" 'Disk Limit IOPs' = Switch ($VMHdd.ExtensionData.StorageIOAllocation.Limit) { '-1' { 'Unlimited' } default { $VMHdd.ExtensionData.StorageIOAllocation.Limit } } 'Disk Provisioning' = Switch ($VMHdd.StorageFormat) { 'EagerZeroedThick' { 'Thick Eager Zeroed' } 'LazyZeroedThick' { 'Thick Lazy Zeroed' } $null { '--' } default { $VMHdd.StorageFormat } } 'Disk Type' = Switch ($VMHdd.DiskType) { 'RawPhysical' { 'Physical RDM' } 'RawVirtual' { "Virtual RDM" } 'Flat' { 'VMDK' } default { $VMHdd.DiskType } } 'Disk Mode' = Switch ($VMHdd.Persistence) { 'IndependentPersistent' { 'Independent - Persistent' } 'IndependentNonPersistent' { 'Independent - Nonpersistent' } 'Persistent' { 'Dependent' } default { $VMHdd.Persistence } } 'SCSI Controller' = $SCSIController.DeviceInfo.Label 'SCSI Address' = "$($SCSIController.BusNumber):$($VMHdd.ExtensionData.UnitNumber)" } $TableParams = @{ Name = "Hard Disk $($VMHdd.Name) - $($VM.Name)" List = $true ColumnWidths = 25, 75 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMHardDiskInfo | Table @TableParams } } } } } if ($VMGuestVols) { Section -Style Heading4 "Guest Volumes" { $VMGuestDiskInfo = foreach ($VMGuestVol in $VMGuestVols) { [PSCustomObject]@{ 'Path' = $VMGuestVol.Path 'Capacity' = "$([math]::Round(($VMGuestVol.CapacityGB), 2)) GB" 'Used Space' = "$([math]::Round((($VMGuestVol.CapacityGB) - ($VMGuestVol.FreeSpaceGB)), 2)) GB" 'Free Space' = "$([math]::Round($VMGuestVol.FreeSpaceGB, 2)) GB" } } $TableParams = @{ Name = "Guest Volumes - $($VM.Name)" ColumnWidths = 25, 25, 25, 25 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMGuestDiskInfo | Table @TableParams } } } if ($VMSnapshotList -and $Options.ShowVMSnapshots) { Section -Style Heading4 "Snapshots" { $VMSnapshots = foreach ($VMSnapshot in $VMSnapshotList) { [PSCustomObject]@{ 'Snapshot Name' = $VMSnapshot.Name 'Description' = $VMSnapshot.Description 'Days Old' = ((Get-Date).ToUniversalTime() - $VMSnapshot.CreateTime).Days } } if ($Healthcheck.VM.VMSnapshots) { $VMSnapshots | Where-Object { $_.'Days Old' -ge 7 } | Set-Style -Style Warning $VMSnapshots | Where-Object { $_.'Days Old' -ge 14 } | Set-Style -Style Critical } $TableParams = @{ Name = "VM Snapshots - $($VM.Name)" ColumnWidths = 45, 45, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VMSnapshots | Table @TableParams } } } } } #endregion Virtual Machine Detailed Information } } } #endregion Virtual Machine Section #region VMware Update Manager Section Write-PScriboMessage "VUM InfoLevel set at $($InfoLevel.VUM)." if (($InfoLevel.VUM -ge 1) -and ($VumServer.Name)) { if ("Desktop" -eq $PSVersionTable.PsEdition) { $VUMBaselines = Get-PatchBaseline -Server $vCenter } else { Write-PScriboMessage 'VUM patch baseline information is not currently available with your version of PowerShell.' } if ($VUMBaselines) { Section -Style Heading2 'VMware Update Manager' { Paragraph "The following sections detail the configuration of VMware Update Manager managed by vCenter Server $vCenterServerName." #region VUM Baseline Detailed Information Section -Style Heading3 'Baselines' { $VUMBaselineInfo = foreach ($VUMBaseline in $VUMBaselines) { [PSCustomObject]@{ 'Baseline' = $VUMBaseline.Name 'Description' = $VUMBaseline.Description 'Type' = $VUMBaseline.BaselineType 'Target Type' = $VUMBaseline.TargetType 'Last Update Time' = ($VUMBaseline.LastUpdateTime).ToLocalTime() '# of Patches' = $VUMBaseline.CurrentPatches.Count } } $TableParams = @{ Name = "VMware Update Manager Baseline Summary - $($vCenterServerName)" ColumnWidths = 25, 25, 10, 10, 20, 10 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VUMBaselineInfo | Sort-Object Baseline | Table @TableParams } #endregion VUM Baseline Detailed Information #region VUM Comprehensive Information if ("Desktop" -eq $PSVersionTable.PsEdition) { $VUMPatches = Get-Patch -Server $vCenter | Sort-Object -Descending ReleaseDate } else { Write-PScriboMessage 'VUM patch information is not currently available with your version of PowerShell.' } if ($VUMPatches -and $InfoLevel.VUM -ge 5) { BlankLine Section -Style Heading3 'Patches' { $VUMPatchInfo = foreach ($VUMPatch in $VUMPatches) { [PSCustomObject]@{ 'Patch' = $VUMPatch.Name 'Product' = ($VUMPatch.Product).Name 'Description' = $VUMPatch.Description 'Release Date' = $VUMPatch.ReleaseDate 'Vendor ID' = $VUMPatch.IdByVendor } } $TableParams = @{ Name = "VMware Update Manager Patch Information - $($vCenterServerName)" ColumnWidths = 20, 20, 20, 20, 20 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $VUMPatchInfo | Table @TableParams } } #endregion VUM Comprehensive Information } } } #endregion VMware Update Manager Section } #endregion vCenter Server Heading1 Section # Disconnect vCenter Server $Null = Disconnect-VIServer -Server $VIServer -Confirm:$false -ErrorAction SilentlyContinue } # End of If $vCenter #endregion Generate vSphere report #region Variable cleanup Clear-Variable -Name vCenter #endregion Variable cleanup } # End of Foreach $VIServer #endregion Script Body } # End Invoke-AsBuiltReport.VMware.vSphere function |