<# .SYNOPSIS Daily HPE OneView enclosure and server inventory for PowerBI trending and lifecycle planning. .DESCRIPTION Iterates across all configured HPE OneView appliances, collecting enclosure and server hardware metadata including compute capacity, power state, and server profile assignment. Exports timestamped CSVs and inserts into SQL each run. Append daily runs to build a historical dataset for PowerBI trend analysis and physical hardware purchasing / lifecycle decisions. Appliances processed: itdoneviewp1.nd.gov - Rack servers only (no enclosures) itdbissyncompp1.nd.gov - Synergy enclosures + servers itdmdnsyncompp1.nd.gov - Synergy enclosures + servers .NOTES Run the following DDL once to create the destination tables: DROP TABLE IF EXISTS [dbo].[VMware_Trends_Enclosure] CREATE TABLE [dbo].[VMware_Trends_Enclosure] ( [ReportDate] DATETIME2 NOT NULL, [ApplianceConnection] NVARCHAR(100) NULL, [EnclosureName] NVARCHAR(255) NOT NULL, [EnclosureModel] NVARCHAR(255) NULL, [EnclosureSerialNumber] NVARCHAR(100) NULL, [Status] NVARCHAR(50) NULL, [DeviceBayCount] INT NULL ) DROP TABLE IF EXISTS [dbo].[VMware_Trends_Server] CREATE TABLE [dbo].[VMware_Trends_Server] ( [ReportDate] DATETIME2 NOT NULL, [ApplianceConnection] NVARCHAR(100) NULL, [EnclosureName] NVARCHAR(255) NULL, [BayNumber] INT NULL, [ServerHardwareName] NVARCHAR(255) NOT NULL, [ServerName] NVARCHAR(255) NULL, [ServerModel] NVARCHAR(255) NULL, [ServerSerialNumber] NVARCHAR(100) NULL, [Status] NVARCHAR(50) NULL, [PowerState] NVARCHAR(50) NULL, [ServerProfileName] NVARCHAR(255) NULL, [ProcessorType] NVARCHAR(255) NULL, [ProcessorCount] INT NULL, [MemoryGB] DECIMAL(10,2) NULL, [FormFactor] NVARCHAR(100) NULL ) EnclosureName and BayNumber are NULL for rack-mounted servers not seated in an enclosure. ServerName is the iLO/DNS hostname populated once a server profile is assigned. #> [CmdletBinding()] param( ) #region --- Setup --------------------------------------------------------------- [string[]] $OVHostnames = @( 'itdoneviewp1.nd.gov', 'itdbissyncompp1.nd.gov', 'itdmdnsyncompp1.nd.gov' ) [string] $OutputPath = 'C:\temp\OV_Trends\' [string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1' [string] $Database = 'ITD-Systems-Automation' [string] $EnclosureTable = 'VMware_Trends_Enclosure' [string] $ServerTable = 'VMware_Trends_Server' [System.Management.Automation.PSCredential] $OVCredential = $Secret:ndgov_svcitdvmhpe [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 "OVServerInventory_$DateStamp.log") -Append #endregion #region --- Collect Data From All Appliances ----------------------------------- $AllEnclosureResults = [System.Collections.Generic.List[PSCustomObject]]::new() $AllServerResults = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($OVHostname in $OVHostnames) { Write-Verbose "Connecting to HPE OneView: $OVHostname" Connect-OVMgmt -Hostname $OVHostname -Credential $OVCredential -AuthLoginDomain nd.gov -LoginAcknowledge #--- Enclosures ------------------------------------------------------------ Write-Verbose "[$OVHostname] Gathering enclosures..." $Enclosures = Get-OVEnclosure # Build URI map for server lookups (only populated if there are enclosures) $EnclosureUriMap = @{} foreach ($Enclosure in $Enclosures) { $EnclosureUriMap[$Enclosure.uri] = $Enclosure.name $AllEnclosureResults.Add([PSCustomObject]@{ ReportDate = $Timestamp ApplianceConnection = $OVHostname EnclosureName = $Enclosure.name EnclosureModel = $Enclosure.model EnclosureSerialNumber = $Enclosure.serialNumber Status = $Enclosure.status DeviceBayCount = ($Enclosure.deviceBays | Measure-Object).Count }) } #--- Servers --------------------------------------------------------------- Write-Verbose "[$OVHostname] Gathering servers..." $Servers = Get-OVServer | Where-Object { $_.serverName -like 'itdvm*' } # Build profile URI map for this appliance $ProfileUriMap = @{} Get-OVServerProfile | ForEach-Object { $ProfileUriMap[$_.uri] = $_.name } Write-Verbose "[$OVHostname] Processing $($Servers.Count) servers..." foreach ($Server in $Servers) { $EnclosureName = if ($Server.locationUri) { $EnclosureUriMap[$Server.locationUri] } else { $null } $ProfileName = if ($Server.serverProfileUri) { $ProfileUriMap[$Server.serverProfileUri] } else { $null } $MemoryGB = if ($null -ne $Server.memoryMb -and $Server.memoryMb -gt 0) { [Math]::Round($Server.memoryMb / 1024, 2) } else { $null } $AllServerResults.Add([PSCustomObject]@{ ReportDate = $Timestamp ApplianceConnection = $OVHostname EnclosureName = $EnclosureName BayNumber = if ($null -ne $Server.position) { [int]$Server.position } else { $null } ServerHardwareName = $Server.name ServerName = $Server.serverName ServerModel = $Server.model ServerSerialNumber = $Server.serialNumber Status = $Server.status PowerState = $Server.powerState ServerProfileName = $ProfileName ProcessorType = $Server.processorType ProcessorCount = if ($null -ne $Server.processorCount) { [int]$Server.processorCount } else { $null } MemoryGB = $MemoryGB FormFactor = $Server.formFactor }) } Disconnect-OVMgmt Write-Verbose "[$OVHostname] Disconnected." } #endregion #region --- Export CSVs -------------------------------------------------------- $EnclosureCsv = Join-Path $OutputPath "OVEnclosures_$DateStamp.csv" $ServerCsv = Join-Path $OutputPath "OVServers_$DateStamp.csv" $AllEnclosureResults | Export-Csv -Path $EnclosureCsv -NoTypeInformation $AllServerResults | Export-Csv -Path $ServerCsv -NoTypeInformation Write-Verbose "Exported $($AllEnclosureResults.Count) enclosure records to: $EnclosureCsv" Write-Verbose "Exported $($AllServerResults.Count) server records to: $ServerCsv" #endregion #region --- SQL Insert: Enclosures --------------------------------------------- $EnclosureTable_DT = [System.Data.DataTable]::new() $EnclosureColDefs = [ordered]@{ ReportDate = [datetime] ApplianceConnection = [string] EnclosureName = [string] EnclosureModel = [string] EnclosureSerialNumber = [string] Status = [string] DeviceBayCount = [int] } foreach ($Col in $EnclosureColDefs.GetEnumerator()) { $Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value) $Column.AllowDBNull = $true [void]$EnclosureTable_DT.Columns.Add($Column) } foreach ($Row in $AllEnclosureResults) { $DataRow = $EnclosureTable_DT.NewRow() foreach ($Col in $EnclosureColDefs.Keys) { $Val = $Row.$Col $DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value } } [void]$EnclosureTable_DT.Rows.Add($DataRow) } Write-SqlTableData -ServerInstance $ServerInstance -DatabaseName $Database -SchemaName 'dbo' ` -TableName $EnclosureTable -Credential $SqlCredential -InputData $EnclosureTable_DT Write-Verbose "Inserted $($EnclosureTable_DT.Rows.Count) enclosure records into [$Database].[dbo].[$EnclosureTable]" #endregion #region --- SQL Insert: Servers ------------------------------------------------ $ServerTable_DT = [System.Data.DataTable]::new() $ServerColDefs = [ordered]@{ ReportDate = [datetime] ApplianceConnection = [string] EnclosureName = [string] BayNumber = [int] ServerHardwareName = [string] ServerName = [string] ServerModel = [string] ServerSerialNumber = [string] Status = [string] PowerState = [string] ServerProfileName = [string] ProcessorType = [string] ProcessorCount = [int] MemoryGB = [decimal] FormFactor = [string] } foreach ($Col in $ServerColDefs.GetEnumerator()) { $Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value) $Column.AllowDBNull = $true [void]$ServerTable_DT.Columns.Add($Column) } foreach ($Row in $AllServerResults) { $DataRow = $ServerTable_DT.NewRow() foreach ($Col in $ServerColDefs.Keys) { $Val = $Row.$Col $DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value } } [void]$ServerTable_DT.Rows.Add($DataRow) } Write-SqlTableData -ServerInstance $ServerInstance -DatabaseName $Database -SchemaName 'dbo' ` -TableName $ServerTable -Credential $SqlCredential -InputData $ServerTable_DT Write-Verbose "Inserted $($ServerTable_DT.Rows.Count) server records into [$Database].[dbo].[$ServerTable]" #endregion #region --- Cleanup ------------------------------------------------------------ Stop-Transcript #endregion