##
## Version 1.9
## Written By Brian Vogel bfvogel@gmail.com
## MIT License
##
##
param (
[switch]$Excel = $false
)
$ScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
try {
. ("$ScriptDirectory\Config.ps1")
}
catch {
Write-Host "Error while loading supporting PowerShell Scripts"
exit 1
}
$RATIOAlert = 2.5
$PERCENTILE = 90
$Lookback = 30
$Ready_Threshold = 10000
## Module Load
if (!(Get-Module -Name VMware.VimAutomation.Core) -and (Get-Module -ListAvailable -Name VMware.VimAutomation.Core)) {
Write-Output "loading the VMware COre Module..."
if (!(Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {
# Error out if loading fails
Write-Error "`nERROR: Cannot load the VMware Module. Is the PowerCLI installed?"
}
$Loaded = $True
}
elseif (!(Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -and !(Get-Module -Name VMware.VimAutomation.Core) -and ($Loaded -ne $True)) {
Write-Output "loading the VMware Core Snapin..."
if (!(Add-PSSnapin -PassThru VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {
# Error out if loading fails
Write-Error "`nERROR: Cannot load the VMware Snapin or Module. Is the PowerCLI installed?"
Write-Host "Try: 'Install-Module VMware.PowerCLI'"
}
}
#Connect-VIServer $hosts | out-null
Connect-VIServer $hosts -Credential $CREDS | out-null
#Get Date for file naming
$DATE = (get-date).ToString("yyyy-MM-dd")
#
#Get-Cluster | select name,@{n="NumVMHostCPU"; e={(Get-VMHost -Location $_ | Measure-Object NumCpu -Sum).Sum}}
$TotalNumvCPUs = 0
$TotalNumRAMs = 0
$HostNumProvisionedStorage = 0
$HostNumUsedStorage = 0
$ClusterTotalRAM = 0
$Cpurdy = 0
[System.Collections.ArrayList]$ClusterArray = @()
Foreach ($Cluster in (Get-Cluster |Sort Name)){
$HostNumvCPUs = 0
$HostNumRAMs = 0
$HostNumProvisionedStorage = 0
$HostNumUsedStorage = 0
$ClusterTotalRAM = 0
$CpurdyAVG = 0
$CpurdyMAX = 0
$CpurdyMIN = 9999999999
$CpuRdyVMPERCENTILE = @()
$ClusterRAMPercentile = @()
$CPUReadyArrayVMAVG = @()
Write-Host -NoNewLine "Cluster: $($Cluster.name)"
$ClusterNumCPUs = (Get-VMHost -Location $Cluster.name | Measure-Object NumCpu -Sum).Sum
$ClusterTotalRAM = (Get-VMHost -Location $Cluster.name | Measure-Object MemoryTotalGB -Sum).Sum
Foreach ($ESXHost in ($Cluster |Get-VMHost |Sort Name)){
Write-Host -NoNewLine -
$RunningLimit = $null
#$RunningLimit = ($ESXHost |Get-VMHostAdvancedConfiguration).get_Item(“Misc.RunningVCpuLimit“)
If ($RunningLimit -eq $null){
$RunningLimit = 128
}
Foreach ($VM in ($ESXHost |Get-VM)){
$HostNumvCPUs += ($VM).NumCpu
$HostNumRAMs += ($VM).MemoryGB
$HostNumProvisionedStorage += ($VM).ProvisionedSpaceGB
$HostNumUsedStorage += ($VM).UsedSpaceGB
if ( ($VM).PowerState -eq "PoweredOn" )
{
$StatCpurdy = Get-Stat -Entity ($VM) -start (get-date).AddDays(-$Lookback) -Finish (Get-Date)-MaxSamples 10000 -Intervalmins 30 -stat cpu.ready.summation
$Sequence = $StatCpurdy | Select -ExpandProperty Value
[int[]]$Sequence = $Sequence | Sort-Object
#write-host $Sequence
[int]$LENGTH = $Sequence.Length
[int]$MIN = $Sequence | select -first 1
[int]$MAX = $Sequence | select -last 1
[int]$RANGE = ($MAX - $MIN) + 1
$VMrdyStat = ($RANGE / 100) * $PERCENTILE
#Write-host $VM.Name MIN:$MIN MAX:$MAX RANGE:$RANGE PERCENTILE:$VMrdyStat
#$VMrdyStat
$CpuRdyVMPERCENTILE += $VMrdyStat
$CpurdyVMAVG = ([math]::round(($StatCpurdy | Measure-Object -Property Value -Average).Average,0))
$CPUReadyArrayVMAVG += $CpurdyVMAVG
if ( $CpurdyMAX -lt ($StatCpurdy | select -ExpandProperty Value| sort-object | select -last 1) )
{
$CpurdyMAX = $StatCpurdy | select -ExpandProperty Value| sort-object | select -last 1
}
if ( $CpurdyMIN -gt ($StatCpurdy | select -ExpandProperty Value| sort-object | select -first 1) )
{
$CpurdyMIN = $StatCpurdy | select -ExpandProperty Value| sort-object | select -first 1
}
}
}
#Write-Host “ Number of vCPU on host: $($HostNumvCPUs)“
$TotalNumvCPUs += $HostNumvCPUs
$TotalNumRAMs += $HostNumRAMs
$TotalNumProvisionedStorage += $HostNumProvisionedStorage
$TotalNumUsedStorage += $HostNumUsedStorage
#$Cpurdy += $StatCpurdy | Measure-Object -Property value -Average -Maximum -Minimum
#Gather Host MIN/Max RAM
$HostStatRAM = Get-Stat -Entity ($ESXHost) -start (get-date).AddDays(-$Lookback) -Finish (Get-Date)-MaxSamples 10000 -Intervalmins 30 -stat mem.usage.average
$HostStatRAMMax = ($HostStatRAM | Sort -Property Value | Select -last 1).Value
#Get the average and count CPU Ready fo all VM's across cluster
foreach($i in $HostStatRAM.Value){
$RunningTotal += $i
}
$HostStatRAMAvg = ([decimal]($RunningTotal) / [decimal]($HostStatRAM.Length));
$RunningTotal = $null
#
$HostStatRAMMin = ($HostStatRAM | Sort -Property Value | Select -first 1).Value
$TotalNumRAMMax += $HostStatRAMMax
$TotalNumRAMAvg += $HostStatRAMAvg
$TotalNumRAMMin += $HostStatRAMMin
#Get RAMPercentaile of collective vm ready percentiles
[int[]]$Sequence = ($ESXHost| Get-Stat -Stat mem.usage.average -MaxSamples 10000 -Intervalmins 30 -Start (get-date).AddDays(-$Lookback) -Finish (Get-Date)).Value
[int[]]$Sequence = $Sequence | Sort-Object
#write-host $Sequence
[int]$LENGTH = $Sequence.Length
#[int]$MIN = $Sequence | select -first 1
[int]$MIN = 0
[int]$MAX = $Sequence | select -last 1
[int]$RANGE = ($MAX - $MIN) + 1
$HostRAMPercentile = ($RANGE / 100) * $PERCENTILE
$ClusterRAMPercentile += $HostRAMPercentile
$HostNumvCPUs = 0
$HostNumRAMs = 0
$HostNumProvisionedStorage = 0
$HostNumUsedStorage = 0
$VMrdyStat = 0
$CpurdyVMAVG = 0
$HostStatRAM = 0
$HostStatRAMMax = 0
$HostStatRAMMin = 0
$HostStatRAMAvg= 0
$HostRAMPercentile = 0
}
#Get the average and count CPU Ready fo all VM's across cluster
foreach($i in $CPUReadyArrayVMAVG){
$RunningTotal += $i
}
$CpurdyAVG = ([decimal]($RunningTotal) / [decimal]($CPUReadyArrayVMAVG.Length));
$RunningTotal = $null
#$CpurdyAVG
$RatioCPU = $TotalNumvCPUs / $ClusterNumCPUs
$TotalNumRAMMax = $TotalNumRAMMax / ($Cluster | Get-VMHost).Count
$TotalNumRAMAvg = $TotalNumRAMAvg / ($Cluster | Get-VMHost).Count
$TotalNumRAMMin = $TotalNumRAMMin / ($Cluster | Get-VMHost).Count
$ClusterNumRAMs = [math]::round(($Cluster | Get-Stat -Stat mem.usage.average -MaxSamples 10000 -Intervalmins 30 -Start (get-date).AddDays(-$Lookback) -Finish (Get-Date) | Measure-Object -Property Value -Average).Average,0)
#Get CPUReady Percentaile of collective vm ready percentiles
$Sequence = $CpuRdyVMPERCENTILE
[int[]]$Sequence = $Sequence | Sort-Object
#write-host $Sequence
[int]$LENGTH = $Sequence.Length
[int]$MIN = $Sequence | select -first 1
[int]$MAX = $Sequence | select -last 1
[int]$RANGE = ($MAX - $MIN) + 1
$ClusterPercentile = ($RANGE / 100) * $PERCENTILE
#Write-host $ESXHost.Name MIN:$MIN MAX:$MAX RANGE:$RANGE PERCENTILE:$ClusterPercentile
#Get RAMPercentaile of collective Host percentiles
[int[]]$Sequence = $ClusterRAMPercentile
[int[]]$Sequence = $Sequence | Sort-Object
$Sequence
#write-host $Sequence
[int]$LENGTH = $Sequence.Length
#[int]$MIN = $Sequence | select -first 1
#MIN needs to be 0 as we are factoring a percentile of percentages
[int]$MIN = 0
[int]$MAX = $Sequence | select -last 1
[int]$RANGE = ($MAX - $MIN) + 1
$ClusterRAMPercentile = ($RANGE / 100) * $PERCENTILE
## STDDEV Math
if ($CPUReadyArrayVMAVG -match '\d+' -and $CPUReadyArrayVMAVG.Count -gt 1) {
#Variables used later
[decimal]$newNumbers = $Null
[decimal]$stdDev = $null
#Get the average and count
foreach($i in $CPUReadyArrayVMAVG){
$RunningTotal += $i
}
$avgValue = ([decimal]($RunningTotal) / [decimal]($CPUReadyArrayVMAVG.Length));
$RunningTotal = $null
$avgCount = $CPUReadyArrayVMAVG.Length
#Iterate through each of the numbers and get part of the variance via some PowerShell math.
ForEach($number in $CPUReadyArrayVMAVG) {
$newNumbers += [Math]::Pow(($number - $avgValue), 2)
}
#Finish the variance calculation, and get the square root to finally get the standard deviation.
$ClusterstdDev = [math]::Sqrt($($newNumbers / ($avgCount - 1)))
}
$ClusterObject = [PSCustomObject]@{
Name = $Cluster.name
VMHost_Count = ($Cluster | Get-VMHost).Count
pCPU_Count = $ClusterNumCPUs
vCPU_Count = $TotalNumvCPUs
CPU_Prov_Ratio = [math]::Round($RatioCPU,2)
#
#https://kb.vmware.com/s/article/2002181
#
CPU_Contention_PCT = [math]::Round(($CpurdyAVG / (300 * 1000)) * 1000,0)
CPU_Ready_PCTILE_ms = [math]::Round($ClusterPercentile,0)
CPU_Ready_AVG_ms = [math]::Round($CpurdyAVG,0)
CPU_Ready_STDDEV_ms = [math]::Round($ClusterstdDev,0)
CPU_Ready_MAX_ms = $CpurdyMAX
CPU_Ready_MIN_ms = $CpurdyMIN
'RAM_Total_GB' = [math]::Round($ClusterTotalRAM,0)
'RAM_Provisioned_GB' = [math]::Round($TotalNumRAMs,0)
'RAM_PCTILE' = $ClusterRAMPercentile
'RAM_Active_PCT' = $ClusterNumRAMs
'RAM_Utilized_MAX_PCT' = [math]::Round($TotalNumRAMMax,0)
'RAM_Utilized_MIN_PCT' = [math]::Round($TotalNumRAMMin,0)
'RAM_Utilized_AVG_PCT' = [math]::Round($TotalNumRAMAvg,0)
Prov_Storage_GB = [math]::Round($TotalNumProvisionedStorage,0)
Used_Storage_GB = [math]::Round($TotalNumUsedStorage,0)
}
$ClusterArray += $ClusterObject
Write-Host "|"
$ClusterNumCPUs = 0
$TotalNumvCPUs = 0
$TotalNumRAMs = 0
$ClusterTotalRAM = 0
$ClusterNumRAMs = 0
$TotalNumProvisionedStorage = 0
$TotalNumUsedStorage = 0
$CpurdyAVG = 0
$CpurdyMAX = 0
$CpurdyMIN = 9999999999
$CpurdyPCTILE = 0
$CpuRdyVMPERCENTILE = $null
$ClusterPercentile = 0
$ClusterstdDev = 0
$CPUReadyArrayVMAVG = $null
$CpurdyVMAVG = 0
$CPUReadyArrayVMAVG = $null
$TotalNumRAMMax = 0
$TotalNumRAMAvg = 0
$TotalNumRAMMin = 0
$ClusterRAMPercentile = 0
$RAMPERCENTILE = 0
}
$ClusterArray | Export-csv -path $OUTPUTDIR\Capacity-$DATE.csv
if ($Excel)
{
$FileName = "$OUTPUTDIR\Capacity-$DATE"
# create some CSV data
Get-Process | Export-Csv -Path "$FileName.csv" -NoTypeInformation -Encoding UTF8
# load into Excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
# change thread culture
[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'
$excel.Workbooks.Open("$FileName.csv").SaveAs("$FileName.xlsx",51)
$excel.Quit()
}
if ($SMTPSERVER)
{
Send-MailMessage -SmtpServer $SMTPServer -Port $SMTPPort -From $SMTPFrom -To $SMTPTo -Subject "VMWare Capacity Metrics $Date" -Body "These are the capacity metrics for all configured VMWare environments" -Attachments $OUTPUTDIR\Capacity-$DATE.csv -Priority High | out-null
}
if ($Post)
{
$uri = "https://www.nobrandsan.com/filedrop/index.php"
$contentType = "multipart/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu"
$body = @{
"uploaded_file" = Get-Content -Path "$OUTPUTDIR\Capacity-$DATE.csv" -Raw
}
Invoke-WebRequest -UseBasicParsing -Uri $uri -Method Post -ContentType $contentType -Body $body
}
Disconnect-VIServer * -confirm:$false