<#
.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  = $PrvCred  #$Secret:ndgov_svcitdvmhpe
[System.Management.Automation.PSCredential] $SqlCredential = $SqlCred    #$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
