VM Stats
VM Stats V2
IF (Get-PSSnapin | where {$_.name -eq "VMware.VimAutomation.Core"})
{write-host "VMware.VimAutomation.Core snapin already loaded"}
Else
{
write-host "Loading VMware.VimAutomation.Core snapin"
add-PSSnapin VMware.VimAutomation.Core
write-host "VMware.VimAutomation.Core snapin Loaded"
}
$VirtualCenters= "vmvc1","vmvc2"
#Hashtable for Tier Multiplier, lookup the Tier and return the TierMultiplier
$TierMultiplier = @{2=2; 3=1; 4=0}
#This is the percent for "Adjusted Percent Memory Commit" that a row will be highlighted in red
$ThresholdMarkCritical = 100
#This is the percent for "Adjusted Percent Memory Commit" that will cause Required RAM to be Calculated
$ThresholdCalcRequireRAM = 90
#This is the percent in decimal for "Adjusted Percent Memory Commit" that is used to Calculate Required RAM
$DesiredAdjustedPercentMemoryCommit = .75
#This is the amount of RAM in GB that comes in a newly purchased host
$NewHostMemorySize=128
$RequiredHostsFieldName= "Required $NewHostMemorySize GB Hosts"
$smtpServer = "mail.blah.com"
$To = "Kevin Curran <kcurran@blah.com>"
$From = "AD Reporting <ADReporting@blah.com>"
foreach ($VC in $VirtualCenters)
{
Connect-VIServer $VC
$DataCenters= Get-Datacenter
#Collect Cluster Stats for email body
$VMStats +=foreach ($DataCenter in $DataCenters)
{
$clusters = get-cluster -Location $DataCenter.Name
if ($clusters -eq $null)
{(get-vm -Location $DataCenter.Name).count | select @{Name="Name";Expression= {$DataCenter.Name}},
@{Name="Hosts";Expression={((Get-VMHost -Location $DataCenter.Name) | measure ).count}},
@{Name="VMs";Expression={(get-vm -Location $DataCenter.Name).count}},
@{Name="Memory Total GB";Expression={[Math]::Round($(Get-VMHost -Location $_.Name | measure -Property MemoryTotalGB -sum).sum,2)}},
@{Name="Memory Commit GB";Expression={[Math]::Round($(Get-VM -Location $_.Name | measure -Property MemoryGB -sum).sum,2)}},
@{Name="Top Host RAM GB";Expression={[Math]::Round($(Get-VMHost -Location $_.Name | Sort-Object -Property MemoryTotalGB | select MemoryTotalGB -Last 1).MemoryTotalGB,2)}},
@{Name="DataCenter";Expression={($DataCenter)}}
}
else
{
get-cluster -Location $DataCenter.Name | select Name, @{Name="Hosts"; Expression = {($_.ExtensionData.Host).count}},
@{Name="VMs"; Expression = {(Get-VM -Location $_.Name).count}},
@{Name="Memory Total GB";Expression={[Math]::Round($(Get-VMHost -Location $_.Name | measure -Property MemoryTotalGB -sum).sum,2)}},
@{Name="Memory Commit GB";Expression={[Math]::Round($(Get-VM -Location $_.Name | measure -Property MemoryGB -sum).sum,2)}},
@{Name="Top Host RAM GB";Expression={[Math]::Round($(Get-VMHost -Location $_.Name | Sort-Object -Property MemoryTotalGB | select MemoryTotalGB -Last 1).MemoryTotalGB,2)}},
@{Name="DataCenter";Expression={($DataCenter)}}
}
}
Disconnect-VIServer -Confirm:$False
}
#Create a summary report to email VM to Host ratios
foreach ($Cluster in $VMStats)
{
#Add Ratios
$Cluster | Add-Member -membertype noteproperty -name Ratio -Value `
$(
try {
"{0:n2} to 1" -f(($Cluster.VMs) / ($Cluster.Hosts))
}
catch [System.DivideByZeroException] {
"Divide by 0!"
}
finally {
# Cleanup
}
)
#Tier is set as the 3rd character of the cluster name
#if that is not a number this switch will adjust them
#May consider adding Tier as a custom annotation
#Get-Cluster cv201 | Set-Annotation -CustomAttribute "Tier" -Value 2
$Cluster | Add-Member -membertype noteproperty -name Tier -Value $($Cluster.Name.substring(2,1))
$Cluster.Tier =switch ($Cluster.Tier)
{
"F" {2}
"L" {3}
"W" {3}
"X" {3}
#Cast Tier to int
default {[int]$_}
}
#Add Percent Memory Commit
$Cluster | Add-Member -membertype noteproperty -name "Percent Memory Commit" -Value `
$([Math]::Round((($Cluster."Memory Commit GB") / ($Cluster."Memory Total GB") * 100),2))
#Now that Tier is valid calculate "Adjusted Percent Memory Commit"
#"Adjusted Percent Memory Commit" is
#Total VM RAM commit / divided by
#Total ESX Server RAM - [(Largest ESX server RAM value) x {Tier multiplier}]
$Cluster | Add-Member -membertype noteproperty -name "Adjusted Percent Memory Commit" -Value `
$(
try {
[Math]::Round((($Cluster."Memory Commit GB" / ($Cluster."Memory Total GB" - ($Cluster."Top Host RAM GB" * $TierMultiplier.Item($Cluster.Tier)))) * 100),2)
}
catch [System.DivideByZeroException] {
"Divide by 0!"
}
finally {
# Cleanup
}
)
#Mark Critical Clusters This mark is later replaced in HTML to convert row color to red
If ($Cluster."Adjusted Percent Memory Commit" -ge $ThresholdMarkCritical )
{$Cluster.Name = "Critical" + $Cluster.Name}
#Add Required RAM to reach a 75% Adjusted Percent Memory Commit
$Cluster | Add-Member -membertype noteproperty -name "Required Ram GB" -Value `
$(
If ($Cluster."Adjusted Percent Memory Commit" -ge $ThresholdCalcRequireRAM)
{[Math]::Round(($Cluster."Memory Commit GB" / $DesiredAdjustedPercentMemoryCommit ) + ($Cluster."Top Host RAM GB" * $TierMultiplier.Item($Cluster.Tier)) - $Cluster."Memory Total GB" ,2)}
)
#Number of hosts to purchase to reach a 75% Adjusted Percent Memory Commit
$Cluster | Add-Member -membertype noteproperty -name $RequiredHostsFieldName -Value `
$(
If ($Cluster."Required Ram GB" -ne $null)
#Rounding up [system.math]::ceiling(3.1)
{[Math]::ceiling($Cluster."Required Ram GB" / $NewHostMemorySize)}
)
}
$TotalHosts = ($VMStats | Measure-Object -Sum -Property Hosts).sum
$TotalVMs = ($VMStats | Measure-Object -Sum -Property VMs).sum
$TotalRatio = "{0:n2} to 1" -f(($TotalVMs) / ($TotalHosts))
$TotalMessage = "<H2>Totals</H2>Total Hosts`: $totalHosts <br>Total VMs`: $totalVMs <br>Total Ratio`: $totalRatio <br>"
$SumaryReport= $VMStats | Group-Object -Property DataCenter | Sort Name
foreach ($RPTDataCtr in $SumaryReport)
{
$RPTDC = switch ($RPTDataCtr.Name)
{
"LADC" {"LA"}
"NYDC" {"NY"}
"CDC" {"Chicago"}
"FDC" {"Florida"}
default {$_}
}
$TempHTML = $null
$SubTotalHosts = $null
$SubTotalVMs = $null
$SubTotalRatio = $null
$SubTotalHosts = ($RPTDataCtr.Group | Measure-Object -Sum -Property Hosts).sum
$SubTotalVMs = ($RPTDataCtr.Group | Measure-Object -Sum -Property VMs).sum
#$SubTotalRatio = "{0:n2} to 1" -f(($SubTotalVMs) / ($SubTotalHosts))
$SubTotalMemTotalGB = ($RPTDataCtr.Group | Measure-Object -Sum -Property "Memory Total GB").sum
$SubTotalMemCommitGB = ($RPTDataCtr.Group | Measure-Object -Sum -Property "Memory Commit GB").sum
$TempHTML= $RPTDataCtr.Group | ConvertTo-Html -Fragment -Property Name, Hosts, VMs, "Memory Total GB", "Memory Commit GB", Tier, "Top Host RAM GB", "Percent Memory Commit", "Adjusted Percent Memory Commit", "Required Ram GB", $RequiredHostsFieldName -PreContent "<b>$RPTDC</b>" | ForEach-Object {$_}
$TempHTML = $TempHTML -Replace "</table>","<tr style=`"font-weight`: bold`;`"><td>Total</td><td>$SubTotalHosts</td><td>$SubTotalVMs</td><td>$SubTotalMemTotalGB</td><td>$SubTotalMemCommitGB</td></tr>`r`n</table>"
$Message += $TempHTML
#$Message+= $RPTDataCtr.Group | ConvertTo-Html -Fragment -Property Name, Hosts, VMs, Ratio -PreContent "<H2>$($RPTDataCtr.Name)</H2>" | ForEach-Object {$_}
}
#$Message= $VMStats | sort -Property Name | ConvertTo-Html
#$Message= $VMStats | sort -Property Name | ConvertTo-Html -PreContent "Current VM Stats <H2>VM Ratios </H2>" | ForEach-Object {$_}
#Modify HTML to display clusters marked as Critical in Red
$ModifiedHTML=ForEach ($line in $Message)
{
$line.replace('<tr><td>Critical','<tr class="Crit"><td>')
}
$PreMessage = @"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HTML TABLE</title>
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:MidnightBlue; color:Yellow}
TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:PaleGoldenrod}
TR.D0 TD {background-color: White; color: black;}
TR.D1 TD {background-color: LawnGreen; color: black;}
TR.Crit TD {background-color: Red; color: black;}
</style>
</head><body>
“@
$PostMessage += "<br>This script was run by " + $env:username + " on " + $env:COMPUTERNAME
$PostMessage += "<br>ScriptName: $($MyInvocation.MyCommand) "
$PostMessage += "<br>Script path: $(Split-Path -Parent $MyInvocation.MyCommand.Path)"
$PostMessage += "</body></html>"
$html = $PreMessage + $TotalMessage + $($ModifiedHTML | out-string) + $PostMessage
#Send Message
Send-MailMessage -From $From -To $To -SmtpServer $smtpServer `
-Subject "VM Stats V2" -BodyasHTML $HTML
Ugly script still needs work.
@"
===============================================================================
Title: Export-VMInfo.ps1
Description: Exports VM Information from vCenter into a .CSV file for importing into anything
Usage: .\Export-VMInfo.ps1
Date: 04/03/2010
http://www.wooditwork.com/2010/08/16/exporting-all-that-useful-vm-information-with-powercli/
===============================================================================
"@
IF (Get-PSSnapin | where {$_.name -eq "VMware.VimAutomation.Core"})
{write-host "VMware.VimAutomation.Core snapin already loaded"}
Else
{
write-host "Loading VMware.VimAutomation.Core snapin"
add-PSSnapin VMware.VimAutomation.Core
write-host "VMware.VimAutomation.Core snapin Loaded"
}
$VirtualCenters= "vmvc1","vmvc2"
$Report = @()
$ExportFilePath = "c:\temp\Export-VMInfo.csv"
Function check-even ($num) {[bool]!($num%2)}
$smtpServer = "mail.blah.com"
$To = "Kevin Curran <kcurran@blah.com>"
$From = "AD Reporting <ADReporting@blah.com>"
function Get-VMSerial
{
param([VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]$VirtualMachine)
$s = ($VirtualMachine.ExtensionData.Config.Uuid).Replace("-", "")
$Uuid = "VMware-"
for ($i = 0; $i -lt $s.Length; $i += 2)
{
$Uuid += ("{0:x2}" -f [byte]("0x" + $s.Substring($i, 2)))
if ($Uuid.Length -eq 30) { $Uuid += "-" } else { $Uuid += " " }
}
Write-Output $Uuid.TrimEnd()
}
foreach ($VC in $VirtualCenters)
{
Connect-VIServer $VC
$VMs = Get-VM
$Datastores = Get-Datastore | select Name, Id
$VMHosts = Get-VMHost | select Name, Parent
$VMHostDataCenter=Get-Datacenter | foreach {
$Datacenter = $_.name
$_ | Get-VMHost | select @{Name="VMHost";Expression={($_.Name)}}, @{Name="DataCenter";Expression={($Datacenter)}}
}
$DataCenters= Get-Datacenter
#Collect Cluster Stats for email body
$VMsPerCLuster +=foreach ($DataCenter in $DataCenters)
{
$clusters=get-cluster -Location $DataCenter.Name
if ($clusters -eq $null)
{(get-vm -Location $DataCenter.Name).count | select @{Name="Name";Expression= {$DataCenter.Name}},
@{Name="Hosts";Expression={((Get-VMHost -Location $DataCenter.Name) | measure ).count}},
@{Name="VMs";Expression={(get-vm -Location $DataCenter.Name).count}},
@{Name="DataCenter";Expression={($DataCenter)}}
}
else
{
get-cluster -Location $DataCenter.Name | select Name, @{Name="Hosts"; Expression = {($_.ExtensionData.Host).count}},
@{Name="VMs"; Expression = {(Get-VM -Location $_.Name).count}},
@{Name="DataCenter";Expression={($DataCenter)}}
}
}
#collect VM data for attached list of vms
ForEach ($VM in $VMs) {
$counter ++
Write-Host $counter $VM.name
$VMView = $VM | Get-View
$Annotation = $vm | Get-Annotation
$VMInfo = {} | Select VMName,Powerstate,OS,IPAddress,ToolsStatus,Host,
Cluster,Datacenter,Datastore,inSRMResourcePool,NumCPU,MemMb,ProvisionedSpaceGB,
UsedSpaceGB,DiskGb,DiskFree,DiskUsed,Notes,BusinessOwner,
CreationDate,DownTimeWindow,PrimarySysAdmin,Tier,SerialNumber
$VMInfo.VMName = $vm.name
$VMInfo.Powerstate = $vm.Powerstate
$VMInfo.OS = $vm.Guest.OSFullName
$VMInfo.IPAddress = $vm.Guest.IPAddress[0]
$VMInfo.ToolsStatus = $VMView.Guest.ToolsStatus
$VMInfo.Host = $vm.VMhost.name
$VMInfo.Cluster = $vm.VMhost.Parent.Name
$VMInfo.Datacenter = ($VMHostDataCenter | where {$_.VMHost -eq $vm.VMhost.name} | select DataCenter).DataCenter
$VMInfo.Datastore = ($Datastores | where {$_.ID -match ($vmview.Datastore | Select -First 1).Value}).Name
$VMInfo.inSRMResourcePool = $vm.ResourcePool.Name -like "*SRM*"
$VMInfo.NumCPU = $vm.NumCPU
$VMInfo.MemMb = [Math]::Round(($vm.MemoryMB),2)
$VMInfo.ProvisionedSpaceGB = [Math]::Round($vm.ProvisionedSpaceGB)
$VMInfo.UsedSpaceGB = [Math]::Round($vm.UsedSpaceGB)
$VMInfo.DiskGb = [Math]::Round((($vm | get-harddisk | Measure-Object -Property CapacityKB -Sum).Sum * 1KB / 1GB),2)
$VMInfo.DiskFree = [Math]::Round((($vm.Guest.Disks | Measure-Object -Property FreeSpace -Sum).Sum / 1GB),2)
$VMInfo.DiskUsed = $VMInfo.DiskGb - $VMInfo.DiskFree
$VMInfo.Notes = $vm.Notes
$VMInfo.BusinessOwner = ($Annotation | where {$_.Name -eq "Business Owner"}).value
$VMInfo.CreationDate = ($Annotation | where {$_.Name -eq "Creation Date"}).value
$VMInfo.DownTimeWindow = ($Annotation | where {$_.Name -eq "Down Time Window"}).value
$VMInfo.PrimarySysAdmin = ($Annotation | where {$_.Name -eq "Primary SysAdmin"}).value
$VMInfo.Tier = ($Annotation | where {$_.Name -eq "Tier"}).value
$VMInfo.SerialNumber = Get-VMSerial $vm
$Report += $VMInfo
}
Disconnect-VIServer -Confirm:$False
}
#Export VM List Report to a File to attach to email
$Report = $Report | Sort-Object VMName
IF ($Report -ne "") {
$report | Export-Csv $ExportFilePath -NoTypeInformation
}
#Create a summary report to email VM to Host ratios
#Add Ratios
$VMStats=$VMsPerCLuster | Select DataCenter, Name, Hosts, VMs, @{Name="Ratio"; Expression = {"{0:n2} to 1" -f(($_.VMs) / ($_.Hosts)) }}
$TotalHosts = ($VMStats | Measure-Object -Sum -Property Hosts).sum
$TotalVMs = ($VMStats | Measure-Object -Sum -Property VMs).sum
$TotalRatio = "{0:n2} to 1" -f(($TotalVMs) / ($TotalHosts))
$TotalMessage = "<H2>Totals</H2>Total Hosts`: $totalHosts <br>Total VMs`: $totalVMs <br>Total Ratio`: $totalRatio <br>"
$SumaryReport= $VMStats | Group-Object -Property DataCenter | Sort Name
foreach ($RPTDataCtr in $SumaryReport)
{
$RPTDC = switch ($RPTDataCtr.Name)
{
"LADC" {"LA"}
"NYDC" {"NY"}
"CDC" {"Chicago"}
"FDC" {"Florida"}
default {$_}
}
$TempHTML = $null
$SubTotalHosts = $null
$SubTotalVMs = $null
$SubTotalRatio = $null
$SubTotalHosts = ($RPTDataCtr.Group | Measure-Object -Sum -Property Hosts).sum
$SubTotalVMs = ($RPTDataCtr.Group | Measure-Object -Sum -Property VMs).sum
$SubTotalRatio = "{0:n2} to 1" -f(($SubTotalVMs) / ($SubTotalHosts))
$TempHTML= $RPTDataCtr.Group | ConvertTo-Html -Fragment -Property Name, Hosts, VMs, Ratio -PreContent "<b>$RPTDC</b>" | ForEach-Object {$_}
$TempHTML = $TempHTML -Replace "</table>","<tr style=`"font-weight`: bold`;`"><td>Total</td><td>$SubTotalHosts</td><td>$SubTotalVMs</td><td>$SubTotalRatio</td></tr>`r`n</table>"
$Message += $TempHTML
#$Message+= $RPTDataCtr.Group | ConvertTo-Html -Fragment -Property Name, Hosts, VMs, Ratio -PreContent "<H2>$($RPTDataCtr.Name)</H2>" | ForEach-Object {$_}
}
#$Message= $VMStats | sort -Property Name | ConvertTo-Html
#$Message= $VMStats | sort -Property Name | ConvertTo-Html -PreContent "Current VM Stats <H2>VM Ratios </H2>" | ForEach-Object {$_}
$ModifiedHTML=ForEach ($line in $Message)
{
$counter++
if (check-even $counter)
{$line.replace('<tr><td>','<tr class="d0"><td>')}
Else
{$line.replace('<tr><td>','<tr class="d1"><td>')}
}
$PreMessage = @"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>HTML TABLE</title>
<style>
BODY{background-color:white;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:MidnightBlue; color:Yellow}
TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:PaleGoldenrod}
TR.D0 TD {background-color: White; color: black;}
TR.D1 TD {background-color: LawnGreen; color: black;}
</style>
</head><body>
“@
$PostMessage += "<br>This script was run by " + $env:username + " on " + $env:COMPUTERNAME
$PostMessage += "<br>ScriptName: $($MyInvocation.MyCommand) "
$PostMessage += "<br>Script path: $(Split-Path -Parent $MyInvocation.MyCommand.Path)"
$PostMessage += "</body></html>"
$html = $PreMessage + $TotalMessage + $($ModifiedHTML | out-string) + $PostMessage
#Send Message
Send-MailMessage -From $From -To $To -SmtpServer $smtpServer `
-Subject "VM Ratios" -BodyasHTML $HTML -Attachments $ExportFilePath