{"id":87,"date":"2021-06-30T13:39:42","date_gmt":"2021-06-30T17:39:42","guid":{"rendered":"https:\/\/nobrandsan.com\/wordpress\/?p=87"},"modified":"2021-06-30T13:39:43","modified_gmt":"2021-06-30T17:39:43","slug":"vmware-capacity-metrics-powershell","status":"publish","type":"post","link":"https:\/\/nobrandsan.com\/wordpress\/index.php\/2021\/06\/30\/vmware-capacity-metrics-powershell\/","title":{"rendered":"VMware Capacity Metrics PowerShell"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>##\r\n## Version 1.9\r\n## Written By Brian Vogel bfvogel@gmail.com \r\n## MIT License\r\n##\r\n##\r\nparam (\r\n&#91;switch]$Excel = $false\r\n\r\n)\r\n$ScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent\r\ntry {\r\n    . (\"$ScriptDirectory\\Config.ps1\")\r\n}\r\ncatch {\r\n    Write-Host \"Error while loading supporting PowerShell Scripts\" \r\n\texit 1\r\n}\r\n\r\n\r\n$RATIOAlert = 2.5 \r\n$PERCENTILE = 90\r\n$Lookback = 30\r\n$Ready_Threshold = 10000\r\n\r\n\r\n## Module Load\r\nif (!(Get-Module -Name VMware.VimAutomation.Core) -and (Get-Module -ListAvailable -Name VMware.VimAutomation.Core)) {  \r\n    Write-Output \"loading the VMware COre Module...\"  \r\n    if (!(Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {  \r\n        # Error out if loading fails  \r\n        Write-Error \"`nERROR: Cannot load the VMware Module. Is the PowerCLI installed?\"  \r\n     }  \r\n    $Loaded = $True  \r\n    }  \r\n    elseif (!(Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -and !(Get-Module -Name VMware.VimAutomation.Core) -and ($Loaded -ne $True)) {  \r\n        Write-Output \"loading the VMware Core Snapin...\"  \r\n     if (!(Add-PSSnapin -PassThru VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {  \r\n     # Error out if loading fails  \r\n     Write-Error \"`nERROR: Cannot load the VMware Snapin or Module. Is the PowerCLI installed?\"  \r\n\t Write-Host \"Try: 'Install-Module VMware.PowerCLI'\"\r\n     }  \r\n    }  \r\n#Connect-VIServer $hosts | out-null\r\n\r\nConnect-VIServer $hosts -Credential $CREDS | out-null\r\n#Get Date for file naming\r\n$DATE = (get-date).ToString(\"yyyy-MM-dd\")\r\n#\r\n#Get-Cluster | select name,@{n=\"NumVMHostCPU\"; e={(Get-VMHost -Location $_ | Measure-Object NumCpu -Sum).Sum}}\r\n$TotalNumvCPUs = 0\r\n$TotalNumRAMs = 0\r\n$HostNumProvisionedStorage = 0\r\n$HostNumUsedStorage = 0\r\n$ClusterTotalRAM = 0\r\n$Cpurdy = 0\r\n&#91;System.Collections.ArrayList]$ClusterArray = @()\r\nForeach ($Cluster in (Get-Cluster |Sort Name)){\r\n\t$HostNumvCPUs = 0\r\n\t$HostNumRAMs = 0\r\n\t$HostNumProvisionedStorage = 0\r\n\t$HostNumUsedStorage = 0\r\n\t$ClusterTotalRAM = 0\r\n\t$CpurdyAVG = 0\r\n\t$CpurdyMAX  = 0\r\n\t$CpurdyMIN = 9999999999\r\n\t$CpuRdyVMPERCENTILE = @()\r\n\t$ClusterRAMPercentile = @()\r\n\t$CPUReadyArrayVMAVG = @()\r\n\tWrite-Host -NoNewLine \"Cluster: $($Cluster.name)\"\r\n\t$ClusterNumCPUs = (Get-VMHost -Location $Cluster.name | Measure-Object NumCpu -Sum).Sum\r\n\t$ClusterTotalRAM = (Get-VMHost -Location $Cluster.name | Measure-Object MemoryTotalGB -Sum).Sum\r\n\r\n\r\n\r\n\tForeach ($ESXHost in ($Cluster |Get-VMHost |Sort Name)){\r\n\t\tWrite-Host -NoNewLine -\r\n\r\n\t\t$RunningLimit = $null\r\n\t\t#$RunningLimit = ($ESXHost |Get-VMHostAdvancedConfiguration).get_Item(\u201cMisc.RunningVCpuLimit\u201c)\r\n\t\tIf ($RunningLimit -eq $null){\r\n\t\t\t$RunningLimit = 128\r\n\t\t}\r\n\r\n\t\tForeach ($VM in ($ESXHost |Get-VM)){\r\n\t\t\t$HostNumvCPUs += ($VM).NumCpu\r\n\t\t\t$HostNumRAMs += ($VM).MemoryGB\r\n\t\t\t$HostNumProvisionedStorage += ($VM).ProvisionedSpaceGB\r\n\t\t\t$HostNumUsedStorage += ($VM).UsedSpaceGB\r\n\t\t\tif ( ($VM).PowerState -eq \"PoweredOn\" )\r\n\t\t\t{\r\n\t\t\t\t$StatCpurdy = Get-Stat -Entity ($VM) -start (get-date).AddDays(-$Lookback) -Finish (Get-Date)-MaxSamples 10000 -Intervalmins 30 -stat cpu.ready.summation\r\n\r\n\r\n\r\n\t\t\t\t$Sequence = $StatCpurdy | Select -ExpandProperty Value\r\n\t\t\t\t&#91;int&#91;]]$Sequence = $Sequence | Sort-Object\r\n\t\t\t\t#write-host $Sequence\r\n\t\t\t\t&#91;int]$LENGTH = $Sequence.Length\r\n\t\t\t\t&#91;int]$MIN = $Sequence | select -first 1\r\n\t\t\t\t&#91;int]$MAX = $Sequence | select -last 1\r\n\t\t\t\t&#91;int]$RANGE = ($MAX - $MIN) + 1\r\n\t\t\t\t\r\n\t\t\t\t$VMrdyStat =  ($RANGE \/ 100) * $PERCENTILE\r\n\t\t\t\t#Write-host $VM.Name MIN:$MIN MAX:$MAX RANGE:$RANGE PERCENTILE:$VMrdyStat\r\n\t\t\t\t#$VMrdyStat\r\n\t\t\t\t$CpuRdyVMPERCENTILE += $VMrdyStat\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t$CpurdyVMAVG  = (&#91;math]::round(($StatCpurdy | Measure-Object -Property Value -Average).Average,0)) \r\n\t\t\t\t\r\n\t\t\t\t$CPUReadyArrayVMAVG += $CpurdyVMAVG \r\n\t\t\t\t\r\n\r\n\t\t\t\tif ( $CpurdyMAX -lt ($StatCpurdy | select -ExpandProperty Value| sort-object | select -last 1) )\r\n\t\t\t\t{\r\n\t\t\t\t\t$CpurdyMAX  = $StatCpurdy | select -ExpandProperty Value| sort-object | select -last 1\r\n\t\t\t\t}\r\n\t\t\t\tif ( $CpurdyMIN -gt ($StatCpurdy | select -ExpandProperty Value| sort-object | select -first 1) )\r\n\t\t\t\t{\r\n\t\t\t\t\t$CpurdyMIN  = $StatCpurdy | select -ExpandProperty Value| sort-object | select -first 1\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n#Write-Host \u201c Number of vCPU on host: $($HostNumvCPUs)\u201c\r\n\t\t$TotalNumvCPUs += $HostNumvCPUs\r\n\t\t$TotalNumRAMs += $HostNumRAMs\r\n\t\t$TotalNumProvisionedStorage += $HostNumProvisionedStorage\r\n\t\t$TotalNumUsedStorage += $HostNumUsedStorage\r\n#$Cpurdy += $StatCpurdy | Measure-Object -Property value -Average -Maximum -Minimum \r\n#Gather Host MIN\/Max RAM\r\n\t\t$HostStatRAM = Get-Stat -Entity ($ESXHost) -start (get-date).AddDays(-$Lookback) -Finish (Get-Date)-MaxSamples 10000 -Intervalmins 30 -stat mem.usage.average\r\n\r\n\t\t$HostStatRAMMax =  ($HostStatRAM | Sort -Property Value | Select -last 1).Value\r\n\t\t#Get the average and count CPU Ready fo all VM's across cluster\r\n\t\tforeach($i in $HostStatRAM.Value){\r\n\t\t$RunningTotal += $i\r\n\t\t}\r\n\t\t$HostStatRAMAvg  =  (&#91;decimal]($RunningTotal) \/ &#91;decimal]($HostStatRAM.Length));\r\n\t\t$RunningTotal = $null\r\n\t\t#\r\n\t\t$HostStatRAMMin =  ($HostStatRAM | Sort -Property Value | Select -first 1).Value\r\n\r\n\t\t$TotalNumRAMMax += $HostStatRAMMax\r\n\t\t$TotalNumRAMAvg += $HostStatRAMAvg\r\n\t\t$TotalNumRAMMin += $HostStatRAMMin     \r\n\t\t\r\n\t\t#Get RAMPercentaile of collective vm ready percentiles\r\n\t\r\n\t&#91;int&#91;]]$Sequence = ($ESXHost| Get-Stat -Stat mem.usage.average -MaxSamples 10000 -Intervalmins 30 -Start (get-date).AddDays(-$Lookback) -Finish (Get-Date)).Value\r\n\t\r\n\t&#91;int&#91;]]$Sequence = $Sequence | Sort-Object\r\n\t#write-host $Sequence\r\n\t&#91;int]$LENGTH = $Sequence.Length\r\n\t#&#91;int]$MIN = $Sequence | select -first 1\r\n\t&#91;int]$MIN = 0\r\n\t&#91;int]$MAX = $Sequence | select -last 1\r\n\t&#91;int]$RANGE = ($MAX - $MIN) + 1\r\n\t\r\n\t$HostRAMPercentile =  ($RANGE \/ 100) * $PERCENTILE\r\n\t$ClusterRAMPercentile += $HostRAMPercentile\r\n\t\t$HostNumvCPUs = 0\r\n\t\t$HostNumRAMs = 0\r\n\t\t$HostNumProvisionedStorage = 0\r\n\t\t$HostNumUsedStorage = 0\r\n\t\t$VMrdyStat = 0\r\n\t\t$CpurdyVMAVG  = 0\r\n\t\t$HostStatRAM = 0\r\n\r\n\t\t$HostStatRAMMax = 0\r\n\t\t$HostStatRAMMin = 0\r\n\t\t$HostStatRAMAvg= 0\r\n\t\t$HostRAMPercentile = 0\r\n\r\n\t}\r\n#Get the average and count CPU Ready fo all VM's across cluster\r\n\t\tforeach($i in $CPUReadyArrayVMAVG){\r\n\t\t$RunningTotal += $i\r\n\t\t}\r\n\t\t$CpurdyAVG  =  (&#91;decimal]($RunningTotal) \/ &#91;decimal]($CPUReadyArrayVMAVG.Length));\r\n\t\t$RunningTotal = $null\r\n\t\t\r\n\t\t#$CpurdyAVG\r\n\t\t\r\n$RatioCPU = $TotalNumvCPUs \/ $ClusterNumCPUs\r\n\r\n\r\n$TotalNumRAMMax = $TotalNumRAMMax \/ ($Cluster | Get-VMHost).Count\r\n$TotalNumRAMAvg = $TotalNumRAMAvg \/ ($Cluster | Get-VMHost).Count\r\n$TotalNumRAMMin = $TotalNumRAMMin \/ ($Cluster | Get-VMHost).Count\r\n\r\n\r\n$ClusterNumRAMs = &#91;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)\r\n\r\n\r\n#Get CPUReady Percentaile of collective vm ready percentiles\r\n\t$Sequence = $CpuRdyVMPERCENTILE\r\n\t&#91;int&#91;]]$Sequence = $Sequence | Sort-Object\r\n\t#write-host $Sequence\r\n\t&#91;int]$LENGTH = $Sequence.Length\r\n\t&#91;int]$MIN = $Sequence | select -first 1\r\n\t&#91;int]$MAX = $Sequence | select -last 1\r\n\t&#91;int]$RANGE = ($MAX - $MIN) + 1\r\n\t\r\n\t$ClusterPercentile =  ($RANGE \/ 100) * $PERCENTILE\r\n\t#Write-host $ESXHost.Name MIN:$MIN MAX:$MAX RANGE:$RANGE PERCENTILE:$ClusterPercentile\r\n\r\n\r\n#Get RAMPercentaile of collective Host percentiles\r\n\t\r\n\t&#91;int&#91;]]$Sequence = $ClusterRAMPercentile\r\n\t\r\n\t&#91;int&#91;]]$Sequence = $Sequence | Sort-Object\r\n\t$Sequence\r\n\t#write-host $Sequence\r\n\t&#91;int]$LENGTH = $Sequence.Length\r\n\t#&#91;int]$MIN = $Sequence | select -first 1\r\n\t#MIN needs to be 0 as we are factoring a percentile of percentages\r\n\t&#91;int]$MIN = 0\r\n\t&#91;int]$MAX = $Sequence | select -last 1\r\n\t&#91;int]$RANGE = ($MAX - $MIN) + 1\r\n\t\r\n\t$ClusterRAMPercentile =  ($RANGE \/ 100) * $PERCENTILE\r\n## STDDEV Math\r\n\r\n\tif ($CPUReadyArrayVMAVG -match '\\d+' -and $CPUReadyArrayVMAVG.Count -gt 1) {\r\n\r\n\t\t#Variables used later\r\n\t\t&#91;decimal]$newNumbers  = $Null\r\n\t\t&#91;decimal]$stdDev      = $null\r\n\t\t\r\n\t\t#Get the average and count \r\n\t\tforeach($i in $CPUReadyArrayVMAVG){\r\n\t\t$RunningTotal += $i\r\n\t\t}\r\n\t\t$avgValue  =  (&#91;decimal]($RunningTotal) \/ &#91;decimal]($CPUReadyArrayVMAVG.Length));\r\n\t\t$RunningTotal = $null\r\n\t\t\r\n\t\t$avgCount             = $CPUReadyArrayVMAVG.Length\r\n\t\t\r\n\t\t#Iterate through each of the numbers and get part of the variance via some PowerShell math.\r\n\t\tForEach($number in $CPUReadyArrayVMAVG) {\r\n\r\n\t\t\t$newNumbers += &#91;Math]::Pow(($number - $avgValue), 2)\r\n\r\n\t\t}\r\n\r\n\t\t#Finish the variance calculation, and get the square root to finally get the standard deviation.\r\n\t\t$ClusterstdDev = &#91;math]::Sqrt($($newNumbers \/ ($avgCount - 1)))\r\n\r\n\t\t\r\n\t\t}\r\n\r\n\r\n\r\n\r\n\r\n$ClusterObject = &#91;PSCustomObject]@{\r\n\r\n\t\t\tName     \t\t\t= $Cluster.name\r\n\t\t\tVMHost_Count     \t\t= ($Cluster | Get-VMHost).Count\r\n\t\t\tpCPU_Count        \t\t= $ClusterNumCPUs\r\n\t\t\tvCPU_Count                = $TotalNumvCPUs\r\n\t\t\tCPU_Prov_Ratio\t\t\t= &#91;math]::Round($RatioCPU,2)\r\n\t\t\t\r\n\t\t\t#\r\n\t\t\t#https:\/\/kb.vmware.com\/s\/article\/2002181\r\n\t\t\t#\r\n\t\t\tCPU_Contention_PCT\t\t\t= &#91;math]::Round(($CpurdyAVG \/ (300 * 1000)) * 1000,0)\r\n\t\t\tCPU_Ready_PCTILE_ms\t\t\t= &#91;math]::Round($ClusterPercentile,0) \r\n\t\t\tCPU_Ready_AVG_ms\t\t\t= &#91;math]::Round($CpurdyAVG,0)\r\n\t\t\tCPU_Ready_STDDEV_ms\t\t\t= &#91;math]::Round($ClusterstdDev,0)\r\n\t\t\tCPU_Ready_MAX_ms\t\t\t= $CpurdyMAX\r\n\t\t\tCPU_Ready_MIN_ms\t\t\t= $CpurdyMIN\r\n\t\t\t'RAM_Total_GB'         = &#91;math]::Round($ClusterTotalRAM,0)\r\n\t\t\t'RAM_Provisioned_GB'        = &#91;math]::Round($TotalNumRAMs,0)\r\n\t\t\t'RAM_PCTILE'\t\t\t=\t$ClusterRAMPercentile\r\n\t\t\t'RAM_Active_PCT'    = $ClusterNumRAMs\r\n\t\t\t'RAM_Utilized_MAX_PCT'\t= &#91;math]::Round($TotalNumRAMMax,0)\r\n\t\t\t'RAM_Utilized_MIN_PCT'\t= &#91;math]::Round($TotalNumRAMMin,0)\r\n\t\t\t'RAM_Utilized_AVG_PCT'\t= &#91;math]::Round($TotalNumRAMAvg,0)\r\n\t\t\tProv_Storage_GB         = &#91;math]::Round($TotalNumProvisionedStorage,0)\r\n\t\t\tUsed_Storage_GB         = &#91;math]::Round($TotalNumUsedStorage,0)\r\n\r\n\t\t\t\r\n\t\t}\r\n\r\n$ClusterArray += $ClusterObject\r\n\r\nWrite-Host \"|\"\r\n$ClusterNumCPUs = 0\r\n$TotalNumvCPUs = 0\r\n$TotalNumRAMs = 0\r\n$ClusterTotalRAM = 0\r\n$ClusterNumRAMs = 0\r\n$TotalNumProvisionedStorage = 0\r\n$TotalNumUsedStorage = 0\r\n$CpurdyAVG = 0\r\n$CpurdyMAX  = 0\r\n$CpurdyMIN = 9999999999\r\n$CpurdyPCTILE = 0\r\n$CpuRdyVMPERCENTILE = $null\r\n$ClusterPercentile  = 0\r\n$ClusterstdDev = 0\r\n$CPUReadyArrayVMAVG = $null\r\n$CpurdyVMAVG  = 0\r\n$CPUReadyArrayVMAVG = $null\r\n$TotalNumRAMMax = 0\r\n$TotalNumRAMAvg = 0\r\n$TotalNumRAMMin = 0\r\n$ClusterRAMPercentile = 0\r\n$RAMPERCENTILE  = 0\r\n}\r\n$ClusterArray | Export-csv -path $OUTPUTDIR\\Capacity-$DATE.csv\r\n\r\nif ($Excel)\r\n{\r\n$FileName = \"$OUTPUTDIR\\Capacity-$DATE\"\r\n\r\n# create some CSV data\r\nGet-Process | Export-Csv -Path \"$FileName.csv\" -NoTypeInformation -Encoding UTF8\r\n\r\n# load into Excel\r\n$excel = New-Object -ComObject Excel.Application \r\n$excel.Visible = $true\r\n\r\n# change thread culture\r\n&#91;System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'\r\n\r\n$excel.Workbooks.Open(\"$FileName.csv\").SaveAs(\"$FileName.xlsx\",51)\r\n$excel.Quit()\r\n}\r\n\r\nif ($SMTPSERVER)\r\n{\r\nSend-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\r\n}\r\n\r\nif ($Post)\r\n{\r\n$uri = \"https:\/\/nobrandsan.com\/wordpress\/filedrop\/index.php\"\r\n$contentType = \"multipart\/form-data; boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu\"\r\n$body = @{\r\n        \"uploaded_file\" = Get-Content -Path \"$OUTPUTDIR\\Capacity-$DATE.csv\" -Raw\r\n    }\r\nInvoke-WebRequest -UseBasicParsing -Uri $uri -Method Post -ContentType $contentType -Body $body\r\n}\r\nDisconnect-VIServer * -confirm:$false<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[1],"tags":[5,6,4],"class_list":["post-87","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-powershell","tag-scripting","tag-vmware"],"_links":{"self":[{"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/87","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=87"}],"version-history":[{"count":1,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/87\/revisions"}],"predecessor-version":[{"id":88,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/87\/revisions\/88"}],"wp:attachment":[{"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=87"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=87"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nobrandsan.com\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=87"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}