217 lines
7.3 KiB
PowerShell
217 lines
7.3 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Daily VMware Host metadata report for PowerBI trending and hardware capacity planning.
|
|
|
|
.DESCRIPTION
|
|
Collects ESXi host metadata from vCenter including hardware, compute capacity, and
|
|
version information. Exports a timestamped CSV and inserts into SQL each run.
|
|
Append daily runs to build a historical dataset for PowerBI trend analysis and
|
|
physical hardware purchasing / lifecycle decisions.
|
|
|
|
.NOTES
|
|
Run the following DDL once to create the destination table:
|
|
|
|
DROP TABLE IF EXISTS [dbo].[VMware_Trends_Host]
|
|
|
|
CREATE TABLE [dbo].[VMware_Trends_Host] (
|
|
[ReportDate] DATETIME2 NOT NULL,
|
|
[HostName] NVARCHAR(255) NOT NULL,
|
|
[Datacenter] NVARCHAR(100) NULL,
|
|
[Cluster] NVARCHAR(100) NULL,
|
|
[ConnectionState] NVARCHAR(50) NULL,
|
|
[PowerState] NVARCHAR(50) NULL,
|
|
[InMaintenanceMode] BIT NOT NULL,
|
|
[Model] NVARCHAR(255) NULL,
|
|
[ProcessorType] NVARCHAR(255) NULL,
|
|
[CpuSockets] INT NULL,
|
|
[CoresPerSocket] INT NULL,
|
|
[TotalCpuCores] INT NULL,
|
|
[CpuMhz] INT NULL,
|
|
[TotalCpuMhz] INT NULL,
|
|
[MemoryTotalGB] DECIMAL(10,2) NULL,
|
|
[MemoryUsageGB] DECIMAL(10,2) NULL,
|
|
[VMCount] INT NULL,
|
|
[EsxiVersion] NVARCHAR(50) NULL,
|
|
[EsxiBuild] NVARCHAR(50) NULL,
|
|
[UptimeDays] DECIMAL(10,2) NULL
|
|
)
|
|
|
|
MemoryUsageGB reflects real-time memory consumed by running VMs at the time of collection.
|
|
UptimeDays will be NULL for hosts that are powered off or disconnected.
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
)
|
|
|
|
Connect-ITDvCenter -Credential $Secret:ndgov_svcitdvmvcro
|
|
|
|
#region --- Setup ---------------------------------------------------------------
|
|
[string] $OutputPath = 'C:\temp\VM_Trends\'
|
|
[string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1'
|
|
[string] $Database = 'ITD-Systems-Automation'
|
|
[string] $Table = 'VMware_Trends_Host'
|
|
[System.Management.Automation.PSCredential] $SqlCredential = $Secret:sql_itdpsu1
|
|
|
|
$RunDate = Get-Date
|
|
$DateStamp = $RunDate.ToString('yyyyMMdd')
|
|
$Timestamp = $RunDate.ToString('yyyy-MM-dd')
|
|
|
|
if (-not (Test-Path -Path $OutputPath)) {
|
|
New-Item -ItemType Directory -Path $OutputPath | Out-Null
|
|
}
|
|
|
|
Start-Transcript -Path (Join-Path $OutputPath "VMHostMetadataReport_$DateStamp.log") -Append
|
|
|
|
#endregion
|
|
|
|
#region --- Build VMHost -> Cluster/Datacenter Lookups (avoids per-host queries) -
|
|
|
|
Write-Verbose 'Building host-to-cluster and host-to-datacenter maps...'
|
|
$HostClusterMap = @{}
|
|
$HostDatacenterMap = @{}
|
|
|
|
Get-Datacenter | ForEach-Object {
|
|
$DatacenterName = $_.Name
|
|
Get-VMHost -Location $_ | ForEach-Object {
|
|
$HostDatacenterMap[$_.Name] = $DatacenterName
|
|
}
|
|
}
|
|
|
|
Get-Cluster | ForEach-Object {
|
|
$ClusterName = $_.Name
|
|
Get-VMHost -Location $_ | ForEach-Object {
|
|
$HostClusterMap[$_.Name] = $ClusterName
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region --- Collect Host Data --------------------------------------------------
|
|
|
|
Write-Verbose 'Gathering hosts...'
|
|
$AllHosts = $HostDatacenterMap.Keys | ForEach-Object { Get-VMHost -Name $_ }
|
|
|
|
Write-Verbose "Processing $($AllHosts.Count) hosts..."
|
|
|
|
$Results = foreach ($VMHost in $AllHosts) {
|
|
|
|
$Ext = $VMHost.ExtensionData # single API object -- reuse for all fields
|
|
|
|
#--- CPU info from hardware summary
|
|
$CpuSockets = $Ext.Hardware.CpuInfo.NumCpuPackages
|
|
$TotalCpuCores = $Ext.Hardware.CpuInfo.NumCpuCores
|
|
$CoresPerSocket = if ($CpuSockets -gt 0) { [int]($TotalCpuCores / $CpuSockets) } else { $null }
|
|
$CpuMhz = $Ext.Hardware.CpuInfo.Hz / 1000000 # Hz -> MHz
|
|
$TotalCpuMhz = $Ext.Summary.Hardware.CpuMhz * $TotalCpuCores
|
|
|
|
#--- Memory
|
|
$MemoryTotalGB = [Math]::Round($VMHost.MemoryTotalGB, 2)
|
|
$MemoryUsageGB = [Math]::Round($VMHost.MemoryUsageGB, 2)
|
|
|
|
#--- VM count on this host right now
|
|
$VMCount = ($Ext.Vm | Measure-Object).Count
|
|
|
|
#--- Uptime (null when host is powered off / disconnected)
|
|
$UptimeDays = $null
|
|
$BootTime = $Ext.Runtime.BootTime
|
|
if ($null -ne $BootTime) {
|
|
$UptimeDays = [Math]::Round(($RunDate - $BootTime).TotalDays, 2)
|
|
}
|
|
|
|
[PSCustomObject]@{
|
|
ReportDate = $Timestamp
|
|
HostName = $VMHost.Name
|
|
Datacenter = $HostDatacenterMap[$VMHost.Name]
|
|
Cluster = $HostClusterMap[$VMHost.Name]
|
|
ConnectionState = $VMHost.ConnectionState
|
|
PowerState = $VMHost.PowerState
|
|
InMaintenanceMode = $VMHost.ExtensionData.Runtime.InMaintenanceMode
|
|
Model = $Ext.Hardware.SystemInfo.Model
|
|
ProcessorType = $VMHost.ProcessorType
|
|
CpuSockets = $CpuSockets
|
|
CoresPerSocket = $CoresPerSocket
|
|
TotalCpuCores = $TotalCpuCores
|
|
CpuMhz = [int]$CpuMhz
|
|
TotalCpuMhz = $TotalCpuMhz
|
|
MemoryTotalGB = $MemoryTotalGB
|
|
MemoryUsageGB = $MemoryUsageGB
|
|
VMCount = $VMCount
|
|
EsxiVersion = $VMHost.Version
|
|
EsxiBuild = $VMHost.Build
|
|
UptimeDays = $UptimeDays
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region --- Export CSV ---------------------------------------------------------
|
|
|
|
$OutputFile = Join-Path $OutputPath "VMHostMetadata_$DateStamp.csv"
|
|
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
|
|
Write-Verbose "Exported $($Results.Count) host records to: $OutputFile"
|
|
|
|
#endregion
|
|
|
|
#region --- SQL Insert ---------------------------------------------------------
|
|
|
|
$DataTable = [System.Data.DataTable]::new()
|
|
|
|
$ColDefs = [ordered]@{
|
|
ReportDate = [datetime]
|
|
HostName = [string]
|
|
Datacenter = [string]
|
|
Cluster = [string]
|
|
ConnectionState = [string]
|
|
PowerState = [string]
|
|
InMaintenanceMode = [bool]
|
|
Model = [string]
|
|
ProcessorType = [string]
|
|
CpuSockets = [int]
|
|
CoresPerSocket = [int]
|
|
TotalCpuCores = [int]
|
|
CpuMhz = [int]
|
|
TotalCpuMhz = [int]
|
|
MemoryTotalGB = [decimal]
|
|
MemoryUsageGB = [decimal]
|
|
VMCount = [int]
|
|
EsxiVersion = [string]
|
|
EsxiBuild = [string]
|
|
UptimeDays = [decimal]
|
|
}
|
|
|
|
foreach ($Col in $ColDefs.GetEnumerator()) {
|
|
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
|
|
$Column.AllowDBNull = $true
|
|
[void]$DataTable.Columns.Add($Column)
|
|
}
|
|
|
|
foreach ($Row in $Results) {
|
|
$DataRow = $DataTable.NewRow()
|
|
foreach ($Col in $ColDefs.Keys) {
|
|
$Val = $Row.$Col
|
|
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
|
|
}
|
|
[void]$DataTable.Rows.Add($DataRow)
|
|
}
|
|
|
|
$SqlParams = @{
|
|
ServerInstance = $ServerInstance
|
|
DatabaseName = $Database
|
|
SchemaName = 'dbo'
|
|
TableName = $Table
|
|
Credential = $SqlCredential
|
|
InputData = $DataTable
|
|
}
|
|
Write-SqlTableData @SqlParams
|
|
|
|
Write-Verbose "Inserted $($DataTable.Rows.Count) host records into [$Database].[dbo].[$Table]"
|
|
|
|
Disconnect-ITDvCenter
|
|
|
|
#endregion
|
|
|
|
#region --- Cleanup -------------------------------------------------------------
|
|
Stop-Transcript
|
|
|
|
#endregion
|