helpers/proxmox/Get-ProxmoxDashboard.ps1

<#
.SYNOPSIS
    Generates an interactive HTML dashboard report for Proxmox VE environments.
.DESCRIPTION
    Orchestration script that connects to one or more Proxmox VE servers via the
    REST API, collects VM details (CPU, memory, disk, network, HA state), and
    produces a searchable, sortable Bootstrap Table HTML dashboard. Output
    includes both a JSON data file and a self-contained HTML report.
.PARAMETER ProxmoxServers
    One or more Proxmox VE server URIs (e.g. https://192.168.1.100:8006).
    If omitted, prompts interactively.
.PARAMETER Username
    Proxmox authentication username (e.g. root@pam). If omitted, prompts interactively.
.PARAMETER Password
    Proxmox authentication password. If omitted, prompts interactively as a secure string.
.PARAMETER SkipSSLCheck
    Bypass SSL certificate validation. Useful for lab environments with self-signed
    certificates.
.EXAMPLE
    .\Get-ProxmoxDashboard.ps1
 
    Prompts for Proxmox server(s) and credentials, then generates the dashboard.
.EXAMPLE
    .\Get-ProxmoxDashboard.ps1 -ProxmoxServers "https://192.168.1.100:8006" -Username "root@pam"
 
    Generates a dashboard for a single Proxmox server (prompts for password).
.EXAMPLE
    .\Get-ProxmoxDashboard.ps1 -ProxmoxServers "https://pve1:8006","https://pve2:8006" -SkipSSLCheck
 
    Connects to multiple Proxmox servers with SSL bypass and generates a combined dashboard.
.EXAMPLE
    .\Get-ProxmoxDashboard.ps1 -ProxmoxServers "https://pve:8006" -Username "admin@pve" -Password "MyPass"
 
    Generates a dashboard with explicit credentials (non-interactive).
.OUTPUTS
    System.Void
    Produces a JSON file (proxmox_dashboard.json) and an HTML dashboard
    (Proxmox-Dashboard.html) in the system temp directory, then opens the HTML in the default browser.
.NOTES
    Author : jason@wug.ninja
    Version : 1.0.0
    Date : 2025-07-15
    Requires: PowerShell 5.1+, network access to Proxmox VE API (port 8006), ProxmoxHelpers.ps1 in the same directory.
.LINK
    https://github.com/jayyx2/WhatsUpGoldPS
#>


param (
    [Parameter(Mandatory = $false)]
    [string[]]$ProxmoxServers,

    [Parameter(Mandatory = $false)]
    [string]$Username,

    [Parameter(Mandatory = $false)]
    [string]$Password,

    [Parameter(Mandatory = $false)]
    [switch]$SkipSSLCheck
)

# --- Configuration -----------------------------------------------------------
$helpersPath = Join-Path $PSScriptRoot "ProxmoxHelpers.ps1"
if (Test-Path $helpersPath) {
    . $helpersPath
}
else {
    throw "ProxmoxHelpers.ps1 not found at $helpersPath. Ensure it is in the same directory."
}

if (Get-Module -ListAvailable -Name WhatsUpGoldPS) {
    if (-not (Get-Module -Name WhatsUpGoldPS)) {
        Import-Module -Name WhatsUpGoldPS
    }
}

# --- Input prompts -----------------------------------------------------------
if (-not $ProxmoxServers -or $ProxmoxServers.Count -eq 0) {
    $serverInput = Read-Host -Prompt "Enter Proxmox VE server(s) - e.g. https://192.168.1.100:8006 (comma-separated)"
    $ProxmoxServers = $serverInput -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
}

if (-not $ProxmoxServers -or $ProxmoxServers.Count -eq 0) {
    throw "At least one Proxmox VE server must be specified."
}

if (-not $Username) {
    $Username = Read-Host -Prompt "Enter Proxmox username (e.g. root@pam)"
}

if (-not $Password) {
    $securePassword = Read-Host -Prompt "Enter Proxmox password" -AsSecureString
    $Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
        [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
    )
}

# Output paths
$outputDir = if ($env:TEMP) { $env:TEMP } else { "C:\temp" }
$jsonPath  = Join-Path $outputDir "proxmox_dashboard.json"
$htmlPath  = Join-Path $outputDir "Proxmox-Dashboard.html"
if (-not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null }

# --- Connect and collect data ------------------------------------------------
if ($SkipSSLCheck) {
    Initialize-SSLBypass
}

$allData = @()

foreach ($server in $ProxmoxServers) {
    Write-Host "Connecting to $server ..." -ForegroundColor Cyan
    try {
        $cookie = Connect-ProxmoxServer -Server $server -Username $Username -Password $Password
        Write-Host " Authenticated to $server" -ForegroundColor Green

        $data = Get-ProxmoxDashboard -Server $server -Cookie $cookie
        if ($data) { $allData += $data }
        Write-Host " Collected $(@($data).Count) VM(s) from $server" -ForegroundColor Green
    }
    catch {
        Write-Warning "Failed to connect to ${server}: $($_.Exception.Message)"
    }
}

if (-not $allData -or $allData.Count -eq 0) {
    Write-Warning "No VM data collected. Exiting."
    return
}

# --- Summary -----------------------------------------------------------------
$running = @($allData | Where-Object { $_.Status -eq 'running' }).Count
$stopped = @($allData | Where-Object { $_.Status -eq 'stopped' }).Count
$other   = $allData.Count - $running - $stopped
$nodes   = @($allData | Select-Object -ExpandProperty Node -Unique).Count

Write-Host "`n--- Proxmox Summary ---" -ForegroundColor Yellow
Write-Host " Nodes: $nodes"
Write-Host " Running: $running" -ForegroundColor Green
Write-Host " Stopped: $stopped" -ForegroundColor Red
if ($other -gt 0) { Write-Host " Other: $other" -ForegroundColor DarkYellow }

# --- Generate outputs --------------------------------------------------------
$allData | ConvertTo-Json -Depth 5 | Out-File $jsonPath -Force -Encoding UTF8
Write-Host "`nJSON data written to $jsonPath" -ForegroundColor Yellow

$templatePath = Join-Path $PSScriptRoot "Proxmox-Dashboard-Template.html"
Export-ProxmoxDashboardHtml -DashboardData $allData -OutputPath $htmlPath -ReportTitle "Proxmox VE Dashboard" -TemplatePath $templatePath
Write-Host "HTML dashboard written to $htmlPath" -ForegroundColor Yellow

# --- Optional: Open in browser -----------------------------------------------
if ($env:OS -match 'Windows') {
    $openBrowser = Read-Host -Prompt "Open dashboard in browser? (Y/N)"
    if ($openBrowser -match '^[Yy]') {
        Start-Process $htmlPath
    }
}

Write-Host "Done." -ForegroundColor Green

# SIG # Begin signature block
# MIIVlwYJKoZIhvcNAQcCoIIViDCCFYQCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAENQw+zKMFo8b4
# tVNpIlMRYKukMYYpWTt1R+AFKijcNaCCEdMwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggY+MIIEpqADAgECAhAHnODk0RR/hc05c892LTfrMA0GCSqG
# SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw
# HhcNMjYwMjA5MDAwMDAwWhcNMjkwNDIxMjM1OTU5WjBVMQswCQYDVQQGEwJVUzEU
# MBIGA1UECAwLQ29ubmVjdGljdXQxFzAVBgNVBAoMDkphc29uIEFsYmVyaW5vMRcw
# FQYDVQQDDA5KYXNvbiBBbGJlcmlubzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBAPN6aN4B1yYWkI5b5TBj3I0VV/peETrHb6EY4BHGxt8Ap+eT+WpEpJyE
# tRYPxEmNJL3A38Bkg7mwzPE3/1NK570ZBCuBjSAn4mSDIgIuXZnvyBO9W1OQs5d6
# 7MlJLUAEufl18tOr3ST1DeO9gSjQSAE5Nql0QDxPnm93OZBon+Fz3CmE+z3MwAe2
# h4KdtRAnCqwM+/V7iBdbw+JOxolpx+7RVjGyProTENIG3pe/hKvPb501lf8uBAAD
# LdjZr5ip8vIWbf857Yw1Bu10nVI7HW3eE8Cl5//d1ribHlzTzQLfttW+k+DaFsKZ
# BBL56l4YAlIVRsrOiE1kdHYYx6IGrEA809R7+TZA9DzGqyFiv9qmJAbL4fDwetDe
# yIq+Oztz1LvEdy8Rcd0JBY+J4S0eDEFIA3X0N8VcLeAwabKb9AjulKXwUeqCJLvN
# 79CJ90UTZb2+I+tamj0dn+IKMEsJ4v4Ggx72sxFr9+6XziodtTg5Luf2xd6+Phha
# mOxF2px9LObhBLLEMyRsCHZIzVZOFKu9BpHQH7ufGB+Sa80Tli0/6LEyn9+bMYWi
# 2ttn6lLOPThXMiQaooRUq6q2u3+F4SaPlxVFLI7OJVMhar6nW6joBvELTJPmANSM
# jDSRFDfHRCdGbZsL/keELJNy+jZctF6VvxQEjFM8/bazu6qYhrA7AgMBAAGjggGJ
# MIIBhTAfBgNVHSMEGDAWgBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQU
# 6YF0o0D5AVhKHbVocr8GaSIBibAwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC
# MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIB
# AwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EM
# AQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2Vj
# dGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBE
# BggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGlj
# Q29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl
# Y3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQAEIsm4xnOd/tZMVrKwi3doAXvC
# wOA/RYQnFJD7R/bSQRu3wXEK4o9SIefye18B/q4fhBkhNAJuEvTQAGfqbbpxow03
# J5PrDTp1WPCWbXKX8Oz9vGWJFyJxRGftkdzZ57JE00synEMS8XCwLO9P32MyR9Z9
# URrpiLPJ9rQjfHMb1BUdvaNayomm7aWLAnD+X7jm6o8sNT5An1cwEAob7obWDM6s
# X93wphwJNBJAstH9Ozs6LwISOX6sKS7CKm9N3Kp8hOUue0ZHAtZdFl6o5u12wy+z
# zieGEI50fKnN77FfNKFOWKlS6OJwlArcbFegB5K89LcE5iNSmaM3VMB2ADV1FEcj
# GSHw4lTg1Wx+WMAMdl/7nbvfFxJ9uu5tNiT54B0s+lZO/HztwXYQUczdsFon3pjs
# Nrsk9ZlalBi5SHkIu+F6g7tWiEv3rtVApmJRnLkUr2Xq2a4nbslUCt4jKs5UX4V1
# nSX8OM++AXoyVGO+iTj7z+pl6XE9Gw/Td6WKKKsxggMaMIIDFgIBATBoMFQxCzAJ
# BgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNl
# Y3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYCEAec4OTRFH+FzTlzz3Yt
# N+swDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZ
# BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB
# BAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg/vOpHCHPBU+/WT+m49kO3TCmOV9LHGBQ
# Pn4fweQ2p3owDQYJKoZIhvcNAQEBBQAEggIAi5ytv7ioko8+fFam0UMCYyhbYwWG
# MEaDeOoPW3i2r9d7g59F+QFFfV7n9MIqwds7AbWcG00R7SYolCzkQZm0Tr8PJNj5
# 82CVoWCDuR55VwID04d5C4J/Wo+g62Gfg4d/uI+woY/e2gpWwBcwfuVd4uJw/BuT
# pWAZgK9QURxJWYHxtAEYdhR/LjPd1hq5gUgeAzHj1W7p5kP47gb5X1VtmHwF3a21
# RX+K28NZQpmxYLfzIsoHkUIdY0uGX9NYHGAjKs8aIUqOorixZSh6QvPlzOuz7Vjn
# qwp9SnAWObzZUtYS7ds0oBmPe0s2XNeDPj88gv5bgwxPu5ipVGIi4UEFdiX5PmEf
# SsCABB8hsRiaUo74uCNEHTAF5S7fTlQHMlFk5yEiSjSjkDtaiSeP/ySnu2beo73v
# XeDZAp32Sa6OU2Ojbn219JGct7X708GSlYcPjGoxk1Y1zqvHi9rrduP4sXeYxZYH
# iIHckdUbNA7pcfvSkBLuZVhjwGl23KwpCvC9sg0BgMfpk/uYW9n42ByorKqA84zM
# b1Q6pglq8XP4DJv4F8B10e8QgPv2yHlVpHw3EhUnZxsVgstaOdIGXjEaMWkevZm8
# XDMq5X3PswWe4ijYJx9hrQWF76bTzR3WDz0wOP+HTLQXdsqSvBvsMS74Jt5JqTwe
# DGE7pbdbiTpK1Tw=
# SIG # End signature block