# Generate HyperV VM Dashboard # Version - 3.3 # Created By - Shabarianth Ramadasan - InsideVirtualization.Com # shabarinath@insidevirtualization.com # Date - 10/12/2014 Import-Module -Name FailoverClusters, hyper-v $ResultFile = "HyperV-VM-Report.htm" New-Item -ItemType File $ResultFile -Force [Array]$WarningLevel = "#77FF5B","#FFF632","#FF6B6B","#FF0040" Function Get-CSVtoPhysicalDiskMapping { param ($volumeowner, $csvvolume) $cimSession = New-CimSession -ComputerName $volumeowner $volumeInfo = Get-Disk -CimSession $cimSession | Get-Partition | Select DiskNumber, @{Name="Volume";Expression={Get-Volume -Partition $_ | Select -ExpandProperty ObjectId}} $csvdisknumber = ($volumeinfo | ? { $_.Volume -eq $csvVolume}).Disknumber $DiskDetails = Get-Disk -CimSession $cimSession -Number $csvdisknumber $CSVStorage[$h].DiskType = $DiskDetails.ProvisioningType $CSVStorage[$h].RaidType = $DiskDetails.Model $CSVStorage[$h].StorageInformation = $DiskDetails.Manufacturer $CSVStorage[$h].Connectivity = $DiskDetails.BusType } Function fWriteHtmlHeader { param($FileName) $date = ( get-date ).ToString('yyyy/MM/dd') Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName 'Hyper-V Dashboard' Add-Content $FileName '" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
" Add-Content $FileName "Hyper-V - VM Dashboard - $date" Add-Content $FileName "
" } Function fWriteLegendTable { Param($FileName, $wl0, $wl1, $wl2, $wl3) Add-Content $FileName "
" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
Storage HealthHost Memory Health
Volume Free Space - Less than 10 % or 50 GBAvailable Memory - Less than 10 % or 10 GB
Volume Free Space - Less than 20 % or 100 GBAvailable Memory - Less than 20 % or 20 GB
Volume Free Space - Less than 30 % or 200 GBAvailable Memory - Less than 30 % or 30 GB
" } Function fWriteSubHeadingClusterOrStandAlone { Param ($FileName, $cname) Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
$cname
" } Function fWriteVMTableHeader { Param($FileName) Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" } Function fWriteHostStatusTableHeader { Param($FileName) Add-Content $FileName "
VMUp-TimeIC VersionClusteredvProcessorvRAM-StartUpvRAM-MinvRAM-MaxvRAM-AvgvDisk1-StoragevDisk1-AllocatedvDisk1-UsagevDisk1-FPvDisk2-StoragevDisk2-AllocatedvDisk2-UsagevDisk2-FPvNicFirstSnapShotDate
" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" } Function fWriteStorageTableHeader { Param($FileName) Add-Content $FileName "
Server NameTotal VMsTotal Physical MemoryAvailable Physical MemoryAvailable Physical Memory (%)Total VM Startup MemoryToatl VM Max MemoryPhysical ProcessorsCoresLogical ProcessorsTotal vProcs
" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" } Function fWriteHtmlFooter { Param($FileName) Add-Content $FileName "" Add-Content $FileName "" } Function fWriteSubRowNodeName { Param ($FileName, $nodeName, $TotMem, $AvailMem, $AvailMemPC, $hostMemHealth) Add-Content $FileName "
Volume NameVolume TypeRAID LevelStorageStorageConnectionTotal CapacityCurrent Free SpaceFree Space (%)VHDX-Size-AllocatedVHDX-Size-ActualOver-Provisioned (GB)Over-Provisioned(%)
" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
$nodeNameTotal Memory$TotMem GBAvailable Memory$AvailMem GBAvailable Memory (%)$AvailMemPC %
" } Function fWriteSubHeadingCSVDetails { Param ($FileName, $cluname) Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
Storage Report - $CluName
" } Function fWriteSubHeadingHostDetails { Param ($FileName, $cluname) Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "" Add-Content $FileName "
Host Report - $CluName
" } Function fWriteVMInfo { Param($FileName, $vmname, $utime, $ic, $clusterrole, $vProc, $Startmem, $MinMem, $MaxMem, $AvgMem, $hostmemhealth, $vd1storage, $vd1, $vdu1, $vd1FP, $vd1StorageHealth, $vdtype1, $vd2Storage, $vd2, $vdu2, $vd2fp, $vd2StorageHealth, $vdtype2, $vNetworkInterfaceType, $SSDate, $ICStatus, $vhealth) Add-Content $FileName "" Add-Content $FileName "$vmname" Add-Content $FileName "$utime" If ($ICStatus -match "Update required") { Add-Content $FileName "$ic" } Else { Add-Content $FileName "$ic" } Add-Content $FileName "$clusterrole" Add-Content $FileName "$vProc" Add-Content $FileName "$StartMem GB" If ($MinMem -eq "DM Disabled") { Add-Content $FileName "$MinMem" Add-Content $FileName "$MaxMem" } Else { Add-Content $FileName "$MinMem GB" Add-Content $FileName "$MaxMem GB" } If ($AvgMem -eq "NA") { Add-Content $FileName "$AvgMem" } Else { Add-Content $FileName "$AvgMem GB" } Add-Content $FileName "$vd1Storage" If (($vdtype1 -eq "vhd") -OR ($vhdtype1 -eq "avhd")) { Add-Content $FileName "$vd1 GB" Add-Content $FileName "$vdu1 GB" } Else { Add-Content $FileName "$vd1 GB" Add-Content $FileName "$vdu1 GB" } Add-Content $FileName "$vd1fp" If ($vd2Storage -match "NA") { Add-Content $FileName "$vd2Storage" Add-Content $FileName "$vd2" Add-Content $FileName "$vdu2" Add-Content $FileName "$vd2fp" } ElseIF ($vd2Storage -ne "NA") { Add-Content $FileName "$vd2Storage" If (($vdtype2 -eq "vhd") -OR ($vhdtype2 -eq "avhd")) { Add-Content $FileName "$vd2 GB" Add-Content $FileName "$vdu2 GB" Add-Content $FileName "$vd2fp" } Else { Add-Content $FileName "$vd2 GB" Add-Content $FileName "$vdu2 GB" Add-Content $FileName "$vd2fp" } } If ($vNetworkInterfaceType -eq "False") { Add-Content $FileName "Legacy" } Else { Add-Content $FileName "Synthetic" } Add-Content $FileName "$SSDate" Add-Content $FileName "" } Function fWriteHostInfo { Param($FileName, $bgC, $sName, $tVMs, $pMem, $aMem, $aMempc, $allocStartup, $allocMaxmem, $aProc, $aCore, $aLogicalProc, $allocvProc, $hhealth) If ($sName -match "TOTAL") { [int] $aMempc = $aMem*100/$pMem } Add-Content $FileName "" Add-Content $FileName "$sName" Add-Content $FileName "$tVMs" Add-Content $FileName "$pMem GB" Add-Content $FileName "$aMem GB" Add-Content $FileName "$aMemPC %" Add-Content $FileName "$allocStartup GB" Add-Content $FileName "$allocMaxmem GB" Add-Content $FileName "$aProc" Add-Content $FileName "$aCore" Add-Content $FileName "$aLogicalProc" Add-Content $FileName "$allocvProc" Add-Content $FileName "" } Function fWriteStorageInfo { Param($FileName, $volName, $volType, $volRaid, $volStorage, $volConnection, $volTotal, $volFree, $volFreePC, $VHDXAlloc, $VHDXActual, $OverProvisioned, $pcoverprov, $volhealthcode) If($volName -Match "TOTAL") { Add-Content $FileName "" [int] $volFreePC = (($volFree*100)/($volTotal*1024)) } Else { Add-Content $FileName "" } Add-Content $FileName "$volName" Add-Content $FileName "$volType" Add-Content $FileName "$volRaid" Add-Content $FileName "$volStorage" Add-Content $FileName "$volConnection" Add-Content $FileName "$volTotal TB" Add-Content $FileName "$volFree GB" Add-Content $FileName "$volFreePC %" Add-Content $FileName "$VHDXAlloc GB" Add-Content $FileName "$VHDXActual GB" Add-Content $FileName "$OverProvisioned" Add-Content $FileName "$pcoverprov" Add-Content $FileName "" } Function fCreateDashBoard { Param($Name, $Type) If ($Type -match "Cluster") { Write-Host ("Fetching CSV Information from Cluster "+$Name) -foregroundcolor green fWriteSubHeadingClusterOrStandAlone $ResultFile ("Cluster - " +$name) [array] $CSVstorage = Get-ClusterSharedVolume -Cluster $Name| select Ownernode -Expand SharedVolumeInfo |select FriendlyVolumeName, Ownernode , @{n="Name";e={($_.friendlyvolumename).TrimStart("C:\ClusterStorage\")}}, @{n="Capacity";e={$_.Partition.Size}}, @{n="UsedSpace";e={$_.Partition.UsedSpace}}, @{n="FreeSpace";e={$_.Partition.FreeSpace}}, @{n="FreeSpacePC";e={$_.Partition.PercentFree}}, @{n="VHDXAllocatedSpace";e={0}}, @{n="VHDXActualUsage";e={0}}, @{n="VolumeHealthCode";e={[int]"0"}} , @{n="DiskGuid";e={$_.Partition.Name}}, @{n="DiskType";e={"NA"}}, @{n="RaidType";e={"NA"}}, @{n="Connectivity";e={"NA"}}, @{n="StorageInformation";e={"NA"}} $CSVCount = $CSVStorage.length For ($h=0; $h -lt $CSVCount; $h++) { Get-CSVtoPhysicalDiskMapping $CSVStorage[$h].OwnerNode.name $CSVStorage[$h].DiskGuid If (($CSVStorage.FreeSpacepc[$h] -le "10") -OR ($CSVStorage.FreeSpace[$h] -lt "53687091200")){[int] $CSVStorage[$h].VolumeHealthCode = "3"} ElseIf ((($CSVStorage.FreeSpacepc[$h] -le "20") -And ($CSVStorage.FreeSpace[$h] -gt 10)) -OR ($CSVStorage.FreeSpace[$h] -lt "107374182400")){[int] $CSVStorage[$h].VolumeHealthCode = "2"} ElseIf ((($CSVStorage.FreeSpacepc[$h] -le "30") -And ($CSVStorage.FreeSpace[$h] -gt 20)) -OR ($CSVStorage.FreeSpace[$h] -lt "214748364800")){[int] $CSVStorage[$h].VolumeHealthCode = 1} Else {$CSVStorage[$h].VolumeHealthCode = 0} } [array] $cNodes = Get-Cluster $Name|Get-ClusterNode |Where {$_.state -eq "Up"} |Select Name $nodecount = $cNodes.length [array] $hostDetails = Get-Cluster $Name|Get-ClusterNode|Select name, @{Label="TotalPhysicalMemory"; Expression={[int]""}}, @{Label="AvailablePhysicalMemory"; Expression={[int]""}}, @{Label="AvailablePhysicalMemoryPC"; Expression={[int]""}}, @{Label="TotalVMStartupRamAllocated"; Expression={[int]""}}, @{Label="TotalVMMaxMemoryAllocated"; Expression={[int]""}}, @{Label="Processors"; Expression={[int]""}}, @{Label="ProcessorCore"; Expression={[int]""}}, @{Label="LogicalProcessors"; Expression={[int]""}}, @{Label="vProcessors"; Expression={[int]""}}, @{Label="HostMemoryHealth"; Expression={[int]""}}, @{Label="VMs"; Expression={[int]""}} For ($i=0; $i -lt $nodecount; $i++) { Write-Host ("Processing Hyper-V Host "+$cNodes.Name[$i]) -Foregroundcolor Green -BackgroundColor DARKGREEN $hDetails = Get-WmiObject -Class win32_OperatingSystem -ComputerName $cNodes.Name[$i] |Select FreePhysicalMemory, TotalVisibleMemorySize [int] $hostDetails[$i].TotalPhysicalMemory = ((($hDetails).TotalVisibleMemorySize)/1048576) [int] $hostDetails[$i].AvailablePhysicalMemoryPC = (((($hDetails).FreePhysicalMemory)/(($hDetails).TotalVisibleMemorySize))*100) [int] $hostDetails[$i].AvailablePhysicalMemory = ((($hDetails).FreePhysicalMemory)/1048576) If (($hostDetails[$i].AvailablePhysicalMemoryPC -le "10") -OR ($hostDetails[$i].AvailablePhysicalMemory -lt "10")) { [int] $hostDetails[$i].HostMemoryHealth = "3" } ElseIf ((($hostDetails[$i].AvailablePhysicalMemoryPC -le "20") -And ($hostDetails[$i].AvailablePhysicalMemoryPC -gt "10")) -OR (($hostDetails[$i].AvailablePhysicalMemory -lt "20") -And ($hostDetails[$i].AvailablePhysicalMemory -gt "5"))) { [int] $hostDetails[$i].HostMemoryHealth = 2 } ElseIf ((($hostDetails[$i].AvailablePhysicalMemoryPC -le "30") -And ($hostDetails[$i].AvailablePhysicalMemoryPC -gt "20")) -OR (($hostDetails[$i].AvailablePhysicalMemory -lt "30") -And ($hostDetails[$i].AvailablePhysicalMemory -gt "10"))) { [int] $hostDetails[$i].HostMemoryHealth = 1 } Else { [int] $hostDetails[$i].HostMemoryHealth = 0 } $procDetails = Get-WmiObject -Class Win32_Processor -Computername $cNodes.Name[$i] $hostDetails[$i].Processors = ($procDetails.DeviceID).Count $hostDetails[$i].ProcessorCore = ($procDetails.numberofcores |Measure-Object -Sum).sum $hostDetails[$i].LogicalProcessors = ($procDetails.numberoflogicalprocessors |Measure-Object -Sum).sum } For ($i=0; $i -lt $nodecount; $i++) { Write-Host ("Processing Virtual Servers on Hyper-V Host "+$cNodes.Name[$i]) -Foregroundcolor Green -BackgroundColor DARKGREEN [array] $vmList = Get-VM -Computer $cNodes.Name[$i] |Select Name $vmCount = $hostDetails[$i].VMs = $vmList.count If ($vmCount -ge "1") { fWriteSubRowNodeName $ResultFile $cNodes.Name[$i] $hostDetails[$i].TotalPhysicalMemory $hostDetails[$i].AvailablePhysicalMemory $hostDetails[$i].AvailablePhysicalMemoryPC $WarningLevel[$hostDetails[$i].HostMemoryHealth] fWriteVMTableHeader $ResultFile For ($j=0; $j -lt $VMcount; $j++) { Write-Host ("Processing VM - "+$VMList[$j].name) -foregroundcolor Green $vmDetails = Get-VM -VMName $vmList[$j].name -Computer $cNodes.name[$i] |Select name, processorcount, MemoryDemand, VMID, IsClustered, @{Label="MinimumRam"; Expression={[System.Math]::Round(($_.MemoryMinimum/1073741824),2)}}, @{Label="MaximumRam"; Expression={[int]($_.MemoryMaximum/1073741824)}}, DynamicMemoryEnabled, @{Label="StartupRam"; Expression={[System.Math]::Round(($_.MemoryStartup/1073741824),2)}}, IntegrationServicesVersion, Uptime, ParentSnapshotId, IntegrationServicesState, ResourceMeteringEnabled $vmHealth = [int] ($hostDetails[$i]).HostMemoryHealth $hostDetails[$i].vProcessors += $vmDetails.processorcount If ($vmDetails.DynamicMemoryEnabled -eq "True") { $hostDetails[$i].TotalVMStartupRamAllocated += $vmDetails.StartupRam $hostDetails[$i].TotalVMMaxMemoryAllocated += $vmDetails.MaximumRam } Else { $hostDetails[$i].TotalVMStartupRamAllocated += $vmDetails.StartupRam $hostDetails[$i].TotalVMMaxMemoryAllocated += $vmDetails.StartupRam $VMDetails[0].MinimumRam = $vmDetails[0].MaximumRam = "DM Disabled" } If ($vmDetails.ResourceMeteringEnabled -eq "True") { [int] $AvgMemUsage = (((Measure-VM -ComputerName $cNodes.Name[$i] -VMName $VMList.name[$j]).AvgRam)/1024) } Else { [string] $AvgMemUsage = "NA" } If (Get-VHD -VMID $vmDetails.VMID -ComputerName $cNodes.Name[$i]) { [array] $DiskDetails = Get-VHD -VMID $vmDetails.VMID -ComputerName $cNodes[$i].Name | Select path, FragmentationPercentage, @{Label="VolName"; Expression={""}}, @{Label="AllocatedSize"; Expression={$_.Size}}, @{Label="CurrentUsage"; Expression={$_.FileSize}}, @{Label="vDiskType"; Expression={""}}, @{Label="vDiskVolumeHealth"; Expression={""}} $vDiskcount = $DiskDetails.count For ($k = 0; $k -lt $vdiskcount; $k++) { $VolInfo = (($DiskDetails[$k]).path).split('\')[2] $DiskDetails[$k].VolName = IF (($DiskDetails[$k]).Path -match "C:\\ClusterStorage") {($DiskDetails[$k].Path).Split("\")[2]} Else {($DiskDetails[$k].Path).Substring(0,2)} $DiskDetails[$k].VolName = (Get-Culture).textinfo.totitlecase($DiskDetails[$k].VolName) $DiskDetails[$k].vDiskType = (($DiskDetails[$k]).path).split('.')[1] $VolIndex = [array]::indexof($CSVstorage.name,$DiskDetails[$k].VolName) $CSVStorage[$VolIndex].VHDXAllocatedSpace = $CSVStorage[$VolIndex].VHDXAllocatedSpace + $DiskDetails[$k].AllocatedSize $CSVStorage[$VolIndex].VHDXActualUsage = $CSVStorage[$VolIndex].VHDXActualUsage + $DiskDetails[$k].CurrentUsage $DiskDetails[$k].vDiskVolumeHealth = $CSVStorage.VolumeHealthCode[$VolIndex] } } $volHealthMax = $DiskDetails.vDiskVolumeHealth IF ($volHealthMax -gt $VMHealth) { $vmHealth = $volHealthMax } If ($VMDetails.ParentSnapshotId) { $SnapShotDate = ((Get-VMSnapshot -VMName $vmDetails.name -ComputerName $cNodes.Name[$i]).CreationTime |Sort-Object |Select-Object -First 1).ToShortDateString() } Else { [String]$SnapShotDate = "NA" } If ((Get-VMNetworkAdapter -VMName $vmDetails.Name -ComputerName $cNodes.Name[$i]).Count -eq "1") { $vNicType = (Get-VMNetworkAdapter -VMName $vmDetails.Name -ComputerName $cNodes.Name[$i]).IsLegacy } IF ($vDiskCount -gt "1") { fWriteVMInfo $ResultFile $vmDetails.name $vmDetails.UpTime $vmDetails.IntegrationServicesVersion $vmDetails.IsClustered $vmDetails.ProcessorCount $vmDetails.StartupRam $vmDetails.MinimumRam $vmDetails.MaximumRam $AvgMemUsage $WarningLevel[$hostDetails[$i].HostMemoryHealth] $DiskDetails[0].VolName ([int]($DiskDetails.AllocatedSize[0]/1073741824)) ([int]($DiskDetails.CurrentUsage[0]/1073741824)) $DiskDetails[0].FragmentationPercentage $WarningLevel[$DiskDetails[0].vDiskVolumeHealth] $DiskDetails[0].vDiskType $DiskDetails[1].VolName ([int]($DiskDetails.AllocatedSize[1]/1073741824)) ([int]($DiskDetails.CurrentUsage[1]/1073741824)) $DiskDetails[1].FragmentationPercentage $WarningLevel[$DiskDetails[1].vDiskVolumeHealth] $DiskDetails[1].vDiskType $vNicType $SnapShotDate $vmDetails.IntegrationServicesState $WarningLevel[$vmHealth] } Else { fWriteVMInfo $ResultFile $vmDetails.name $vmDetails.UpTime $vmDetails.IntegrationServicesVersion $vmDetails.IsClustered $vmDetails.ProcessorCount $vmDetails.StartupRam $vmDetails.MinimumRam $vmDetails.MaximumRam $AvgMemUsage $WarningLevel[$hostDetails[$i].HostMemoryHealth] $DiskDetails[0].VolName ([int]($DiskDetails.AllocatedSize[0]/1073741824)) ([int]($DiskDetails.CurrentUsage[0]/1073741824)) $DiskDetails[0].FragmentationPercentage $WarningLevel[$DiskDetails[0].vDiskVolumeHealth] $DiskDetails[0].vDiskType "NA" "NA" "NA" "NA" "NA" "NA" $vNicType $SnapShotDate $vmDetails.IntegrationServicesState $WarningLevel[$vmHealth] } Write-Host "Finished processing VM - " $vmDetails.name -ForegroundColor Yellow -BackgroundColor DarkGreen } Write-Host "Finished Processing VMs on Hyper-V Cluster Node - " $cNodes.Name[$i] -ForegroundColor white -BackgroundColor BLUE Add-Content $ResultFile "" } Else { Write-Host "NO VMs Found on Hyper-V Server - " $cNodes.Name[$i] -Foregroundcolor Black -backgroundcolor DarkRed } } fWriteSubHeadingHostDetails $ResultFile $Name fWriteHostStatusTableHeader $ResultFile For ($m = 0; $m -lt $nodecount; $m++) { fWriteHostInfo $ResultFile "#77FF5B" $hostDetails[$m].name $hostDetails[$m].VMs $hostDetails[$m].TotalPhysicalMemory $hostDetails[$m].AvailablePhysicalMemory $hostDetails[$m].AvailablePhysicalMemoryPC $hostDetails[$m].TotalVMStartupRamAllocated $hostDetails[$m].TotalVMMaxMemoryAllocated $hostDetails[$m].processors $hostDetails[$m].ProcessorCore $hostDetails[$m].LogicalProcessors $hostDetails[$m].vProcessors $WarningLevel[($hostDetails[$m]).HostMemoryHealth] } fWriteHostInfo $ResultFile "#FA58D0" "TOTAL" ($hostDetails.VMs|Measure-Object -Sum).sum ($hostDetails.TotalPhysicalMemory|Measure-Object -Sum).sum ($hostDetails.AvailablePhysicalMemory|Measure-Object -Sum).sum "0" ($hostDetails.TotalVMStartupRamAllocated|Measure-Object -Sum).sum ($hostDetails.TotalVMMaxMemoryAllocated|Measure-Object -Sum).sum ($hostDetails.processors|Measure-Object -Sum).sum ($hostDetails.ProcessorCore |Measure-Object -Sum).sum ($hostDetails.LogicalProcessors|Measure-Object -Sum).sum ($hostDetails.vProcessors |Measure-Object -Sum).sum Add-Content $ResultFile "" fWriteSubHeadingCSVDetails $ResultFile $Name Write-Host "Processing CSV Storage details $name " -ForegroundColor "Yellow" fWriteStorageTableHeader $ResultFile $len = $CSVStorage.count For ($z=0; $z -lt $len; $z++) { $csvName = $CSVStorage[$z].name $csvTotalSize = [System.Math]::Round(($CSVStorage[$z].Capacity/1099511627776),2) [int] $csvFree = (($CSVStorage[$z].Freespace)/1073741824) [int] $csvFreePC = $CSVStorage[$z].Freespacepc [int] $vhdxAlloc = (($CSVStorage[$z].VHDXAllocatedSpace)/1073741824) [int] $vhdxActual = (($CSVStorage[$z].VHDXActualUsage)/1073741824) [int] $vhdxOP = (($CSVStorage[$z].VHDXAllocatedSpace - $CSVStorage[$z].Capacity)/1073741824) [int] $pcOP = (($CSVStorage[$z].VHDXAllocatedSpace * 100) /$CSVStorage[$z].Capacity) If ($vhdxop -le "0") { [string] $vhdxop = "Not Over Provisioned" [string] $pcop = "NA" } fWriteStorageInfo $ResultFile $csvName $CSVStorage[$z].DiskType $CSVStorage[$z].RaidType $CSVStorage[$z].StorageInformation $CSVStorage[$z].Connectivity $csvTotalSize $csvFree $csvFreePC $vhdxAlloc $vhdxActual $vhdxOP $pcOP $WarningLevel[($CSVStorage[$z]).VolumeHealthCode] } fWriteStorageInfo $ResultFile "TOTAL" "NA" "NA" "NA" "NA" ([System.Math]::Round(($CSVStorage.Capacity|Measure-Object -Sum).sum/1099511627776)) ([System.Math]::Round(($CSVStorage.Freespace|Measure-Object -Sum).sum/1073741824)) "NA" ([System.Math]::Round(($CSVStorage.VHDXAllocatedSpace|Measure-Object -Sum).sum/1073741824)) ([System.Math]::Round(($CSVStorage.VHDXActualUsage|Measure-Object -Sum).sum/1073741824)) "NA" "NA" Add-Content $ResultFile "" $date = ( get-date ).ToString('yyyy/MM/dd') Write-Host ("Finished Processing Storage for Cluster $name") -ForegroundColor BLACK -BackgroundColor CYAN } ElseIf ($type -match "StandAlone") { Write-Host ("Processing Storage Information on StandAlone Hyper-V Host " +$Name) -Foregroundcolor Green fWriteSubHeadingClusterOrStandAlone $ResultFile ("Standalone Node - " +$name) [array] $LocalStorage = Get-WmiObject Win32_LogicalDisk -filter "DriveType=3" -computer $Name | Select DeviceID, Size, FreeSpace, @{n="FreeSpacePC";e={[int]($_.FreeSpace/$_.Size*100)}}, @{n="VHDXAllocatedSpace";e={0}}, @{n="VHDXActualUsage";e={0}}, @{n="VolumeHealthCode";e={[int]"0"}} $IsClustered = "NA" $LocalStorageCount = ($LocalStorage).count For ($k=0; $k -lt $LocalStorageCount; $k++) { If (($LocalStorage.FreeSpacePC[$k] -le "10") -OR ($LocalStorage.FreeSpace[$k] -lt "53687091200")){[int] $LocalStorage[$k].VolumeHealthCode = "3"} ElseIf ((($LocalStorage.FreeSpacePC[$k] -le "20") -And ($LocalStorage.FreeSpace[$k] -gt 10)) -OR ($LocalStorage.FreeSpace[$k] -lt "107374182400")){[int] $LocalStorage[$k].VolumeHealthCode = "2"} ElseIf ((($LocalStorage.FreeSpacePC[$k] -le "30") -And ($LocalStorage.FreeSpace[$k] -gt 20)) -OR ($LocalStorage.FreeSpace[$k] -lt "214748364800")){[int] $LocalStorage[$k].VolumeHealthCode = 1} Else {$LocalStorage[$k].VolumeHealthCode = 0} } Write-Host "Processing Physical Host Details " $Name -Foregroundcolor Green -BackgroundColor DARKGREEN [array] $hostDetails = Get-WmiObject -Class win32_OperatingSystem -ComputerName $Name |Select @{Label="TotalPhysicalMemory"; Expression={[int]($_.TotalVisibleMemorySize/1048576)}}, @{Label="AvailableMemory"; Expression={[int]($_.FreePhysicalMemory/1048576)}}, @{Label="AvailablePhysicalMemoryPC"; Expression={[int](($_.FreePhysicalMemory/$_.TotalVisibleMemorySize)*100)}} [Int] $TotalVMMaxMemoryAllocated = [int] $TotalVMStartupRamAllocated = [int] $vProcessors = 0 If (($hostDetails.AvailablePhysicalMemoryPC -le "10") -OR ($hostDetails.AvailableMemory -lt "10")) { [int] $HostMemoryHealth = 3 } ElseIf ((($hostDetails.AvailablePhysicalMemoryPC -le "20") -And ($hostDetails.AvailablePhysicalMemoryPC -gt "10")) -OR (($hostDetails.AvailableMemory -lt "20") -And ($hostDetails.AvailableMemory -gt "10"))) { [int] $HostMemoryHealth = 2 } ElseIf ((($hostDetails.AvailablePhysicalMemoryPC -le "70") -And ($hostDetails.AvailablePhysicalMemoryPC -gt "20")) -OR (($hostDetails.AvailableMemory -lt "30") -And ($hostDetails.AvailableMemory -gt "20"))) { [int] $HostMemoryHealth = 1 } Else { [int] $HostMemoryHealth = 0 } $procDetails = Get-WmiObject -Class Win32_Processor -Computername $Name $Processors = ($procDetails.DeviceID).Count $ProcessorCore = ($procDetails.numberofcores |Measure-Object -Sum).sum $LogicalProcessors = ($procDetails.numberoflogicalprocessors |Measure-Object -Sum).sum [array] $vmList = Get-VM -Computer $Name $vmCount = ($vmList.Name).count If ($vmCount -gt "0") { Write-Host "Processing Virtual Servers on Hyper-V Standalone Host - " $Name -Foregroundcolor Green -BackgroundColor DARKGREEN fWriteSubRowNodeName $ResultFile $Name $hostDetails.TotalPhysicalMemory $hostDetails.AvailablePhysicalMemory $hostDetails.AvailablePhysicalMemoryPC $WarningLevel[$HostMemoryHealth] fWriteVMTableHeader $ResultFile For ($j=0; $j -lt $vmCount; $j++) { Write-Host "Processing VM - " $VMList[$j].name -ForegroundColor Yellow [array]$vmDetails = Get-VM -VMName $vmList[$j].name -Computer $Name |Select name, processorcount, MemoryDemand, VMID, @{Label="MinimumRam"; Expression={[int]($_.MemoryMinimum/1073741824)}}, @{Label="MaximumRam"; Expression={[int]($_.MemoryMaximum/1073741824)}}, DynamicMemoryEnabled, @{Label="StartupRam"; Expression={[int]($_.MemoryStartup/1073741824)}}, IntegrationServicesVersion, Uptime, ParentSnapshotId, IntegrationServicesState, ResourceMeteringEnabled If ($vmDetails.DynamicMemoryEnabled -eq "True") { $TotalVMStartupRamAllocated += $vmDetails.StartupRam $TotalVMMaxMemoryAllocated += $vmDetails.MaximumRam } Else { $TotalVMStartupRamAllocated += $vmDetails.StartupRam $TotalVMMaxMemoryAllocated += $vmDetails.StartupRam $VMDetails[0].MinimumRam = $vmDetails[0].MaximumRam = "DM Disabled" } If ($vmDetails.ResourceMeteringEnabled -eq "True") { [int] $AvgMemUsage = (((Measure-VM -ComputerName $Name -VMName $VMList.name[$j]).AvgRam)/1024) } ElseIf ($vmDetails.ResourceMeteringEnabled -match "False") { Write-Host "Resource Metering is disabled - Skipping Avg Memory on " $VMList[$j].name -ForegroundColor Red [string] $AvgMemUsage = "NA" } If (Get-VHD -VMID $vmDetails.VMID -ComputerName $Name -erroraction SilentlyContinue) { [array] $DiskDetails = Get-VHD -VMID $vmDetails.VMID -ComputerName $Name | Select path, FragmentationPercentage, @{Label="VolName"; Expression={""}}, @{Label="AllocatedSize"; Expression={$_.Size}}, @{Label="CurrentUsage"; Expression={$_.FileSize}}, @{Label="vDiskType"; Expression={""}}, @{Label="vDiskVolumeHealth"; Expression={""}} $vDiskcount = $DiskDetails.count For ($k = 0; $k -lt $vdiskcount; $k++) { $DiskDetails[$k].VolName = ($DiskDetails[$k].Path).Substring(0,2) $DiskDetails[$k].VolName = (Get-Culture).textinfo.totitlecase($DiskDetails[$k].VolName) $VolInfo = (($DiskDetails[$k]).path).Substring(0,2) $VolIndex = [array]::indexof($localStorage.DeviceID,$VolInfo) $DiskDetails[$k].vDiskType = (($DiskDetails[$k]).path).split('.')[1] $localStorage[$VolIndex].VHDXAllocatedSpace = $localStorage[$VolIndex].VHDXAllocatedSpace + $DiskDetails[$k].AllocatedSize $localStorage[$VolIndex].VHDXActualUsage = $localStorage[$VolIndex].VHDXActualUsage + $DiskDetails[$k].CurrentUsage $DiskDetails[$k].vDiskVolumeHealth = $localStorage[$VolIndex].VolumeHealthCode } } If ($VMDetails.ParentSnapshotId) { $SnapShotDate = ((Get-VMSnapshot -VMName $vmDetails.name -ComputerName $Name).CreationTime |Sort-Object |Select-Object -First 1).ToShortDateString() } Else { [String]$SnapShotDate = "NA" } If ((Get-VMNetworkAdapter -VMName $vmDetails.Name -ComputerName $Name).Count -eq "1") { $vNicType = (Get-VMNetworkAdapter -VMName $vmDetails.Name -ComputerName $Name).IsLegacy } If ($vDiskcount -gt "1") { fWriteVMInfo $ResultFile $vmDetails.name $vmDetails.UpTime $vmDetails.IntegrationServicesVersion "NA" $vmDetails.ProcessorCount $vmDetails.StartupRam $vmDetails.MinimumRam $vmDetails.MaximumRam $AvgMemUsage $HostMemoryHealth $DiskDetails[0].VolName ([int]($DiskDetails.AllocatedSize[0]/1073741824)) ([int]($DiskDetails.CurrentUsage[0]/1073741824)) $DiskDetails[0].FragmentationPercentage $WarningLevel[$DiskDetails[0].vDiskVolumeHealth] $DiskDetails[0].vDiskType $DiskDetails[1].VolName ([int]($DiskDetails.AllocatedSize[1]/1073741824)) ([int]($DiskDetails.CurrentUsage[1]/1073741824)) $DiskDetails[1].FragmentationPercentage $WarningLevel[$DiskDetails[1].vDiskVolumeHealth] $DiskDetails[1].vDiskType $vNicType $SnapShotDate $vmDetails.IntegrationServicesState $WarningLevel[$vmHealth] } Else { fWriteVMInfo $ResultFile $vmDetails.name $vmDetails.UpTime $vmDetails.IntegrationServicesVersion "NA" $vmDetails.ProcessorCount $vmDetails.StartupRam $vmDetails.MinimumRam $vmDetails.MaximumRam $AvgMemUsage $WarningLevel[$HostMemoryHealth] $DiskDetails[0].VolName ([int]($DiskDetails.AllocatedSize[0]/1073741824)) ([int]($DiskDetails.CurrentUsage[0]/1073741824)) $DiskDetails[0].FragmentationPercentage $WarningLevel[$DiskDetails[0].vDiskVolumeHealth] $DiskDetails[0].vDiskType "NA" "NA" "NA" "NA" "NA" "NA" $vNicType $SnapShotDate $vmDetails.IntegrationServicesState } Write-Host "Finished Processing " $VMDetails.name -ForegroundColor Yellow -BackgroundColor DarkGreen } Write-Host "Finished Processing VMs on Hyper-V Server " $Name -ForegroundColor Yellow -BackgroundColor DarkGreen Add-Content $ResultFile "" } Else { Write-Host "NO VMs Found on Hyper-V Server $Name" -ForegroundColor Black -BackgroundColor Red } fWriteSubHeadingHostDetails $ResultFile $Name fWriteHostStatusTableHeader $ResultFile fWriteHostInfo $ResultFile "#77FF5B" $name $vmCount $hostDetails.TotalPhysicalMemory $hostDetails.AvailableMemory $TotalVMStartupRamAllocated $TotalVMMaxMemoryAllocated $processors $ProcessorCore $LogicalProcessors $vProcessors $WarningLevel[$HostMemoryHealth] Add-Content $ResultFile "" fWriteSubHeadingCSVDetails $ResultFile $Name fWriteStorageTableHeader $ResultFile $len = $localStorage.count For ($z=0; $z -lt $len; $z++) { $localDiskName = $localStorage[$z].deviceID $localDiskTotalSize = [System.Math]::Round((($localStorage[$z].Size)/1099511627776),2) [int] $localDiskFree = [System.Math]::Round((($localStorage[$z].Freespace)/1073741824),2) [int] $localDiskFreePC = (($localStorage[$z].Freespace * 100) / $localStorage[$z].Size) [int] $vhdxAlloc = (($localStorage[$z].VHDXAllocatedSpace)/1073741824) [int] $vhdxActual = (($localStorage[$z].VHDXActualUsage)/1073741824) [int] $vhdxOP = (($localStorage[$z].VHDXAllocatedSpace - $localStorage[$z].Size)/1073741824) [int] $pcOP = (($localStorage[$z].VHDXAllocatedSpace*100)/$localStorage[$z].Size) If ($vhdxop -le "0") { [string] $vhdxop = "Not Over Provisioned" [string] $pcop = "NA" } fWriteStorageInfo $ResultFile $localDiskName "NA" "NA" "NA" "NA" $localDiskTotalSize $localDiskFree $localDiskFreePC $vhdxAlloc $vhdxActual $vhdxOP $pcOP } Add-Content $ResultFile "" $date = ( get-date ).ToString('yyyy/MM/dd') Write-Host ("Finished Processing Storage for Hyper-V Standalone Host $name ") -ForegroundColor DarkRed -BackgroundColor DarkGreen } } fWriteHtmlHeader $ResultFile fWriteLegendTable $ResultFile $WarningLevel[0] $WarningLevel[1] $WarningLevel[2] $WarningLevel[3] Write-Host "Fetching Input File" -foregroundColor Green If (Test-Path .\servers.txt) { $sList = (Get-Content .\servers.txt) $sCount = $sList.Count If ($sCount -eq "0") { Write-Host "Servers.txt Empty. Please check if the input file exists and populated with server names" } If ($sCount -gt "0") { Write-Host "Identified Servers.txt. Checking if Servers/Clusters are Available" If ($sCount -eq "1") { [array] $sList = $sList } For ($a = 0; $a -lt $sCount; $a++) { Write-Host ("Checking "+$sList[$a]) If (Get-Cluster $sList[$a] -erroraction SilentlyContinue) { Write-Host ("Identified cluster "+$sList[$a]) -Background White -Foreground Black fCreateDashBoard $sList[$a] "Cluster" } ElseIf ((get-vmhost $sList[$a]) -And (!(Get-Service -name clussvc -ComputerName $sList[$a] -erroraction SilentlyContinue))) { Write-Host ("Identified Standalone HyperV Server - "+$sList[$a]) -Background White -Foreground Black fCreateDashBoard $sList[$a] "StandAlone" } Else { Write-Host "Not able to identify the server as a StandAlone host or Cluster $sList[$a] " -Background White -Foreground Black } } } } ElseIf ((Get-Cluster) -Or (Get-VMHost)) { Write-Host "Checking local host for Hyper-V Role" IF (Get-Cluster) { $sList = (Get-Cluster).name fCreateDashBoard $sList "Cluster" } ElseIf (Get-VMHost) { $sList = (Get-VmHost).name fCreateDashBoard $sList "StandAlone" } } ElseIf ((!(Get-Cluster)) -And (!(Get-VMHost)) -And (!(Test-Path Servers.txt))) { Write-Host "Please provide input file or run the script from a Hyper-V Standlone Server / Hyper-V Cluster node" -ForegroundColor BLACK -BackgroundColor "Red" Write-Host "######### EXITING SCRIPT ########### " -ForegroundColor Yellow -BackgroundColor "Red" } fWriteHtmlFooter $ResultFile Send-MailMessage -To youremail@yourdomain.com -From HyperV-Dashboard@yourdomain.com -Body (Get-Content HyperV-VM-Report.htm |Out-String) -SmtpServer Smtp.YourDomain.Com -Subject "HyperV Health Report" -BodyAsHtml