#region Help # ---------- <# .SYNOPSIS Get-HyperVReport.ps1 (aka Hyper-V Reporting Script) can be used to report Hyper-V Cluster or Standalone environments. Highlights: o Creates a plain but detailed and user-friendly HTML report which is compatible with all modern browsers. o Has an Overview section which shows momentary cluster resource usage. o Storage Overcommitment (see details below) o Shows alerts in the report for certain situations (utilizations, vm checkpoints, replication status, etc.) o Provides more detailed information via tooltips in the HTML report. (cells with asteriks and highlighted) o Includes a mode that reports only alerts in the Hyper-V environment. (aka HighlightsOnly mode) o Collects information by using standard Hyper-V and Clustering PowerShell cmdlets and custom WMI queries. o Checks and installs required runtime environment prerequisites like Hyper-V and Clustering Powershells. o Can be used directly from command-line or as a scheduled Windows task. o Supports report delivery via e-mail with advanced options. (authentication, TLS/SSL, multiple recipients) o Advanced error handling and logging. (Console messages and log file) Version History: [x] Version 1.5 - 05.March.2015 [ ] Version 1.1 - 14.January.2015 [ ] Version 1.0 - 06.January.2015 Requirements: o Hyper-V Targets (Clustered or Standalone) * Active Directory domain membership * Supported Operating Systems - Windows Server 2012 - Windows Server 2012 R2 - Hyper-V Server 2012 - Hyper-V Server 2012 R2 o Script Runtime Operating System (directly on a Hyper-V target or remote Windows operating system) * Same or trusted Active Directory domain membership with Hyper-V target * Supported Operating Systems - Windows Server 2012 - Windows Server 2012 R2 - Windows 8 - Windows 8.1 * Windows PowerShell 3.0 or 4.0 (installed by default on supported server operating systems) * Sets the Windows PowerShell execution policy to RemoteSigned or Unrestricted * Hyper-V PowerShell (if not, automatically installed by the Get-HyperVReport.ps1 for server oses) * Failover Clustering PowerShell (if not, automatically installed by the Get-HyperVReport.ps1 for server oses) * The script requires administrative privileges on the target Hyper-V server(s) .DESCRIPTION It can be difficult to monitor and assess resources in large Hyper-V environments. This script helps you to understand virtualization inventory, capacity and general resource availability in your Hyper-V environment. Report details: 1) Cluster Overview (Applicable on clusters only): o Pyhsical Resources * Node * Processor * Memory * Storage o Virtual Resources * vMachine * vProcessor * vMemory * vStorage 2) Hyper-V Host (Clustered or Standalone): o Hostname * Computer Manufacturer, Model o Operating System Version o State o Uptime o Domain Name o Total and Running VM Count * Detailed as Clustered and Non-clustered o Processor Count * Logical processor count * Physical processor socket count * Processor Manufacturer, Model, Ghz * Hyper-Threading state for Intel processor (shown as tooltip) * Virtual Processors per Logical Processor ratio o Used Physical RAM o Free Physical RAM o Total Physical RAM 3) Disk/Volume (Clustered or Standalone): o Name * Volume Name (Local Volume, Clustered Volume, Cluster Shared Volume) * Volume label or CSV path (shown as tooltip) * Disk name (Physical Disk, Clustered Disk) * Total/Allocated/Unallocated physical disk size (shown as tooltip) o Disk/Volume State o Usage (Logical Partition, Cluster Volume, Cluster Shared Volume, Quorum, System Volume, Pass-through, Unassigned) o Owner o Physical Disk Bus Type o Volume File System o Active VHD (Storage Overcommitment) o Used Size o Free Size o Total Size 4) Virtual Machine o Name * VM name * Configuration XML path (shown as tooltip) * Generation * Version o State o Uptime o Owner * Owner hostname o Virtual Processor * Count o Virtual RAM * Startup * Minimum (if dynamic memory enabled) * Maximum (if dynamic memory enabled) * Assigned o Integration Services * State like UpToDate, UpdateRequired, MayBeRequired, NotDetected * Version number (shown as tooltip) o Checkpoint * Checkpoint state * Checkpoint count (if exists, shown as tooltip) * Checkpoint chain (if exists) o Replica * Replication State and Health * Primary, Replica and Extended modes * Replica Server or Primary Server (shown as tooltip) * Replication Frequency (shown as tooltip) * Last Replication Time (shown as tooltip) o Disk * VHD Name * VHD File Path (shown as tooltip) * Current VHD file size * Maximum VHD disk size * VHD Type * Controller Type * VHD fragmentation percent * Including pass-through disks (if exists) * Including differencing virtual disk chain (if exists) * Can detects missing VHD files (if exists) o Network Adapter * Device type * Connection status * Virtual switch name * IP address * VLAN ID * Advanced - MAC Address, MAC Type, DHCP Guard, Raouter Guard, Port Mirroring, Protected Network o Can detects missing VHD files o Can detects clustered VM configuration resource problems like offline o Can detects clustered VM failed state .PARAMETER Cluster A single Hyper-V Cluster name. .PARAMETER VMHost A single standalone Hyper-V Host name or an array of standalone Hyper-V Host names. .PARAMETER HighlightsOnly A filtering mode only allows the reporting of highlighted events and alerts. .PARAMETER ReportFilePath HTML report file path. Script working directory is the default value. .PARAMETER ReportFileNamePrefix HTML report file name prefix. The default value is "HyperVReport" .PARAMETER SendMail Send e-mail option ($true/$false). The default value is "$fale". .PARAMETER SMTPServer Mail server address. .PARAMETER SMTPPort Mail server port. The default value is "25". .PARAMETER MailTo A single mail recipient or an array of mail recipients. .PARAMETER MailFrom Mail sender address. .PARAMETER MailFromPassword Mail sender password for SMTP authentication. .PARAMETER SMTPServerTLSorSSL SMTP TLS/SSL option ($true/$false). The default value is "$fale". .PARAMETER ReportFileNameTimeStamp Adds Timestamp to HTML report file name (The default is $true). If you set it to $false then html report’s filename will not have date and time value and it will always has the same filename. .EXAMPLE Creates a Hyper-V Cluster report in the working directory. .\Get-HyperVReport.ps1 -Cluster Hvcluster1 .EXAMPLE Creates a Hyper-V Cluster report that shown only highlighted events and alerts in the working directory. .\Get-HyperVReport.ps1 -Cluster Hvcluster1 -HighlightsOnly $true .EXAMPLE Creates one or more standalone Hyper-V Host(s) report in the working directory. .\Get-HyperVReport.ps1 -VMHost Host1,Host2,Host3 .EXAMPLE Creates a Hyper-V Cluster report with custom file name prefix and saves is to the specified folder. .\Get-HyperVReport.ps1 -Cluster Hvcluster1 -ReportFileNamePrefix HvReport -ReportFilePath c:\tools .EXAMPLE Creates a Hyper-V Cluster report and sends it to multiple recipients as attachment without smtp authentication. .\Get-HyperVReport.ps1 -Cluster Hvcluster1 -SendMail $true -SMTPServer 10.29.0.50 -MailFrom sender@hyperv.corp -MailTo recepient1@hyperv.corp,recepient2@hyperv.corp .EXAMPLE Creates a Hyper-V Cluster report and sends it to multiple recipients as attachment with smtp authentication and TLS/SSL communication. -SMTPServerTLSorSSL is optional and used if forced by the smtp server. .\Get-HyperVReport.ps1 -Cluster Hvcluster1 -SendMail $true -SMTPServer 10.29.0.50 -MailFrom sender@hyperv.corp -MailFromPassword P@ssw0rd -SMTPServerTLSorSSL $true -MailTo recepient1@hyperv.corp,recepient2@hyperv.corp .INPUTS None .OUTPUTS None .NOTES Author: Serhat AKINCI Website: http://www.serhatakinci.com Email: serhatakinci@gmail.com Date created: 26.December.2014 Last modified: 05.March.2015 Version: 1.5 .LINK http://www.serhatakinci.com https://twitter.com/serhatakinci #> #endregion Help #region Script Parameters # ----------------------- [CmdletBinding(SupportsShouldProcess=$True)] Param ( [parameter( Mandatory=$false, HelpMessage='Hyper-V Cluster name (like HvCluster1 or hvcluster1.domain.corp')] [string]$Cluster, [parameter( Mandatory=$false, HelpMessage='Standalone Hyper-V Host name(s) (like Host1, Host2, Host3)')] [array]$VMHost, [parameter( Mandatory=$false, HelpMessage='Reports that shown only highlighted events and alerts')] [bool]$HighlightsOnly = $false, [parameter( Mandatory=$false, HelpMessage='Disk path for HTML reporting file')] [string]$ReportFilePath = (Get-Location).path, [parameter( Mandatory=$false, HelpMessage='Adds a prefix to the HTML report file name (The default nameprefix is HyperVReport)')] [string]$ReportFileNamePrefix = "HyperVReport", [parameter( Mandatory=$false, HelpMessage='Adds Timestamp to HTML report file name (The default is $true)')] [bool]$ReportFileNameTimeStamp = $true, [parameter( Mandatory=$false, HelpMessage='Activates the e-mail sending feature ($true/$false). The default value is "$false"')] [bool]$SendMail = $false, [parameter( Mandatory=$false, HelpMessage='SMTP Server Address (Like IP address, hostname or FQDN)')] [string]$SMTPServer, [parameter( Mandatory=$false, HelpMessage='SMTP Server port number (Default 25)')] [int]$SMTPPort = "25", [parameter( Mandatory=$false, HelpMessage='Recipient e-mail address')] [array]$MailTo, [parameter( Mandatory=$false, HelpMessage='Sender e-mail address')] [string]$MailFrom, [parameter( Mandatory=$false, HelpMessage='Sender e-mail address password for SMTP authentication (If needed)')] [string]$MailFromPassword, [parameter( Mandatory=$false, HelpMessage='SMTP TLS/SSL option ($true/$false). The default value is "$false"')] [bool]$SMTPServerTLSorSSL = $false ) #endregion Script Parameters #region Functions #---------------- # Get WMI data function sGet-Wmi { param ( [Parameter(Mandatory = $true)] [string]$ComputerName, [Parameter(Mandatory = $true)] [string]$Namespace, [Parameter(Mandatory = $true)] [string]$Class, [Parameter(Mandatory = $false)] $Property, [Parameter(Mandatory = $false)] $Filter, [Parameter(Mandatory = $false)] [switch]$AI ) # Base string $wmiCommand = "gwmi -ComputerName $ComputerName -Namespace $Namespace -Class $Class -ErrorAction Stop" # If available, add Filter parameter if ($Filter) { # $Filter = ($Filter -join ',').ToString() $Filter = [char]34 + $Filter + [char]34 $wmiCommand += " -Filter $Filter" } # If available, add Property parameter if ($Property) { $Property = ($Property -join ',').ToString() $wmiCommand += " -Property $Property" } # If available, Authentication and Impersonation if ($AI) { $wmiCommand += " -Authentication PacketPrivacy -Impersonation Impersonate" } # Try to connect $ResultCode = "1" Try { # $wmiCommand $wmiResult = iex $wmiCommand } Catch { $wmiResult = $_.Exception.Message $ResultCode = "0" } # If wmiResult is null if ($wmiResult -eq $null) { $wmiResult = "Result is null" $ResultCode = "2" } Return $wmiResult, $ResultCode } # Write Log Function sPrint { param( [byte]$Type=1, [string]$Message, [bool]$WriteToLogFile ) $TimeStamp = Get-Date -Format "dd.MMM.yyyy HH:mm:ss" $Time = Get-Date -Format "HH:mm:ss" if ($Type -eq 1) { Write-Host "[INFO] - $Time - $Message" -ForegroundColor Green if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "[INFO] - $TimeStamp - $Message" } } elseif ($Type -eq 2) { Write-Host "[WARNING] - $Time - $Message" -ForegroundColor Yellow if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "[WARNING] - $TimeStamp - $Message" } } elseif ($Type -eq 5) { if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "[DEBUG] - $TimeStamp - $Message" } } elseif ($Type -eq 6) { if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "" } } elseif ($Type -eq 0) { Write-Host "[ERROR] - $Time - $Message" -ForegroundColor Red if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "[ERROR] - $TimeStamp - $Message" } } else { Write-Host "[UNKNOWN] - $Time - $Message" -ForegroundColor Gray if (($WriteToLogFile) -and ($Logging)) { Add-Content -Path $LogFile -Value "[UNKNOWN] - $TimeStamp - $Message" } } } # Convert Volume Size to KB/MB/GB/TB Function sConvert-Size { param ( # Disk or Volume Space [Parameter(Mandatory = $false)] $DiskVolumeSpace, # Disk or Volume Space Input Unit [Parameter(Mandatory = $true)] [string]$DiskVolumeSpaceUnit ) if ($DiskVolumeSpaceUnit -eq "byte") # byte input { if (($DiskVolumeSpace -ge "1024") -and ($DiskVolumeSpace -lt "1048576")) { $DiskVolumeSpace = [math]::round(($DiskVolumeSpace/1024)) $DiskVolumeSpaceUnit = "KB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1048576") -and ($DiskVolumeSpace -lt "1073741824")) { $DiskVolumeSpace = [math]::round(($DiskVolumeSpace/1024/1024)) $DiskVolumeSpaceUnit = "MB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1073741824") -and ($DiskVolumeSpace -lt "1099511627776")) { $DiskVolumeSpace = "{0:N1}" -f ($DiskVolumeSpace/1024/1024/1024) $DiskVolumeSpaceUnit = "GB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1099511627776") -and ($DiskVolumeSpace -lt "1125899906842624")) { $DiskVolumeSpace = "{0:N2}" -f ($DiskVolumeSpace/1024/1024/1024/1024) $DiskVolumeSpaceUnit = "TB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif ($DiskVolumeSpace -eq $null) { $DiskVolumeSpace = "N/A" $DiskVolumeSpaceUnit = "-" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } else { $DiskVolumeSpace = $DiskVolumeSpace $DiskVolumeSpaceUnit = "Byte" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } } elseif ($DiskVolumeSpaceUnit -eq "kb") # kb input { if (($DiskVolumeSpace -ge "1") -and ($DiskVolumeSpace -lt "1024")) { $DiskVolumeSpace = $DiskVolumeSpace $DiskVolumeSpaceUnit = "KB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1024") -and ($DiskVolumeSpace -lt "1048576")) { $DiskVolumeSpace = ($DiskVolumeSpace/1024) $DiskVolumeSpaceUnit = "MB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1048576") -and ($DiskVolumeSpace -lt "1073741824")) { $DiskVolumeSpace = "{0:N1}" -f ($DiskVolumeSpace/1024/1024) $DiskVolumeSpaceUnit = "GB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1073741824") -and ($DiskVolumeSpace -lt "1099511627776")) { $DiskVolumeSpace = "{0:N2}" -f ($DiskVolumeSpace/1024/1024/1024) $DiskVolumeSpaceUnit = "TB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif ($DiskVolumeSpace -eq $null) { $DiskVolumeSpace = "N/A" $DiskVolumeSpaceUnit = "-" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } else { $DiskVolumeSpace = $DiskVolumeSpace $DiskVolumeSpaceUnit = "KB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } } elseif ($DiskVolumeSpaceUnit -eq "mb") # mb input { if (($DiskVolumeSpace -ge "1") -and ($DiskVolumeSpace -lt "1024")) { $DiskVolumeSpace = $DiskVolumeSpace $DiskVolumeSpaceUnit = "MB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1024") -and ($DiskVolumeSpace -lt "1048576")) { $DiskVolumeSpace = "{0:N1}" -f ($DiskVolumeSpace/1024) $DiskVolumeSpaceUnit = "GB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif (($DiskVolumeSpace -ge "1048576") -and ($DiskVolumeSpace -lt "1073741824")) { $DiskVolumeSpace = "{0:N2}" -f ($DiskVolumeSpace/1024/1024) $DiskVolumeSpaceUnit = "TB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } elseif ($DiskVolumeSpace -eq $null) { $DiskVolumeSpace = "N/A" $DiskVolumeSpaceUnit = "-" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } else { $DiskVolumeSpace = $DiskVolumeSpace $DiskVolumeSpaceUnit = "MB" return $DiskVolumeSpace, $DiskVolumeSpaceUnit } } else { return "Unknown Parameter" } } # Convert BusType Value to BusType Name Function sConvert-BusTypeName { Param ([Byte] $BusTypeValue) if ($BusTypeValue -eq 1){$Result = "SCSI"} elseif ($busTypeValue -eq 2){$Result = "ATAPI"} elseif ($busTypeValue -eq 3){$Result = "ATA"} elseif ($busTypeValue -eq 4){$Result = "IEEE 1394"} elseif ($busTypeValue -eq 5){$Result = "SSA"} elseif ($busTypeValue -eq 6){$Result = "FC"} elseif ($busTypeValue -eq 7){$Result = "USB"} elseif ($busTypeValue -eq 8){$Result = "RAID"} elseif ($busTypeValue -eq 9){$Result = "iSCSI"} elseif ($busTypeValue -eq 10){$Result = "SAS"} elseif ($busTypeValue -eq 11){$Result = "SATA"} elseif ($busTypeValue -eq 12){$Result = "SD"} elseif ($busTypeValue -eq 13){$Result = "SAS"} elseif ($busTypeValue -eq 14){$Result = "Virtual"} elseif ($busTypeValue -eq 15){$Result = "FB Virtual"} elseif ($busTypeValue -eq 16){$Result = "Storage Spaces"} elseif ($busTypeValue -eq 17){$Result = "NVMe"} else {$Result = "Unknown"} Return $Result } # Convert Cluster Disk State Value to Name Function sConvert-ClusterDiskState { Param ([Byte] $StateValue) if ($StateValue -eq 0){$Result = "Inherited",$stateBgColors[5],$stateWordColors[5]} elseif ($StateValue -eq 1){$Result = "Initializing",$stateBgColors[4],$stateWordColors[4]} elseif ($StateValue -eq 2){$Result = "Online",$stateBgColors[1],$stateWordColors[1]} elseif ($StateValue -eq 3){$Result = "Offline",$stateBgColors[2],$stateWordColors[2]} elseif ($StateValue -eq 4){$Result = "Failed",$stateBgColors[3],$stateWordColors[3]} elseif ($StateValue -eq 127){$Result = "Offline",$stateBgColors[2],$stateWordColors[2]} elseif ($StateValue -eq 128){$Result = "Pending",$stateBgColors[4],$stateWordColors[4]} elseif ($StateValue -eq 129){$Result = "Online Pending",$stateBgColors[4],$stateWordColors[4]} elseif ($StateValue -eq 130){$Result = "Offline Pending",$stateBgColors[4],$stateWordColors[4]} else {$Result = "Unknown",$stateBgColors[5],$stateWordColors[5]} # Including "-1" state Return $Result } # Convert BusType Value to BusType Name Function sConvert-DiskPartitionStyle { Param ([Byte] $PartitionStyleValue) if ($PartitionStyleValue -eq 1) { $Result = "MBR" } elseif ($PartitionStyleValue -eq 2) { $Result = "GPT" } else { $Result = "Unknown" } Return $Result } # Generate Volume Size Colors Function sConvert-VolumeSizeColors { Param ([Byte] $FreePercent) if (($FreePercent -le 10) -and ($FreePercent -gt 5)) { $Result = $stateBgColors[4],$stateBgColors[4],$stateWordColors[4] } elseif ($FreePercent -le 5) { $Result = $stateBgColors[3],$stateBgColors[3],$stateWordColors[3] } else { $Result = $stateBgColors[0],$stateBgColors[0],"#BDBDBD" } Return $Result } #endregion Functions #region Variables #---------------- # Print MSG sPrint -Type 1 -Message "Started! Hyper-V Reporting Script (Version 1.5)" Start-Sleep -Seconds 3 # State Colors [array]$stateBgColors = "", "#ACFA58","#E6E6E6","#FB7171","#FBD95B","#BDD7EE" #0-Null, 1-Online(green), 2-Offline(grey), 3-Failed/Critical(red), 4-Warning(orange), 5-Other(blue) [array]$stateWordColors = "", "#298A08","#848484","#A40000","#9C6500","#204F7A","#FFFFFF" #0-Null, 1-Online(green), 2-Offline(grey), 3-Failed/Critical(red), 4-Warning(orange), 5-Other(blue), 6-White # Date and Time $Date = Get-Date -Format d/MMM/yyyy $Time = Get-Date -Format "hh:mm:ss tt" # Log and report file/folder $FileTimeSuffix = ((Get-Date -Format dMMMyy).ToString()) + "-" + ((get-date -Format hhmmsstt).ToString()) if ($ReportFileNameTimeStamp) { $ReportFile = $ReportFilePath + "\" + $ReportFileNamePrefix + "-" + $FileTimeSuffix + ".html" } else { $ReportFile = $ReportFilePath + "\" + $ReportFileNamePrefix + ".html" } $LogFile = $ReportFilePath + "\" + "ScriptLog" + ".txt" # Logging enabled [bool]$Logging = $True # HighlightsOnly Mode String $hlString = $null if ($HighlightsOnly) { $hlString = "
 (HighlightsOnly Mode) 
" sPrint -Type 1 -Message "HighlightsOnly mode is enabled." -WriteToLogFile $True } #endregion Variables #region Prerequisities Check #--------------------------- # Log file check and write subject line if (!(Test-Path -Path $LogFile)) { New-Item -Path $LogFile -ItemType file -Force -ErrorAction SilentlyContinue | Out-Null if (Test-Path -Path $LogFile) { sPrint -Type 6 -WriteToLogFile $true sPrint -Type 5 -Message "----- Start -----" -WriteToLogFile $true sPrint -Type 1 -Message "Logging started: $LogFile" -WriteToLogFile $True Start-Sleep -Seconds 3 } else { $Logging = $false sPrint -Type 2 -Message "Unable to create the log file. Script will continue without logging..." Start-Sleep -Seconds 3 } } else { sPrint -Type 6 -WriteToLogFile $true sPrint -Type 5 -Message "----- Start -----" -WriteToLogFile $true sPrint -Type 1 -Message "Logging started: $LogFile" -WriteToLogFile $true Start-Sleep -Seconds 3 } # Controls for some important prerequisites if ((!$VMHost) -and (!$Cluster)) { sPrint -Type 0 -Message "Hyper-V target parameter is missing. Use -Cluster or -VMHost parameter to define target." -WriteToLogFile $True sPrint -Type 2 -Message "For technical information, type: Get-Help .\Get-HyperVReport.ps1 -examples" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } if (($VMHost) -and ($Cluster)) { sPrint -Type 0 -Message "-Cluster and -VMHost parameters can not be used together." -WriteToLogFile $True sPrint -Type 2 -Message "For technical information, type: Get-Help .\Get-HyperVReport.ps1 -examples" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } # Controls for runtime environment operating system version, Hyper-V PowerShell and Clustering PowerShell modules sPrint -Type 1 -Message "Checking prerequisites to run script on the $($env:COMPUTERNAME.ToUpper())..." -WriteToLogFile $True $osVersion = $null $osName = $null $osVersion = sGet-Wmi -ComputerName $env:COMPUTERNAME -Namespace root\Cimv2 -Class Win32_OperatingSystem -Property Version,Caption if ($osVersion[1] -eq 1) { $osName = $osVersion[0].Caption $osVersion = $osVersion[0].Version } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): $($osVersion[0])" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } if ($osVersion) { if (($OsVersion -like "6.2*") -or ($OsVersion -like "6.3*")) { if ($osName -like "Microsoft Windows 8*") { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Operating system is supported as script runtime environment." -WriteToLogFile $True # Check Hyper-V PowerShell if ((Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-Management-PowerShell -Online).State -eq "Enabled") { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module is not found. Please enable manually and run this script again. You can use `"Turn Windows features on or off`" to enable `"Hyper-V Module for Windows PowerShell`"." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } # Check Failover Cluster PowerShell if ($Cluster) { if (Get-Hotfix -ID KB2693643 -ErrorAction SilentlyContinue) { if ((Get-WindowsOptionalFeature -FeatureName RemoteServerAdministrationTools-Features-Clustering -Online).State -eq "Enabled") { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module is not found. Please enable manually and run this script again. You can use `"Turn Windows features on or off`" to enable `"Failover Clustering Tools`"." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Remote Server Administration Tools (RSAT) is not found. Please download (KB2693643) and install manually and run this script again." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } } else { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Operating system is supported as script runtime environment." -WriteToLogFile $True # Check Hyper-V PowerShell if ((Get-WindowsFeature -ComputerName $env:COMPUTERNAME -Name "Hyper-V-PowerShell").Installed) { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 2 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module is not found." -WriteToLogFile $True sPrint -Type 2 -Message "$($env:COMPUTERNAME.ToUpper()): Installing Hyper-V PowerShell Module... " -WriteToLogFile $True Start-Sleep -Seconds 3 Add-WindowsFeature -Name "Hyper-V-PowerShell" -ErrorAction SilentlyContinue | Out-Null if ((Get-WindowsFeature -ComputerName $env:COMPUTERNAME -Name "Hyper-V-PowerShell").Installed) { sPrint -Type 1 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Hyper-V PowerShell Module could not be installed. Please install it manually." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } # Check Failover Cluster PowerShell if ($Cluster) { if ((Get-WindowsFeature -ComputerName $env:COMPUTERNAME -Name "RSAT-Clustering-PowerShell").Installed) { sPrint -Type 5 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 2 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module is not found." -WriteToLogFile $True sPrint -Type 2 -Message "$($env:COMPUTERNAME.ToUpper()): Installing Failover Clustering PowerShell Module..." -WriteToLogFile $True Start-Sleep -Seconds 3 Add-WindowsFeature -Name "RSAT-Clustering-PowerShell" | Out-Null if ((Get-WindowsFeature -ComputerName $env:COMPUTERNAME -Name "RSAT-Clustering-PowerShell").Installed) { sPrint -Type 1 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module is OK." -WriteToLogFile $True } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Failover Clustering PowerShell Module could not be installed. Please install it manually." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" Break } } } } } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Incompatible operating system version detected. Supported operating systems are Windows Server 2012 and Windows Server 2012 R2." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } else { sPrint -Type 0 -Message "$($env:COMPUTERNAME.ToUpper()): Could not detect operating system version." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } $Computers = $null $ClusterName = $null [array]$VMHosts = $null #endregion Prerequisities Check #region HTML Start #---------------- # HTML Head $outHtmlStart = " Hyper-V Environment Report

Hyper-V Environment Report

Generated on $($Date) at $($Time)
$($hlString)
" #endregion #region Gathering Hyper-V Host Information #----------------------------------------- if ($Cluster) { if (($Cluster -eq "localhost") -or ($Cluster -eq "127.0.0.1")) { $ClusterName = $env:COMPUTERNAME } else { $ClusterName = $Cluster } $ClusterNodes = $null if (Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue) { $ClusterName = (Get-Cluster -Name $ClusterName).Name $hostTableCaption = "Cluster Nodes" $volumeTableCaption = "Clustered Disks/Volumes" sPrint -Type 1 -Message "$($ClusterName.ToUpper()) is accessible. Gathering Node information..." -WriteToLogFile $True Start-Sleep -Seconds 3 sPrint -Type 1 -Message "Checking prerequisites for Hyper-V Cluster reporting..." -WriteToLogFile $True Start-Sleep -Seconds 3 $clusterNodesData = Get-ClusterNode -Cluster $ClusterName -ErrorAction SilentlyContinue | select Name,State $ClusterNodes = ($clusterNodesData | where{$_.State -ne "Down"}).Name $downClusterNodes = ($clusterNodesData | where{$_.State -eq "Down"}).Name $ovTotalNode = ($clusterNodesData).Count if ($downClusterNodes) { sPrint -Type 0 "Unavailable or down Hyper-V Cluster Node(s): $downClusterNodes" -WriteToLogFile $True Start-Sleep -Seconds 3 } if ($ClusterNodes) { # Checking Cluster Owner Node OS version and Hyper-V role $clusterOwnerHostName = sGet-Wmi -ComputerName $ClusterName -Namespace root\Cimv2 -Class Win32_ComputerSystem -Property Name if ($clusterOwnerHostName[1] -eq 1) { $clusterOwnerHostName = $clusterOwnerHostName[0].Name } else { sPrint -Type 0 -Message "$ClusterName`: $($clusterOwnerHostName[0])" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } $osVersion = $null $getClusterOwnerNode = Get-ClusterNode -Cluster $ClusterName -Name $clusterOwnerHostName $osVersion = ($getClusterOwnerNode.MajorVersion).ToString() + "." + ($getClusterOwnerNode.MinorVersion).ToString() if (($osVersion -like "6.2") -or ($osVersion -like "6.3")) { if ((Get-WindowsFeature -ComputerName $clusterOwnerHostName -Name "Hyper-V").Installed) { sPrint -Type 5 -Message "Operating system version and Hyper-V role on the cluster owner node is OK." -WriteToLogFile $True $VMHosts = $ClusterNodes # Clear $offlineVmConfigData = $null $ovTotalVm, $ovOfflineVmConfig = 0 # Get ClusterResource Data $clusterResourceData = Get-ClusterResource -Cluster $ClusterName # Detect offline Virtual Machine Configuration resources $offlineVmConfigData = $clusterResourceData | where{($_.ResourceType -eq "Virtual Machine Configuration") -and ($_.State -ne "Online")} # For Cluster Overview $ovTotalVm = ($clusterResourceData | where{$_.ResourceType -eq "Virtual Machine"}).Count } else { sPrint -Type 2 -Message "Hyper-V role is not installed on $clusterOwnerHostName." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } else { sPrint -Type 2 -Message "$($ClusterName.ToUpper()): Incompatible operating system version detected. Supported operating systems are Windows Server 2012 and Windows Server 2012 R2." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } else { sPrint -Type 0 -Message "$ClusterName`: $($error[0].Exception.Message)" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } else { sPrint -Type 0 -Message "$($ClusterName.ToUpper()): $($error[0].Exception.Message)" -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } } if ($VMHost) { if (($Cluster -eq "localhost") -or ($Cluster -eq "127.0.0.1")) { $Computers = $env:COMPUTERNAME } else { $Computers = $VMHost | Sort-Object -Unique } [array]$invalidVmHost = $null [array]$invalidVmHostMsg = $null $hostTableCaption = "Standalone Host(s)" $volumeTableCaption = "Local Disks/Volumes" sPrint -Type 1 -Message "Checking prerequisites for standalone Hyper-V host(s) reporting..." -WriteToLogFile $True Start-Sleep -Seconds 3 foreach ($ComputerName in $Computers) { $osVersion = $null $osVersion = sGet-Wmi -ComputerName $ComputerName -Namespace root\Cimv2 -Class Win32_OperatingSystem -Property Version if ($osVersion[1] -eq 1) { $osVersion = $osVersion[0].Version } else { sPrint -Type 0 -Message "$($ComputerName.ToUpper()): $($osVersion[0])" -WriteToLogFile $True Start-Sleep -Seconds 3 $invalidVmHost += $ComputerName $invalidVmHostMsg += $osVersion[0] Continue } if ($OsVersion) { if (($OsVersion -like "6.2*") -or ($OsVersion -like "6.3*")) { if ((Get-WindowsFeature -ComputerName $ComputerName -Name "Hyper-V").Installed) { $checkClusterMember = sGet-Wmi -ComputerName $ComputerName -Namespace root\MSCluster -Class MSCluster_Cluster -Property Name if ($checkClusterMember[1] -eq 1) { sPrint -Type 0 -Message "$($ComputerName.ToUpper()) is a member of a Hyper-V Cluster and didn't included in the VMHost list. Please use -Cluster parameter to report this node." -WriteToLogFile $True $invalidVmHost += $ComputerName $invalidVmHostMsg += "This Node is a member of a cluster. Please use -Cluster parameter to report this node." } else { sPrint -Type 5 -Message "$($ComputerName.ToUpper()): Operating system version and Hyper-V role is OK." -WriteToLogFile $True $VMHosts += $ComputerName } } else { sPrint -Type 0 -Message "$($ComputerName.ToUpper()): Could not be added to the VMHost list because Hyper-V role is not installed." -WriteToLogFile $True $invalidVmHost += $ComputerName $invalidVmHostMsg += "Could not be added to the VMHost list because Hyper-V role is not installed" } } else { sPrint -Type 0 -Message "$($ComputerName.ToUpper()): Could not be added to the VMHost list because incompatible operating system version detected." -WriteToLogFile $True $invalidVmHost += $ComputerName $invalidVmHostMsg += "Could not be added to the VMHost list because incompatible operating system version detected" } } else { sPrint -Type 0 -Message "$($ComputerName.ToUpper()): Could not be added to the VMHost list because operating system version could not be detected." -WriteToLogFile $True $invalidVmHost += $ComputerName $invalidVmHostMsg += "Could not be added to the VMHost list because operating system version could not be detected" } } } if (!$VMHosts) { sPrint -Type 2 "No valid server for reporting." -WriteToLogFile $True sPrint -Type 0 -Message "Script terminated!" -WriteToLogFile $True Break } else { if ($Cluster) { sPrint -Type 1 "Available Hyper-V Cluster Node(s) for reporting: $VMHosts" -WriteToLogFile $True Start-Sleep -Seconds 3 } else { sPrint -Type 1 "Available Hyper-V server(s) for reporting: $VMHosts" -WriteToLogFile $True Start-Sleep -Seconds 3 } } # Print MSG sPrint -Type 1 "Gathering Hyper-V Host information..." -WriteToLogFile $True # VMHosts-Table Header $outVMHostTableStart ="

$($hostTableCaption)


" # Generate Data Lines $outVMHostTable = $null $ovUpNode, $ovTotalLP, $ovTotalMemory, $ovUsedMemory = 0 $ovTotalVProc, $ovTotalVmMemory, $ovUsedVmMemory, $ovUsedVmVHD, $ovTotalVmVHD = 0 foreach ($vmHostItem in $vmHosts) { $highL = $false $chargerVMHostTable = $null $vmHostData = $null $vmHostTotalVProc = 0 $vmHostVpLpRatio = 0 $vmHostRunningClusVmCount= 0 $vmHostGet = Get-VMHost -ComputerName $vmHostItem $vmHostVMs = Get-VM -ComputerName $vmHostItem $vmHostVmCount = $vmHostVMs.Count + ($offlineVmConfigData | where{$_.OwnerNode -eq "$vmHostItem"}).Count $vmHostRunningVmCount = ($vmHostVMs | where{$_.State -eq "Running"}).Count $vmHostRunningClusVmCount = ($vmHostVMs | where{($_.IsClustered -eq $true) -and ($_.State -eq "Running")}).Count $vmHostRunningNonClusVmCount = $vmHostRunningVmCount - $vmHostRunningClusVmCount $vmHostTotalVProc = (($vmHostVMs | where{(($_.State -eq "Running") -or ($_.State -eq "Paused"))}).ProcessorCount | Measure-Object -Sum).Sum $vmHostClusVProc = (($vmHostVMs | where{(($_.State -eq "Running") -and ($_.IsClustered -eq $true)) -or (($_.State -eq "Paused") -and ($_.IsClustered -eq $true))}).ProcessorCount | Measure-Object -Sum).Sum $vmHostWmiData = Get-WmiObject -ComputerName $vmHostItem -Class Win32_OperatingSystem # For Cluster Overview $ovTotalVProc = $ovTotalVProc + $vmHostClusVProc # State if ($Cluster) { $vmHostState = (Get-ClusterNode -Cluster $ClusterName -Name $vmHostItem).State } else { $vmHostState = "Up" } # State Colors if ($vmHostState -eq "Up") { $outVmHostState = "Up",$stateBgColors[1],$stateWordColors[1] } elseif ($vmHostState -eq "Down") { $outVmHostState = "Down",$stateBgColors[3],$stateWordColors[3] $highL = $true } elseif ($vmHostState -eq "Paused") { $outVmHostState = "Paused",$stateBgColors[2],$stateWordColors[2] } elseif ($vmHostState -eq "Joining") { $outVmHostState = "Joining",$stateBgColors[4],$stateWordColors[4] } else { $outVmHostState = "Unknown",$stateBgColors[5],$stateWordColors[5] $highL = $true } # Clear $TotalUsedMemory = $null $TotalFreeMemory = $null $TotalVisibleMemory = $null $vmHostUptime = $null $TotalFreeMemoryPercentage = $null # Memory Capacty $TotalUsedMemory = sConvert-Size -DiskVolumeSpace ($vmHostWmiData.TotalVisibleMemorySize - $vmHostWmiData.FreePhysicalMemory) -DiskVolumeSpaceUnit kb $TotalFreeMemory = sConvert-Size -DiskVolumeSpace $vmHostWmiData.FreePhysicalMemory -DiskVolumeSpaceUnit kb $TotalVisibleMemory = sConvert-Size -DiskVolumeSpace $vmHostWmiData.TotalVisibleMemorySize -DiskVolumeSpaceUnit kb $TotalFreeMemoryPercentage = [math]::round(($vmHostWmiData.FreePhysicalMemory/$vmHostWmiData.TotalVisibleMemorySize)*100) # Free Memory Percentage Colors # 0 - $totalFreeMemoryBgColor # 1 - $TotalFreeMemoryPercentageBgColor # 2 - $TotalFreeMemoryPercentageWordColor if (($TotalFreeMemoryPercentage -le 10) -and ($TotalFreeMemoryPercentage -gt 5)) { $outVmHostFreeMemoryState = $stateBgColors[4],$stateBgColors[4],$stateWordColors[4] $highL = $true } elseif ($TotalFreeMemoryPercentage -le 5) { $outVmHostFreeMemoryState = $stateBgColors[3],$stateBgColors[3],$stateWordColors[3] $highL = $true } else { $outVmHostFreeMemoryState = $stateBgColors[0],$stateBgColors[0],"#BDBDBD" } # Hostname $outVMHostName = ($vmHostGet.ComputerName).ToUpper() # Uptime $vmHostUptime = ([Management.ManagementDateTimeConverter]::ToDateTime($vmHostWmiData.LocalDateTime)) - ([Management.ManagementDateTimeConverter]::ToDateTime($vmHostWmiData.LastBootUpTime)) if($vmHostUptime.Days -eq "0"){$vmHostUptimeDays = ""} else{$vmHostUptimeDays = ($vmHostUptime.Days).ToString() + " Days
"} $vmHostUptime = ($vmHostUptime.Hours).ToString() + ":" + ($vmHostUptime.Minutes).ToString() + ":" + ($vmHostUptime.Seconds).ToString() # OS Version $vmHostOsVersion = ($vmHostWmiData.Caption).Replace("Microsoft ","") # Processor socket and HT state $processorData = sGet-Wmi -ComputerName $vmHostItem -Namespace root\CIMv2 -Class Win32_Processor -Property DeviceID,NumberOfCores,NumberOfLogicalProcessors if ($processorData[1] -eq 1) { $socketCount = ($processorData[0] | ForEach-Object {$_.DeviceID} | select-object -unique).Count $coreCount = ($processorData[0].NumberOfCores | Measure-Object -Sum).Sum $logicalProcCount = ($processorData[0].NumberOfLogicalProcessors | Measure-Object -Sum).Sum if ($logicalProcCount -gt $coreCount) { $htState = "Active" } Else { $htState = "Inactive" } } else { $socketCount = "-" $htState = "Unknown" } $vmHostLpCount = $vmHostGet.LogicalProcessorCount if (!$vmHostLpCount) { $vmHostLpCount = $logicalProcCount } # For Cluster Overview if(($Cluster) -and ($vmHostState -eq "Up")) { $ovUpNode = $ovUpNode + 1 $ovTotalLP = $ovTotalLP + $vmHostLpCount $ovUsedMemory = $ovUsedMemory + ($vmHostWmiData.TotalVisibleMemorySize - $vmHostWmiData.FreePhysicalMemory) $ovTotalMemory = $ovTotalMemory + $vmHostWmiData.TotalVisibleMemorySize } # LP:VP Ratio $vmHostVpLpRatio = ("{0:N2}" -f ($vmHostTotalVProc / $vmHostLpCount)).Replace(".00","") # Computer and Processor Manufacturer/Model Info $outVmHostComputerInfo = gwmi -ComputerName $vmHostItem -Class Win32_ComputerSystem -Property Manufacturer,Model $outVmHostProcModel = (gwmi -ComputerName $vmHostItem -Class Win32_Processor).Name if($outVmHostProcModel.count -gt 1) { $outVmHostProcModel = $outVmHostProcModel[0] } $outVmHostProcModel = $outVmHostProcModel.Replace(" "," ") # Data Line $chargerVMHostTable =" " # Add to HTML Table if ($HighlightsOnly -eq $false) { # VMHost Output $outVMHostTable += $chargerVMHostTable } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { # VMHost Output $outVMHostTable += $chargerVMHostTable } else { # Blank } } # Add offline or unsupported standalone hosts if ($invalidVmHost) { [bytle]$numb = 0 ForEach ($VMhostIN in $invalidVmHost) { $outVMHostTable +=" " $numb = $numb + 1 } } # Add down cluster nodes if ($downClusterNodes) { ForEach ($downClusterNode in $downClusterNodes) { $outVMHostTable +=" " } } if (($outVMHostTable -eq $null) -and ($downClusterNodes -eq $null)) { if ($Cluster) { $outVMHostTable +=" " } else { $outVMHostTable +=" " } } # End VMHosts-Table $outVMHostTableEnd ="

Name

State

Uptime

Domain

Total
VM

Active
vProcessor

Logical
Processor

Used
Memory

Free
Memory

Total
Memory

$($outVMHostName) *
$($vmHostOsVersion)

$($outVmHostState[0])

$($vmHostUptimeDays)$($vmHostUptime)

$($vmHostGet.FullyQualifiedDomainName)

$($vmHostVmCount)
$($vmHostRunningVmCount) Running *

$($vmHostTotalVProc)
$($vmHostVpLpRatio):1 *

$($vmHostLpCount)
$($socketCount) Socket *

$(($TotalUsedMemory)[0])
$(($TotalUsedMemory)[1])

$(($TotalFreeMemory)[0])
$(($TotalFreeMemory)[1])

$(($TotalVisibleMemory)[0]) $(($TotalVisibleMemory)[1])
 ~%$($TotalFreeMemoryPercentage) free 

$(($VMhostIN).ToUpper())
Operating System Unknown

Unaccessible

$($invalidVmHostMsg[$numb])

$($downClusterNode)
Operating System Unknown

Down

Hyper-V Cluster node is down or unavailable

  All Hyper-V Cluster Nodes are healthy  

 All Standalone Hyper-V Hosts are healthy  

" #endregion #region Gathering VM Information #------------------------------- # Print MSG sPrint -Type 1 "Gathering Virtual Machine information..." -WriteToLogFile $True $outVMTableStart = "

Virtual Machines


" # Generate Data Lines $outVmTable = $null $cntVM = 0 $vmNoInTable = 0 $ovRunningVm = 0 $ovPausedVm = 0 # Active VHD Array $activeVhds = @() ForEach ($VMHostItem in $VMHosts) { $getVMerr = $null $VMs = Get-VM -ComputerName $VMHostItem -ErrorVariable getVMerr -ErrorAction SilentlyContinue $vNetworkAdapters = Get-VM -ComputerName $VMHostItem | Get-VMNetworkAdapter -ErrorAction SilentlyContinue # Offline Virtual Machine Configuration resources on this node if ($Cluster) { $offlineVmConfigs = $null $offlineVmConfigs = $offlineVmConfigData | where{$_.OwnerNode -eq "$VMHostItem"} } # If Get-VM is success if ($VMs) { $cntVM = $cntVM + 1 foreach ($VM in $VMs) { $highL = $false $chargerVmTable = $null $chargerVmMemoryTable = $null $outVmReplReplicaServer = $null $outVmReplFrequency = $null # Table TR Color if([bool]!($vmNoInTable%2)) { #Even or Zero $vmTableTrBgColor = "" } else { #Odd $vmTableTrBgColor = "#F9F9F9" } # Name and Config Path $outVmName = $VM.VMName $outVmPath = $VM.ConfigurationLocation # Generation and Version if (!$VM.Generation -and !$VM.Version) { $outVmGenVer = "" } else { $outVmGenVer = "
Gen$($VM.Generation) (v$($VM.Version))" } # VM State $outVmState = $VM.State # IsClustered Yes or No if ($VM.IsClustered -eq $True) { # For Cluster Overview (Total and Used VmMemory) if ($VM.State -eq "Running") { $ovRunningVm = $ovRunningVm + 1 } if ($VM.State -eq "Paused") { $ovPausedVm = $ovPausedVm + 1 } if(($VM.State -eq "Running") -or ($VM.State -eq "Paused")) { if(!$VM.DynamicMemoryEnabled) { $ovTotalVmMemory = $ovTotalVmMemory + $VM.MemoryStartup } else { $ovTotalVmMemory = $ovTotalVmMemory + $VM.MemoryMaximum } $ovUsedVmMemory = $ovUsedVmMemory + $VM.MemoryAssigned } # Clustered VM State $getClusVMerr = $null $outVmIsClustered = "Yes" $clusVmState = (Get-ClusterResource -Cluster $ClusterName -VMId $VM.VMId -ErrorAction SilentlyContinue -ErrorVariable getClusVMerr).State if ($getClusVMerr) { $outVmState = "Unknown" } elseif ($clusVmState -eq "Online") { if ($VM.State -eq "Paused") { $outVmState = "Paused" } else { $outVmState = "Running" } } elseif ($clusVmState -eq "Offline") { if ($VM.State -eq "Saved") { $outVmState = "Saved" } else { $outVmState = "Off" } } else { $outVmState = $clusVmState } } else { $outVmIsClustered = "No" } # VM State Color if ($outVmState -eq "Running") { $vmStateBgColor = $stateBgColors[1] $vmStateWordColor = $stateWordColors[1] } Elseif ($outVmState -eq "Off") { $vmStateBgColor = $stateBgColors[2] $vmStateWordColor = $stateWordColors[2] } Elseif (($outVmState -match "Critical") -or ($outVmState -match "Failed")) { $vmStateBgColor = $stateBgColors[3] $vmStateWordColor = $stateWordColors[3] $highL = $true } Elseif (($outVmState -eq "Paused") -or ($outVmState -eq "Saved")) { $vmStateBgColor = $stateBgColors[4] $vmStateWordColor = $stateWordColors[4] $highL = $true } else { $vmStateBgColor = $stateBgColors[5] $vmStateWordColor = $stateWordColors[5] } # Uptime if ($VM.Uptime -eq "00:00:00") { $outVmUptimeDays = $null $outVmUptime = "Stopped" } else { $outVmUptimeDays = (($VM.Uptime).Days).ToString() if ($outVmUptimeDays -eq "0") { $outVmUptimeDays = $null } else { $outVmUptimeDays = $outVmUptimeDays + " Days
" } $outVmUptime = (($VM.Uptime).Hours).ToString() + ":" + (($VM.Uptime).Minutes).ToString() + ":" + (($VM.Uptime).Seconds).ToString() } # Owner Host $outVmHost = ($VM.ComputerName).ToUpper() # vCPU $outVmCPU = $VM.ProcessorCount # IS State, Version and Color if ($VM.IntegrationServicesState -eq "Up to date") { $outVmIs = "UpToDate" $outVmIsVer = $VM.IntegrationServicesVersion $vmIsStateBgColor = "" $vmIsStateWordColor = "" } elseif ($VM.IntegrationServicesState -eq "Update required") { $outVmIs = "UpdateRequired" $outVmIsVer = $VM.IntegrationServicesVersion $vmIsStateBgColor = $stateBgColors[4] $vmIsStateWordColor = $stateWordColors[4] $highL = $true } else { if ($vm.State -eq "Running") { if ($VM.IntegrationServicesVersion -eq "6.2.9200.16433") { $outVmIs = "UpToDate" $outVmIsVer = $VM.IntegrationServicesVersion $vmIsStateBgColor = "" $vmIsStateWordColor = "" } elseif ($VM.IntegrationServicesVersion -eq $null) { $outVmIs = "NotDetected" $outVmIsVer = "NotDetected" $vmIsStateBgColor = "" $vmIsStateWordColor = "" } else { $outVmIs = "MayBeRequired" $outVmIsVer = $VM.IntegrationServicesVersion $vmIsStateBgColor = "" $vmIsStateWordColor = "" } } else { $outVmIs = "NotDetected" $outVmIsVer = "NotDetected" $vmIsStateBgColor = "" $vmIsStateWordColor = "" } } # Checkpoint State and Color if ($VM.ParentSnapshotId) { $outVmChekpoint = "Yes" $vmCheckpointBgColor = $stateBgColors[4] $vmCheckpointWordColor = $stateWordColors[4] $vmChekpointCount = (Get-VMSnapshot -ComputerName $VM.ComputerName -VMName $VM.Name).Count $outVmChekpointCount = ($vmChekpointCount).ToString() + " Checkpoint(s)" $highL = $true } else { $outVmChekpoint = "No" $vmCheckpointBgColor = "" $vmCheckpointWordColor = "" $outVmChekpointCount = $null } # Replication if ($VM.ReplicationState -ne "Disabled") { $outVmRepl = $null $chargerVmRepl1 = $null $chargerVmRepl2 = $null $getVmReplication = Get-VMReplication -ComputerName $VM.ComputerName -VMName $VM.Name foreach ($getVmReplItem in $getVmReplication) { if ($getVmReplItem.Mode -eq "Primary") #Primary { $outVmReplType = "Primary" $outVmReplServer = "ReplicaServer: $($getVmReplItem.ReplicaServer) " } elseif ($getVmReplItem.Mode -eq "Replica" -and $getVmReplItem.RelationshipType -eq "Simple") #Replica { $outVmReplType = "Replica" $outVmReplServer = "PrimaryServer: $($getVmReplItem.PrimaryServer) " } elseif ($getVmReplItem.Mode -eq "Replica" -and $getVmReplItem.RelationshipType -eq "Extended") #Replica to Extended { $outVmReplType = "Extended" $outVmReplServer = "ReplicaServer: $($getVmReplItem.ReplicaServer) " } elseif ($getVmReplItem.Mode -eq "ExtendedReplica") #Extended { $outVmReplType = "Extended" $outVmReplServer = "PrimaryServer: $($getVmReplItem.PrimaryServer) " } elseif ($getVmReplItem.Mode -eq "Replica") { $outVmReplType = "Replica" $outVmReplServer = "PrimaryServer: $($getVmReplItem.PrimaryServer) " } else { $outVmReplType = $getVmReplItem.Mode $outVmReplServer = "PrimaryServer/ReplicaServer " } $outVmReplHealth = "Health: $($getVmReplItem.Health) " $outVmReplMode = "Mode: $($getVmReplItem.Mode) " $outVmLastReplTime = "LastReplTime: $($getVmReplItem.LastReplicationTime) " $outVMReplState = "ReplState: $($getVmReplItem.State)" # Repl Frequency if ($getVmReplItem.FrequencySec -gt 30) { $outVmReplFrequency = "Frequency: " + (($getVmReplItem.FrequencySec)/60) + " Min " } elseif ($getVmReplItem.FrequencySec -le 30 -and $getVmReplItem.FrequencySec -gt 0) { $outVmReplFrequency = "Frequency: " + ($getVmReplItem.FrequencySec) + " Sec " } elseif($OsVersion -like "6.2*") { $outVmReplFrequency = "Frequency: 5 Min " } else { $outVmReplFrequency = "Frequency: " } # Repl Health Colors if ($getVmReplItem.Health -eq "Normal") { $vmReplHealthBgColor = $stateBgColors[1] $vmReplHealthWordColor = $stateWordColors[1] } elseif ($getVmReplItem.Health -eq "Warning") { $vmReplHealthBgColor = $stateBgColors[4] $vmReplHealthWordColor = $stateWordColors[4] $highL = $true } elseif ($getVmReplItem.Health -eq "Critical") { $vmReplHealthBgColor = $stateBgColors[3] $vmReplHealthWordColor = $stateWordColors[3] $highL = $true } else { $vmReplHealthBgColor = $stateBgColors[5] $vmReplHealthWordColor = $stateWordColors[5] $highL = $true } if ($getVmReplItem.Mode -eq "Replica" -and $getVmReplItem.RelationshipType -eq "Extended") { $chargerVmRepl2 = "

$($outVmReplType)

" } else { $chargerVmRepl1 = "

$($outVmReplType)

" } } $outVmRepl = $chargerVmRepl1 + $chargerVmRepl2 } else { $outVmRepl = "

N/E

" $vmReplHealthBgColor = "" $vmReplHealthWordColor = "" } # Network Adapter if ($vNetworkAdapters | where{$_.VMId -eq $VM.VMId}) { $vmNetAdapterCount = 1 $vmNetAdapters = $null $outVmNetAdapter = $null $vmNetAdapters = $vNetworkAdapters | where{$_.VMId -eq $VM.VMId} foreach ($vmNetAdapter in $vmNetAdapters) { # Type if (!$vmNetAdapter.IsLegacy) { $outVmNetAdapterName = "Synthetic Network Adapter" } else { $outVmNetAdapterName = "Legacy Network Adapter" } # IP if ($vmNetAdapter.IPAddresses) { if ($vmNetAdapter.IPAddresses.Count -gt 1) { $outVmNetAdapterIP = ($vmNetAdapter.IPAddresses -join ', ').ToString() } else { $outVmNetAdapterIP = $vmNetAdapter.IPAddresses } } else { $outVmNetAdapterIP = "Unable to get ip address information" } # MAC if ($vmNetAdapter.MacAddress) { $outVmNetAdapterMacAddress = "MAC Address: $($vmNetAdapter.MacAddress)" } else { $outVmNetAdapterMacAddress = "MAC Address: Null" } if ($vmNetAdapter.DynamicMacAddressEnabled) { $outVmNetAdapterMacAddressType = "MAC Type: Dynamic" } else { $outVmNetAdapterMacAddressType = "MAC Type: Static" } # Connection if ($vmNetAdapter.Connected) { $outVmNetAdapterConnection = "Connected" $outVmNetAdapterSwitch = "Virtual Switch Name: $($vmNetAdapter.SwitchName)" } else { $outVmNetAdapterConnection = "Not connected" $outVmNetAdapterSwitch = "Not connected to a switch" } # VLAN if (($vmNetAdapter.VlanSetting.AccessVlanId -eq 0) -or ($vmNetAdapter.VlanSetting.AccessVlanId -eq $null)) { $outVmNetAdapterVlan = "VLAN: Disabled" } else { $outVmNetAdapterVlan = "VLAN: Enabled, ID $($vmNetAdapter.VlanSetting.AccessVlanId)" } # Other $outVmNetAdapterDhcpGuard = "DHCP Guard: $($vmNetAdapter.DhcpGuard)" $outVmNetAdapterRouterGuard = "Router Guard: $($vmNetAdapter.RouterGuard)" $outVmNetAdapterPortMirroringMode = "Port Mirroring: $($vmNetAdapter.PortMirroringMode)" if ($vmNetAdapter.ClusterMonitored) { $outVmNetAdapterClusterMonitored = "Protected Network: On" } else { if ($OsVersion -like "6.2*") { $outVmNetAdapterClusterMonitored = "Protected Network: N/A" } else { $outVmNetAdapterClusterMonitored = "Protected Network: Off" } } # Write if ($vmNetAdapterCount -eq 1) { $chargerVmNetAdapter = "

$($outVmNetAdapterName) *
$($outVmNetAdapterConnection) | VLAN | Advanced

" } else { $chargerVmNetAdapter = "

$($outVmNetAdapterName) *
$($outVmNetAdapterConnection) | VLAN | Advanced

" } $outVmNetAdapter += $chargerVmNetAdapter $vmNetAdapterCount = $vmNetAdapterCount + 1 } } else { $outVmNetAdapter = "

No Network Adapter

" } # Get Disks $vmDiskOutput = $null $rowSpanCount = 0 $getVhdErr = $null $vmDisks = Get-VHD -ComputerName $VMHostItem -VMId $vm.VMId -ErrorAction SilentlyContinue -ErrorVariable getVhdErr if ($getVhdErr) { if ($rowSpanCount -eq 0) { $vmDiskOutput +="
" $highL = $true $rowSpanCount = $rowSpanCount + 1 } else { $vmDiskOutput +=" " $highL = $true $rowSpanCount = $rowSpanCount + 1 } } $vmPTDisks = Get-VMHardDiskDrive -ComputerName $VMHostItem -VMname $vm.name | where{$_.Path -like "Disk*"} # Pass-through $vmPTDiskNo = 1 if ($vmPTDisks) { foreach ($vmPTDisk in $vmPTDisks) { if ($rowSpanCount -eq 0) { $vmDiskOutput +=" " $rowSpanCount = $rowSpanCount + 1 $vmPTDiskNo = $vmPTDiskNo + 1 } else { $vmDiskOutput +=" " $rowSpanCount = $rowSpanCount + 1 $vmPTDiskNo = $vmPTDiskNo + 1 } } } # VHD if ($vmDisks -eq $null) { $vmDiskOutput = " " $highL = $true } else { foreach($vmDisk in $vmDisks) { [array]$vmDiskData = $null # Name, Path, Type, Size and File Size $vmDiskName = $vmDisk.Path.Split('\')[-1] $vmDiskPath = $vmDisk.Path $vmDiskType = $vmDisk.VhdType $vmDiskMaxSize = sConvert-Size -DiskVolumeSpace $vmDisk.Size -DiskVolumeSpaceUnit byte $vmDiskFileSize = sConvert-Size -DiskVolumeSpace $vmDisk.FileSize -DiskVolumeSpaceUnit byte # For Cluster Overview if ($VM.IsClustered -eq $true -and $VM.State -eq "Running") { $ovUsedVmVHD = $ovUsedVmVHD + $vmDisk.FileSize $ovTotalVmVHD = $ovTotalVmVHD + $vmDisk.Size } # For Active VHDs File Size $activeVhdFileSize = $vmDisk.FileSize # Get Controller Type $vmDiskControllerType = (Get-VMHardDiskDrive -ComputerName $VMHostItem -VMName $vm.VMName | where{$_.Path -eq $vmDiskPath}).ControllerType # VHD Fragmentation and Color if ($vmDisk.FragmentationPercentage -eq $null) { $vmDiskFragmentation = "N/A" $vmDiskFragmentationBgColor = "" $vmDiskFragmentationTextColor = "" } else { $vmDiskFragmentation = "%$($vmDisk.FragmentationPercentage)" if (($vmDisk.FragmentationPercentage -ge "30") -and ($vmDisk.FragmentationPercentage -lt "50")) { $vmDiskFragmentationBgColor = $stateBgColors[4] $vmDiskFragmentationTextColor = $stateWordColors[4] $highL = $true } elseif ($vmDisk.FragmentationPercentage -ge "50") { $vmDiskFragmentationBgColor = $stateBgColors[3] $vmDiskFragmentationTextColor = $stateWordColors[3] $highL = $true } else { $vmDiskFragmentationBgColor = "" $vmDiskFragmentationTextColor = "" } } # If differencing exist if ($vmDisk.ParentPath) { # Checkpoint label $cpNumber = $null $cpNumber = $vmChekpointCount if ($vmDiskPath.EndsWith(".avhdx",1)) { if (($cpNumber -ne 0) -or ($cpNumber -ne $null)) { $vmDiskName = "Checkpoint $cpNumber" $cpNumber = $cpNumber - 1 } } $vmDiskData += "

$($vmDiskName) *
➤ CurrentFileSize $($vmDiskFileSize[0])$($vmDiskFileSize[1]) (MaximumDiskSize $($vmDiskMaxSize[0])$($vmDiskMaxSize[1]))
➤ $($vmDiskType) VHD | $($vmDiskControllerType) Controller | Fragmentation $($vmDiskFragmentation)

" $parentPath = $vmDisk.ParentPath # Differencing disk loop Do { $vmDiskName = $null $vmDiskPath = $null $vmDiskType = $null $vmDiskMaxSize = $null $vmDiskFileSize = $null $vmDiffDisk = Get-VHD -ComputerName $VMHostItem -Path $parentPath $vmDiskPath = $vmDiffDisk.Path $vmDiskName = $vmDiffDisk.Path.Split('\')[-1] # Checkpoint label if ($vmDiskPath.EndsWith(".avhdx",1)) { if (($cpNumber -ne 0) -or ($cpNumber -ne $null)) { $vmDiskName = "Checkpoint $cpNumber" $cpNumber = $cpNumber - 1 } } $vmDiskType = $vmDiffDisk.VhdType $vmDiskMaxSize = sConvert-Size -DiskVolumeSpace $vmDiffDisk.Size -DiskVolumeSpaceUnit byte $vmDiskFileSize = sConvert-Size -DiskVolumeSpace $vmDiffDisk.FileSize -DiskVolumeSpaceUnit byte # For Active VHD file size $activeVhdFileSize = $activeVhdFileSize + $vmDiffDisk.FileSize # For Cluster Overview if ($VM.IsClustered -eq $true -and $VM.State -eq "Running") { $ovUsedVmVHD = $ovUsedVmVHD + $vmDiffDisk.FileSize } # Disk Fragmentation and Color if ($vmDiffDisk.FragmentationPercentage) { $vmDiskFragmentation = "%$($vmDiffDisk.FragmentationPercentage)" if (($vmDiffDisk.FragmentationPercentage -ge "30") -and ($vmDiffDisk.FragmentationPercentage -lt "50")) { $vmDiskFragmentationBgColor = $stateBgColors[4] $vmDiskFragmentationTextColor = $stateWordColors[4] $highL = $true } elseif ($vmDiffDisk.FragmentationPercentage -ge "50") { $vmDiskFragmentationBgColor = $stateBgColors[3] $vmDiskFragmentationTextColor = $stateWordColors[3] $highL = $true } else { $vmDiskFragmentationBgColor = "" $vmDiskFragmentationTextColor = "" } } $vmDiskData += "

$($vmDiskName) *
➤ CurrentFileSize $($vmDiskFileSize[0])$($vmDiskFileSize[1]) (MaximumDiskSize $($vmDiskMaxSize[0])$($vmDiskMaxSize[1]))
➤ $($vmDiskType) VHD | $($vmDiskControllerType) Controller | Fragmentation $($vmDiskFragmentation)

" $parentPath = $vmDiffDisk.ParentPath } Until ($parentPath -eq $null) } else { $vmDiskData = "

$($vmDiskName) *
➤ CurrentFileSize $($vmDiskFileSize[0])$($vmDiskFileSize[1]) (MaximumDiskSize $($vmDiskMaxSize[0])$($vmDiskMaxSize[1]))
➤ $($vmDiskType) VHD | $($vmDiskControllerType) Controller | Fragmentation $($vmDiskFragmentation)

" } # Active VHD Array ($activeVhds) if ($vm.State -eq "Running") { $vhdHash = @{ Path = $vmDisk.Path Size = $vmDisk.Size FileSize = $activeVhdFileSize Host = $Vm.ComputerName VhdType = $vmDisk.VhdType VhdFormat = $vmDisk.VhdFormat Attached = $vmDisk.Attached VMName = $Vm.VMName } # Create PSCustom object $customObjVHD = New-Object PSObject -Property $vhdHash # Add to Array $activeVhds += $customObjVHD } # Remove top-margin of last item $vmDiskData[-1] = $vmDiskData[-1].Replace("margin-top:5px;","") # Add Indents $itemC = 0 $indentV = ($vmDiskData.count - 1) * 14 Do { $vmDiskData[$itemC] = $vmDiskData[$itemC].Replace("1nd3ntPlaceHolder","$indentV") $indentV = $indentV - 14 $itemC = $itemC + 1 } Until ($itemC -eq $vmDiskData.Count) # Convert String [array]::Reverse($vmDiskData) $vmDiskData = ($vmDiskData -join "").ToString() # Write if ($rowSpanCount -eq 0) { $vmDiskOutput = " " $rowSpanCount = $rowSpanCount + 1 } else { $vmDiskOutput +=" " $rowSpanCount = $rowSpanCount + 1 } } } #If single VHD, rowSpanCount equal to 0 if ($rowSpanCount -eq 1) { $rowSpanCount = 0 } # VM Memory Information if ($VM.DynamicMemoryEnabled) { # Startup Memory $outVmMemStartup = sConvert-Size -DiskVolumeSpace $VM.MemoryStartup -DiskVolumeSpaceUnit byte # Assigned Memory if ($VM.MemoryAssigned -eq 0) { $outVmMemAssigned = "-" } else { $outVmMemAssigned = sConvert-Size -DiskVolumeSpace $VM.MemoryAssigned -DiskVolumeSpaceUnit byte } # Maximum Memory, Minimum Memory $outVmMemMax = sConvert-Size -DiskVolumeSpace $VM.MemoryMaximum -DiskVolumeSpaceUnit byte $outVmMemMin = sConvert-Size -DiskVolumeSpace $VM.MemoryMinimum -DiskVolumeSpaceUnit byte # Charge chargerVmMemoryTable $chargerVmMemoryTable =" " } else { # Startup Memory $outVmMemStartup = sConvert-Size -DiskVolumeSpace $VM.MemoryStartup -DiskVolumeSpaceUnit byte # Charge chargerVmMemoryTable $chargerVmMemoryTable =" " } # Data Line $chargerVmTable +=" " $chargerVmTable += $chargerVmMemoryTable $chargerVmTable +=" " $chargerVmTable += $vmDiskOutput # Output Data if ($HighlightsOnly -eq $false) { $outVMTable += $chargerVmTable $vmNoInTable = $vmNoInTable + 1 } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { $outVMTable += $chargerVmTable $vmNoInTable = $vmNoInTable + 1 } else { # Blank } } } # Error elseif ($getVMerr) { sPrint -Type 0 -Message "$($VMHostItem.ToUpper()): $($getVMerr.exception.message)" -WriteToLogFile $True sPrint -Type 2 -Message "Gathering VM Information for '$($VMHostItem.ToUpper())' failed." -WriteToLogFile $True Start-Sleep -Seconds 3 Continue } else # Blank { sPrint -Type 2 -Message "$($VMHostItem.ToUpper()): Does not have Virtual Machine." -WriteToLogFile $True Start-Sleep -Seconds 3 } # If detected clustered VM configuration resource problem if ($offlineVmConfigs) { ForEach ($offlineVmConfig in $offlineVmConfigs) { # Table TR Color if([bool]!($vmNoInTable%2)) { #Even or Zero $vmTableTrBgColor = "" } else { #Odd $vmTableTrBgColor = "#F9F9F9" } $outVMTable +=" " $vmNoInTable = $vmNoInTable + 1 } } } if (($HighlightsOnly -eq $true) -and ($outVmTable -eq $null) -and ($cntVM -ne 0)) { $outVmTable +=" " } if (($outVmTable -eq $null) -and ($cntVM -eq 0)) { $outVmTable +=" " } # VMs Table - End $outVMTableEnd ="

Name

State

Uptime

Host

vCPU

vRAM
Startup | Min | Max | Assigned

Integration
Services

Check
Point

Replica
Health

Network Adapter

Disk

 $($getVhdErr.count) VHD file(s) missing 
➤ CurrentFileSize N/A (MaximumDiskSize N/A)
➤ VhdType N/A | ControllerType N/A | Fragmentation N/A

 $($getVhdErr.count) VHD file(s) missing 
➤ CurrentFileSize N/A (MaximumDiskSize N/A)
➤ VhdType N/A | ControllerType N/A | Fragmentation N/A

Pass-through disk $vmPTDiskNo *
➤ Path: $($vmPTDisk.Path)
➤ $($vmPTDisk.ControllerType) Controller

Pass-through disk $vmPTDiskNo *
➤ Path: $($vmPTDisk.Path)
➤ $($vmPTDisk.ControllerType) Controller

 Does not have a virtual disk 

$vmDiskData
$vmDiskData

$($outVmMemStartup[0])
$($outVmMemStartup[1])

$($outVmMemMin[0])
$($outVmMemMin[1])

$($outVmMemMax[0])
$($outVmMemMax[1])

$($outVmMemAssigned[0])
$($outVmMemAssigned[1])

$($outVmMemStartup[0])
$($outVmMemStartup[1])

$($outVmName) * $($outVmGenVer)
IsClustered:$($outVmIsClustered)

$($outVmState)

$($outVmUptimeDays)$($outVmUptime)

$($outVmHost)

$($outVmCPU)
CPU

$($outVmIs) *

$($outVmChekpoint)

$($outVmRepl) $($outVmNetAdapter)

$($offlineVmConfig.OwnerGroup) *
IsClustered:Yes

$($offlineVmConfig.State)

-

$outVmHost

-

-

-

-

-

-

 VM cluster configuration resource is $($offlineVmConfig.State) 

  All VMs are healthy  

No virtual machine for reporting

" #endregion #region Gathering Disk/Volume Information #---------------------------------------- # Print MSG sPrint -Type 1 "Gathering Disk/Volume information..." -WriteToLogFile $True # Disks-Volumes-Table Header $outVolumeTableStart += "

$($volumeTableCaption)


" # Generate data lines $outVolumeTable = $null # Cluster if ($Cluster) { $ovUsedStorage = 0 $ovTotalStorage = 0 # Check and get WMI Data $clusResourceDiskData = sGet-Wmi -ComputerName $clusterName -Namespace root\MSCluster -Class MSCluster_Resource -AI -Filter "Type='Physical Disk'" if ($clusResourceDiskData[1] -eq 1) { $clusResourceDiskData = $clusResourceDiskData[0] | Sort-Object $clusResourceToDiskData = gwmi -ComputerName $clusterName -Namespace root\MSCluster -Class MSCluster_ResourceToDisk -Authentication PacketPrivacy -Impersonation Impersonate $clusDiskToDiskPartitionData = gwmi -ComputerName $clusterName -Namespace root\MSCluster -Class MSCluster_DiskToDiskPartition -Authentication PacketPrivacy -Impersonation Impersonate $clusDiskPartitionData = gwmi -ComputerName $clusterName -Namespace root\MSCluster -Class MSCluster_DiskPartition -Authentication PacketPrivacy -Impersonation Impersonate $msftDiskData = gwmi -ComputerName $clusterName -Namespace root\Microsoft\Windows\Storage -Class MSFT_Disk | where{$_.IsClustered -eq $true} $msClusterData = gwmi -ComputerName $clusterName -Namespace root\MSCluster -Class MSCluster_Cluster -Authentication PacketPrivacy -Impersonation Impersonate # If Quorum disk exists, determine the drive letter if ($msClusterData.QuorumTypeValue -eq 3) { if ($msClusterData.QuorumPath) { $quorumPathLetter = ($msClusterData.QuorumPath).Substring(0,2) } else { $quorumPathLetter = $null } } else { $quorumPathLetter = $null } # Each Cluster Disk Resource foreach($clusterDisk in $clusResourceDiskData) { $highL = $false $chargerVolumeTable = $null # Cluster Disk State, If... if ($clusterDisk.State -eq 2) # Online { # IsClusterSharedVolume True if ($clusterDisk.IsClusterSharedVolume -eq $true) { # Clear $clusResourceRELPATH = $null $clusDiskID = $null $shortClusDiskID = $null $clusDiskPartitionPaths = $null # Get DiskID and CSV Paths $clusResourceRELPATH = $clusterDisk.__RELPATH $clusDiskID = ($clusResourceToDiskData | where{$_.GroupComponent -eq $clusResourceRELPATH}).PartComponent $shortClusDiskID = $clusDiskID.TrimStart("MSCluster_Disk.Id=`"").TrimEnd("`"") # Get physical disk information form MSFT_Disk $busTypeName = sConvert-BusTypeName -BusTypeValue ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).BusType $diskPartitionStyle = sConvert-DiskPartitionStyle -PartitionStyleValue ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).PartitionStyle $clusterDiskSize = sConvert-Size -DiskVolumeSpace ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).Size -DiskVolumeSpaceUnit byte $clusterDiskAllocatedSize = sConvert-Size -DiskVolumeSpace ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).AllocatedSize byte $clusterDiskUnAllocatedSize = sConvert-Size -DiskVolumeSpace (($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).Size - ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).AllocatedSize) -DiskVolumeSpaceUnit byte # If maintenance mode enabled if ($clusterDisk.StatusInformation -eq 1) { $clusDiskPartitionPaths = ((($clusDiskToDiskPartitionData | where{$_.GroupComponent -eq $clusDiskID}).PartComponent) -replace "MSCluster_DiskPartition.Path=`"","").TrimEnd("`"") $clusDiskVolumeData = Get-ClusterSharedVolume -Cluster $ClusterName -Name $clusterDisk.Name foreach($clusDiskPartitionPath in $clusDiskPartitionPaths) { $outDiskName = $clusterDisk.Name $outVolumePath = ($clusDiskVolumeData.SharedVolumeInfo | where{$_.Partition.Name -eq $clusDiskPartitionPath}).FriendlyVolumeName $outVolumeName = $outVolumePath.Split("\")[-1] $outVolumeFS = (($clusDiskVolumeData.SharedVolumeInfo.Partition) | where{$_.Name -eq $clusDiskPartitionPath}).FileSystem $outDiskState = "Maintenance","#BDD7EE","#204F7A" $outDiskOwner = $clusterDisk.OwnerNode $outBusType = $busTypeName $outDiskPartStyle = $diskPartitionStyle $outVolumeTotalSize = sConvert-Size -DiskVolumeSpace (($clusDiskVolumeData.SharedVolumeInfo.Partition) | where{$_.Name -eq $clusDiskPartitionPath}).Size -DiskVolumeSpaceUnit byte $outVolumeFreeSpace = sConvert-Size -DiskVolumeSpace (($clusDiskVolumeData.SharedVolumeInfo.Partition) | where{$_.Name -eq $clusDiskPartitionPath}).FreeSpace -DiskVolumeSpaceUnit byte $outVolumeUsedSpace = sConvert-Size -DiskVolumeSpace (($clusDiskVolumeData.SharedVolumeInfo.Partition) | where{$_.Name -eq $clusDiskPartitionPath}).UsedSpace -DiskVolumeSpaceUnit byte $volumeFreePercent = [math]::Round((($clusDiskVolumeData.SharedVolumeInfo.Partition) | where{$_.Name -eq $clusDiskPartitionPath}).PercentFree) $outVolumeFreePercent = " ~%" + $volumeFreePercent + " free " $outVolumeUsage = "CSV" # Volume Free Space Colors $volumeFreeSpaceColors = sConvert-VolumeSizeColors -FreePercent $volumeFreePercent $outVolumeTable +=" " } } else { $clusDiskPartitionPaths = (($clusDiskToDiskPartitionData | where{$_.GroupComponent -eq $clusDiskID}).PartComponent).TrimStart("MSCluster_DiskPartition.Path=`"\\\\?\\Volume").TrimEnd("\\`"") foreach($clusDiskPartitionPath in $clusDiskPartitionPaths) { $highL = $false $outDiskName = $clusterDisk.Name $outVolumePath = ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).MountPoints $outVolumeName = (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).MountPoints).Split("\")[-1] $outVolumeLabel = ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).VolumeLabel $outVolumeFS = ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).FileSystem $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) if ($outDiskState[0] -ne "Online") { $highL = $true } $outDiskOwner = $clusterDisk.OwnerNode $outBusType = $busTypeName $outDiskPartStyle = $diskPartitionStyle $outVolumeTotalSize = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize) -DiskVolumeSpaceUnit mb) $outVolumeFreeSpace = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).FreeSpace) -DiskVolumeSpaceUnit mb) $outVolumeUsedSpace = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize - ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).FreeSpace) -DiskVolumeSpaceUnit mb) $volumeFreePercent = [math]::Round((((($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).FreeSpace) / (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize))) * 100) $outVolumeFreePercent = " ~%" + $volumeFreePercent + " free " $outVolumeUsage = "CSV" # Volume Free Space Colors $volumeFreeSpaceColors = sConvert-VolumeSizeColors -FreePercent $volumeFreePercent # For Cluster Overview $ovUsedStorage = $ovUsedStorage + (($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize - ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).FreeSpace) $ovTotalStorage = $ovTotalStorage + ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize if ($volumeFreePercent -le 10) { $highL = $true } # Active VHD $outActiveVHD = $null $chargerActiveVhd = $null $chargerActiveVhd = $activeVhds | where{$_.Path -like "$outVolumePath*"} if ($chargerActiveVhd) { $activeVhdCount = $chargerActiveVhd.Count $activeVhdTotalFileSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.FileSize | measure -Sum).Sum -DiskVolumeSpaceUnit byte $activeVhdTotalDiskSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.Size | measure -Sum).Sum -DiskVolumeSpaceUnit byte # Color if ((($chargerActiveVhd.Size | measure -Sum).Sum)/1024/1024 -gt ($clusDiskPartitionData | where{$_.Path -match $clusDiskPartitionPath}).TotalSize) { $activeVhdTotalFileSizeColor = "#9C6500","#FBD95B" $highL = $true } else { $activeVhdTotalFileSizeColor = "#BDBDBD","" } $outActiveVHD = "

$($activeVhdCount)
$($activeVhdTotalFileSize[0])$($activeVhdTotalFileSize[1])* / $($activeVhdTotalDiskSize[0])$($activeVhdTotalDiskSize[1])*

" } else { $outActiveVHD = "

0

" } # Data Line $chargerVolumeTable =" " # Add to HTML Table if ($HighlightsOnly -eq $false) { $outVolumeTable += $chargerVolumeTable } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { $outVolumeTable += $chargerVolumeTable } else { # Blank } } } } else # IsClusterSharedVolume False { # Clear $clusResourceRELPATH = $null $clusDiskID = $null $shortClusDiskID = $null $clusDiskPartitionPaths = $null # Get DiskID and Partition Paths (drives) $clusResourceRELPATH = $clusterDisk.__RELPATH $clusDiskID = ($clusResourceToDiskData | where{$_.GroupComponent -eq $clusResourceRELPATH}).PartComponent $shortClusDiskID = $clusDiskID.TrimStart("MSCluster_Disk.Id=`"").TrimEnd("`"") $clusDiskPartitionPaths = ($clusDiskToDiskPartitionData | where{$_.GroupComponent -eq $clusDiskID}).PartComponent # Get physical disk information form MSFT_Disk $busTypeName = sConvert-BusTypeName -BusTypeValue ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).BusType $diskPartitionStyle = sConvert-DiskPartitionStyle -PartitionStyleValue ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).PartitionStyle $clusterDiskSize = sConvert-Size -DiskVolumeSpace ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).Size -DiskVolumeSpaceUnit byte $clusterDiskAllocatedSize = sConvert-Size -DiskVolumeSpace ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).AllocatedSize byte $clusterDiskUnAllocatedSize = sConvert-Size -DiskVolumeSpace (($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).Size - ($msftDiskData | where{(($_.Signature -eq $shortClusDiskID) -or ($_.Guid -eq $shortClusDiskID))}).AllocatedSize) -DiskVolumeSpaceUnit byte # If partition(s) on physical disk exists if ($clusDiskPartitionPaths) { # Get partition (volume) information foreach ($clusDiskPartitionPath in $clusDiskPartitionPaths) { $highL = $false $assignedPT = $false $driveLetterExist = $true $outDiskName = $clusterDisk.Name $outDiskOwner = $clusterDisk.OwnerNode $outBusType = $busTypeName $outDiskPartStyle = $diskPartitionStyle # Disk State if ($clusterDisk.StatusInformation -eq 1) { # In maintenance mode $outDiskState = "Maintenance","#BDD7EE","#204F7A" } else { $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) } if ($outDiskState[0] -ne "Online") { $highL = $true } $outVolumeLabel = ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).VolumeLabel $outVolumeFS = ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).FileSystem # Volume Name if ($clusDiskPartitionPath -match "Volume") { # Missing Volume (drive) Letter $outVolumeName = " No Letter " $highL = $true $driveLetterExist = $false } elseif ($clusDiskPartitionPath -match "GLOBALROOT") { # PT $outVolumeName = "PT Disk" $outVolumeLabel = "Assigned to '$($clusterDisk.OwnerGroup)' as a pass-through disk" $outVolumeFS = "-" $assignedPT = $true } else { $outVolumeName = ($clusDiskPartitionPath -replace "MSCluster_DiskPartition.Path=`"","").TrimEnd("`"") } # Volume Usage Type if ($outVolumeName -eq $quorumPathLetter) { $outVolumeUsage = "Quorum" } elseif ($assignedPT -eq $True) { $outVolumeUsage = "Pass-through" } else { $outVolumeUsage = "Volume" } # Volume Info if (!$assignedPT) { $outVolumeTotalSize = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize) -DiskVolumeSpaceUnit mb) $outVolumeFreeSpace = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).FreeSpace) -DiskVolumeSpaceUnit mb) $outVolumeUsedSpace = (sConvert-Size -DiskVolumeSpace (($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize - ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).FreeSpace) -DiskVolumeSpaceUnit mb) $volumeFreePercent = [math]::Round((((($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).FreeSpace) / (($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize))) * 100) $outVolumeFreePercent = " ~%" + $volumeFreePercent + " free " # For Cluster Overview if(($outVolumeUsage -eq "Volume") -and ($clusterDisk.StatusInformation -ne 1) -and ($driveLetterExist -eq $true)) { $ovUsedStorage = $ovUsedStorage + (($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize - ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).FreeSpace) $ovTotalStorage = $ovTotalStorage + ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize } # Active VHD $outActiveVHD = $null $chargerActiveVhd = $null $chargerActiveVhd = $activeVhds | where{($_.Path -like "$outVolumeName*") -and ($_.Host -eq $clusterDisk.OwnerNode)} if ($chargerActiveVhd) { $activeVhdCount = $chargerActiveVhd.Count $activeVhdTotalFileSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.FileSize | measure -Sum).Sum -DiskVolumeSpaceUnit byte $activeVhdTotalDiskSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.Size | measure -Sum).Sum -DiskVolumeSpaceUnit byte # Color if ((($chargerActiveVhd.Size | measure -Sum).Sum)/1024/1024 -gt ($clusDiskPartitionData | where{$_.__RELPATH -eq $clusDiskPartitionPath}).TotalSize) { $activeVhdTotalFileSizeColor = "#9C6500","#FBD95B" $highL = $true } else { $activeVhdTotalFileSizeColor = "#BDBDBD","" } $outActiveVHD = "

$($activeVhdCount)
$($activeVhdTotalFileSize[0])$($activeVhdTotalFileSize[1])* / $($activeVhdTotalDiskSize[0])$($activeVhdTotalDiskSize[1])*

" } else { $outActiveVHD = "

0

" } } else { $outVolumeTotalSize = "-","" $outVolumeFreeSpace = "-","" $outVolumeUsedSpace = "-","" $volumeFreePercent = 100 $outVolumeFreePercent = $null $outActiveVHD = "

-

" } # Volume Free Space Colors $volumeFreeSpaceColors = sConvert-VolumeSizeColors -FreePercent $volumeFreePercent if ($volumeFreePercent -le 10) { $highL = $true } $chargerVolumeTable = " " # Add to HTML Table if ($HighlightsOnly -eq $false) { $outVolumeTable += $chargerVolumeTable } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { $outVolumeTable += $chargerVolumeTable } else { # Blank } if ($assignedPT -eq $True) { Break } } } else { $highL = $false $outDiskName = $clusterDisk.Name $outDiskOwner = $clusterDisk.OwnerNode $outBusType = $busTypeName $outDiskPartStyle = $diskPartitionStyle # Disk State if ($clusterDisk.StatusInformation -eq 1) { # In maintenance mode $outDiskState = "Maintenance","#BDD7EE","#204F7A" } else { $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) } if ($outDiskState[0] -ne "Online") { $highL = $true } # OwnerGroup if ($clusterDisk.OwnerGroup -eq "Available Storage") { $outVolumeName = "Unassigned Disk" $outVolumeUsage = "Unassigned" $outVolumeLabel = "This clustered disk has not assigned for any purpose" } else { $outVolumeName = "PT Disk" $outVolumeUsage = "Pass-through" $outVolumeLabel = "Assigned to '$($clusterDisk.OwnerGroup)' as a pass-through disk" } # Volume $outVolumeFS = "-" $outVolumeTotalSize = "-","" $outVolumeFreeSpace = "-","" $outVolumeUsedSpace = "-","" $volumeFreePercent = "" $outVolumeFreePercent = $null $volumeFreeSpaceColors = "","","" $chargerVolumeTable = " " # Add to HTML Table if ($HighlightsOnly -eq $false) { $outVolumeTable += $chargerVolumeTable } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { $outVolumeTable += $chargerVolumeTable } else { # Blank } } } } elseif (($clusterDisk.State -eq 3) -or ($clusterDisk.State -eq 127)) # Offline { if ($HighlightsOnly -eq $false) { $outDiskName = $clusterDisk.Name $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) $outDiskOwner = $clusterDisk.OwnerNode $outVolumeTable +=" " } } elseif (($clusterDisk.State -eq 4) -or ($clusterDisk.State -eq 126)) # Failed { $outDiskName = $clusterDisk.Name $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) $outDiskOwner = $clusterDisk.OwnerNode $outVolumeTable +=" " } else # Others { $outDiskName = $clusterDisk.Name $outDiskState = (sConvert-ClusterDiskState -StateValue $clusterDisk.State) $outDiskOwner = $clusterDisk.OwnerNode $outVolumeTable +=" " } } } elseif ($clusResourceDiskData[1] -eq 2) { $outVolumeTable +=" " } else { sPrint -Type 0 -Message "$ClusterName`: Gathering Disk/Volume information failed. $($clusResourceDiskData[0])" -WriteToLogFile $True Start-Sleep -Seconds 3 $outVolumeTable +=" " } } # Standalone if ($VMHost) { $Computers = $VMHosts foreach ($ComputerName in $Computers) { $LogicalDisks = sGet-Wmi -ComputerName $ComputerName -Namespace root\CIMv2 -Class Win32_LogicalDisk -AI -Filter "DriveType='3'" if ($LogicalDisks[1] -eq 1) { $LogicalDisks = $LogicalDisks[0] $SystemDrive = ((gwmi -ComputerName $ComputerName -Class Win32_OperatingSystem).SystemDirectory).Substring(0,2) # Get WMI Data $logicalToDiskPartitionData = gwmi -ComputerName $ComputerName Win32_LogicalDiskToPartition $physicalDiskPathData = gwmi -ComputerName $ComputerName Win32_DiskDriveToDiskPartition $physicalDiskNameData = gwmi -ComputerName $ComputerName Win32_DiskDrive $msftDiskIdData = gwmi -ComputerName $ComputerName -Namespace root\Microsoft\Windows\Storage -Class MSFT_Partition $msftDiskData = gwmi -ComputerName $ComputerName -Namespace root\Microsoft\Windows\Storage -Class MSFT_Disk # Each logical disk foreach ($LogicalDisk in $LogicalDisks) { $highL = $false # Clear variables $chargerVolumeTable = $null $logicalToDiskPartition = $null $physicalDiskPath = $null $physicalDiskName = $null $msftDiskId = $null $msftDisk = $null # Filter for physical disk name $logicalToDiskPartition = ($logicalToDiskPartitionData | where{$_.Dependent -eq $LogicalDisk.Path}).Antecedent $physicalDiskPath = ($physicalDiskPathData | where{$_.Dependent -eq $logicalToDiskPartition}).Antecedent $physicalDiskName = (($physicalDiskNameData | where{($_.Path).Path -eq $physicalDiskPath}).Name).Replace("\\.\PHYSICALDRIVE","Disk ") # Filter for other physical disk information $msftDiskId = ($msftDiskIdData | where{$_.DriveLetter -eq ($LogicalDisk.DeviceID).TrimEnd(":")}).DiskId $msftDisk = $msftDiskData | where{$_.ObjectId -eq $msftDiskId} # Logical disk (volume) information $outLogicalDiskName = $LogicalDisk.Name $outLogicalDiskVolumeName = $LogicalDisk.VolumeName $outLogicalDiskFS = $LogicalDisk.FileSystem $outLogicalDiskUsedSpace = sConvert-Size -DiskVolumeSpace ($LogicalDisk.Size - $LogicalDisk.FreeSpace) -DiskVolumeSpaceUnit byte $outLogicalDiskFreeSpace = sConvert-Size -DiskVolumeSpace $LogicalDisk.FreeSpace -DiskVolumeSpaceUnit byte $outLogicalDiskSize = sConvert-Size -DiskVolumeSpace $LogicalDisk.Size -DiskVolumeSpaceUnit byte $LogicalDiskFreePercent = [math]::Round((($LogicalDisk.FreeSpace) / ($LogicalDisk.Size)) * 100) $outLogicalDiskFreePercent = " ~%" + $LogicalDiskFreePercent + " free " # Physical disk information $outPhysicalDiskName = $physicalDiskName.Replace("PHYSICALDRIVE","Disk") $outMsftDiskSize = sConvert-Size -DiskVolumeSpace $msftDisk.Size -DiskVolumeSpaceUnit byte $outMsftDiskAllocatedSize = sConvert-Size -DiskVolumeSpace $msftDisk.AllocatedSize -DiskVolumeSpaceUnit byte if (($msftDisk.Size -eq $null) -and ($msftDisk.AllocatedSize -eq $null)) { $outmsftDiskUnallocatedSize = "N/A","" } else { $outmsftDiskUnallocatedSize = sConvert-Size -DiskVolumeSpace ($msftDisk.Size - $msftDisk.AllocatedSize) -DiskVolumeSpaceUnit byte } $outMsftDiskBusType = sConvert-BusTypeName -BusTypeValue $msftDisk.BusType $outMsftDiskPartitionStyle = sConvert-DiskPartitionStyle -PartitionStyleValue $msftDisk.PartitionStyle $msftDiskState = "Online" $msftDiskOwner = ($ComputerName).ToUpper() # Volume usage type if ($LogicalDisk.Name -eq $SystemDrive) { $LogicalDiskUsage = "System" } else { $LogicalDiskUsage = "Volume" } # Volume Free Space Colors $LogicalDiskFreeSpaceColors = sConvert-VolumeSizeColors -FreePercent $LogicalDiskFreePercent if ($LogicalDiskFreePercent -le 10) { $highL = $true } # Active VHD $outActiveVHD = $null $chargerActiveVhd = $null $chargerActiveVhd = $activeVhds | where{($_.Path -like "$outLogicalDiskName*") -and ($_.Host -eq $msftDiskOwner)} if ($chargerActiveVhd) { $activeVhdCount = $chargerActiveVhd.Count $activeVhdTotalFileSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.FileSize | measure -Sum).Sum -DiskVolumeSpaceUnit byte $activeVhdTotalDiskSize = sConvert-Size -DiskVolumeSpace ($chargerActiveVhd.Size | measure -Sum).Sum -DiskVolumeSpaceUnit byte # Color if (($chargerActiveVhd.Size | measure -Sum).Sum -gt ($LogicalDisk.Size)) { $activeVhdTotalFileSizeColor = "#9C6500","#FBD95B" $highL = $true } else { $activeVhdTotalFileSizeColor = "#BDBDBD","" } $outActiveVHD = "

$($activeVhdCount)
$($activeVhdTotalFileSize[0])$($activeVhdTotalFileSize[1])* / $($activeVhdTotalDiskSize[0])$($activeVhdTotalDiskSize[1])*

" } else { $outActiveVHD = "

0

" } # Data Line $chargerVolumeTable =" " # Add to HTML Table if ($HighlightsOnly -eq $false) { $outVolumeTable += $chargerVolumeTable } elseif (($HighlightsOnly -eq $true) -and ($highL -eq $true)) { $outVolumeTable += $chargerVolumeTable } else { # Blank } } } elseif ($LogicalDisks[1] -eq 2) { $outVolumeTable +=" " Continue } else { # Error sPrint -Type 0 -Message "$($ComputerName.ToUpper()): Gathering Disk/Volume information failed. $($LogicalDisks[0])" -WriteToLogFile $True Start-Sleep -Seconds 3 $outVolumeTable +=" " Continue } } } if ($outVolumeTable -eq $null) { if ($Cluster) { $outVolumeTable +=" " } else { $outVolumeTable +=" " } } # HTML Disk Table - End $outVolumeTableEnd ="

Name

State

Usage

Owner

Bus
Type

Partition
Style

File
System

Active
VHD

Used
Size

Free
Size

Total
Size

$($outVolumeName) *
$($outDiskName) *

$($outDiskState[0])

$outVolumeUsage

$outDiskOwner

$outBusType

$outDiskPartStyle

$outVolumeFS

-

$($outVolumeUsedSpace[0])
$($outVolumeUsedSpace[1])

$($outVolumeFreeSpace[0])
$($outVolumeFreeSpace[1])

$($outVolumeTotalSize[0]) $($outVolumeTotalSize[1])
$outVolumeFreePercent

$($outVolumeName) *
$($outDiskName) *

$($outDiskState[0])

$outVolumeUsage

$outDiskOwner

$outBusType

$outDiskPartStyle

$outVolumeFS

$outActiveVHD

$($outVolumeUsedSpace[0])
$($outVolumeUsedSpace[1])

$($outVolumeFreeSpace[0])
$($outVolumeFreeSpace[1])

$($outVolumeTotalSize[0]) $($outVolumeTotalSize[1])
$outVolumeFreePercent

$($outVolumeName) *
$($outDiskName) *

$($outDiskState[0])

$outVolumeUsage

$outDiskOwner

$outBusType

$outDiskPartStyle

$outVolumeFS

$outActiveVHD

$($outVolumeUsedSpace[0])
$($outVolumeUsedSpace[1])

$($outVolumeFreeSpace[0])
$($outVolumeFreeSpace[1])

$($outVolumeTotalSize[0]) $($outVolumeTotalSize[1])
$outVolumeFreePercent

$($outVolumeName) *
$($outDiskName) *

$($outDiskState[0])

$outVolumeUsage

$outDiskOwner

$outBusType

$outDiskPartStyle

$outVolumeFS

-

$($outVolumeUsedSpace[0])
$($outVolumeUsedSpace[1])

$($outVolumeFreeSpace[0])
$($outVolumeFreeSpace[1])

$($outVolumeTotalSize[0]) $($outVolumeTotalSize[1])
$outVolumeFreePercent

$outDiskName *
Unknown Volume

$($outDiskState[0])

-

$outDiskOwner

-

-

-

-

-

-

-

$outDiskName *
Unknown Volume

$($outDiskState[0])

-

$outDiskOwner

-

-

-

-

-

-

-

$outDiskName *
Unknown Volume

$($outDiskState[0])

-

$outDiskOwner

-

-

-

-

-

-

-

Hyper-V Cluster does not have any Disk/Volume

$($clusResourceDiskData[0])

$($outLogicalDiskName) *
$($outPhysicalDiskName) *

$($msftDiskState)

$LogicalDiskUsage

$msftDiskOwner

$outMsftDiskBusType

$outMsftDiskPartitionStyle

$outLogicalDiskFS

$outActiveVHD

$($outLogicalDiskUsedSpace[0])
$($outLogicalDiskUsedSpace[1])

$($outLogicalDiskFreeSpace[0])
$($outLogicalDiskFreeSpace[1])

$($outLogicalDiskSize[0]) $($outLogicalDiskSize[1])
$outLogicalDiskFreePercent

$($ComputerName.ToUpper()) does not have any Disk/Volume

$($LogicalDisks[0])

  All Clustered Disks/Volumes are healthy  

  All Local Disks/Volumes are healthy  

" #endregion #region Generate Cluster Overview Information #--------------- if($ovTotalNode -eq $null){$ovTotalNode = 0} if($ovTotalVm -eq $null){$ovTotalVm = 0} if($ovRunningVm -eq $null){$ovRunningVm = 0} if($ovTotalLP -eq $null){$ovTotalLP = 0} $ovUsedMemory = sConvert-Size -DiskVolumeSpace $ovUsedMemory -DiskVolumeSpaceUnit kb if($ovUsedMemory[0] -eq "N/A"){$ovUsedMemory = 0,"GB"} $ovTotalMemory = sConvert-Size -DiskVolumeSpace $ovTotalMemory -DiskVolumeSpaceUnit kb if($ovTotalMemory[0] -eq "N/A"){$ovTotalMemory = 0,"GB"} $ovUsedStorage = sConvert-Size -DiskVolumeSpace $ovUsedStorage -DiskVolumeSpaceUnit mb if($ovUsedStorage[0] -eq "N/A"){$ovUsedStorage = 0,"GB"} $ovTotalStorage = sConvert-Size -DiskVolumeSpace $ovTotalStorage -DiskVolumeSpaceUnit mb if($ovTotalStorage[0] -eq "N/A"){$ovTotalStorage = 0,"GB"} $ovUsedVmMemory = sConvert-Size -DiskVolumeSpace $ovUsedVmMemory -DiskVolumeSpaceUnit byte if($ovUsedVmMemory[0] -eq "N/A"){$ovUsedVmMemory = 0,"GB"} $ovTotalVmMemory = sConvert-Size -DiskVolumeSpace $ovTotalVmMemory -DiskVolumeSpaceUnit byte if($ovTotalVmMemory[0] -eq "N/A"){$ovTotalVmMemory = 0,"GB"} $ovUsedVmVHD = sConvert-Size -DiskVolumeSpace $ovUsedVmVHD -DiskVolumeSpaceUnit byte if($ovUsedVmVHD[0] -eq "N/A"){$ovUsedVmVHD = 0,"GB"} $ovTotalVmVHD = sConvert-Size -DiskVolumeSpace $ovTotalVmVHD -DiskVolumeSpaceUnit byte if($ovTotalVmVHD[0] -eq "N/A"){$ovTotalVmVHD = 0,"GB"} $outClusterOverview = "

Cluster Overview (HVCluster)



$($ovUpNode)

Up

$($ovTotalLP)

$($ovUsedMemory[0])

$($ovUsedMemory[1])

$($ovUsedStorage[0])

$($ovUsedStorage[1])

/$($ovTotalNode)

/$($ovTotalMemory[0]) $($ovTotalMemory[1])

/$($ovTotalStorage[0]) $($ovTotalStorage[1])

Node *

Logical Processor *

Memory *

Storage *




$($ovRunningVm)

Running

$($ovTotalVProc)

$($ovUsedVmMemory[0])

$($ovUsedVmMemory[1])

$($ovUsedVmVHD[0])

$($ovUsedVmVHD[1])

/$($ovTotalVm)

/$($ovTotalVmMemory[0]) $($ovTotalVmMemory[1])

/$($ovTotalVmVHD[0]) $($ovTotalVmVHD[1])

vMachine *

vProcessor *

vMemory *

vStorage *


" #endregion #region HTML End #--------------- $outHtmlEnd ="

ScriptVersion: 1.5 | CreatedBy: Serhat AKINCI - Hyper-V MVP - @serhatakinci | Feedback: serhatakinci@gmail.com


" # Print MSG sPrint -Type 1 -Message "Writing output to file $ReportFile" -WriteToLogFile $True if ($Cluster) { $outFullHTML = $outHtmlStart + $outClusterOverview + $outVMHostTableStart + $outVMHostTable + $outVMHostTableEnd + $outVolumeTableStart + $outVolumeTable + $outVolumeTableEnd + $outVMTableStart + $outVMTable + $outVMTableEnd + $outHtmlEnd } else { $outFullHTML = $outHtmlStart + $outVMHostTableStart + $outVMHostTable + $outVMHostTableEnd + $outVolumeTableStart + $outVolumeTable + $outVolumeTableEnd + $outVMTableStart + $outVMTable + $outVMTableEnd + $outHtmlEnd } $outFullHTML | Out-File $ReportFile if (Test-Path -Path $ReportFile) { sPrint -Type 1 -Message "Report created successfully." -WriteToLogFile $True } else { sPrint -Type 2 -Message "Reporting file could not be created. Please review the log file." -WriteToLogFile $True } #endregion #region Send Mail #--------------- if ($SendMail -or $SMTPServer) { if ($SMTPServer -and $MailFrom -and $MailTo) { sPrint -Type 1 -Message "Sending e-mail..." -WriteToLogFile $True $subject = "Hyper-V Environment Report" $attachment = $ReportFile $MailTo = ($MailTo -join ',').ToString() $mailMessage = New-Object System.Net.Mail.MailMessage $mailMessage.subject = $subject $mailMessage.to.add($MailTo) $mailMessage.from = $MailFrom $mailMessage.attachments.add($attachment) $smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort) if ($MailFromPassword) { $smtp.UseDefaultCredentials = $false $smtp.Credentials = New-Object System.Net.NetworkCredential($MailFrom, $MailFromPassword) } if ($SMTPServerTLSorSSL) { $smtp.EnableSSL = $true } $smtpSendResult = 1 Try { $smtp.send($mailMessage) } Catch { sPrint -Type 0 -Message "E-Mail could not be sent: $($_.Exception.Message)" -WriteToLogFile $True $smtpSendResult = 0 } if ($smtpSendResult -eq 1) { sPrint -Type 1 -Message "E-mail has been sent to the address(es): $MailTo" -WriteToLogFile $True } Remove-Variable -Name smtp Remove-Variable -Name MailFromPassword } else { sPrint -Type 0 -Message "Missing parameter(s): -SMTPServer and(or) -MailFrom and(or) -MailTo" -WriteToLogFile $True } } sPrint -Type 1 "Completed!" -WriteToLogFile $True sPrint -Type 5 -Message "----- End -----" -WriteToLogFile $true #endregion