Lists every VM in a Proxmox cluster with its state and assigned resources, using API token authentication against the REST API. The calls go through curl.exe rather than Invoke-RestMethod, which is the pragmatic answer to self-signed certificates on a PVE node, and the token secret is a credential store reference rather than a value in the file.
powershell
# Proxmox VM Inventory
# Single use case: list all VMs in the cluster with state and resources
# Uses the Proxmox REST API with API Token Authentication
# Uses curl.exe for reliable SSL/TLS connections with self-signed certs
#-----------------------------------------------------------------
function Write-Log {
param($Message)
Write-Host "<WRITE-LOG = `"*$Message*`">"
}
function Invoke-ProxmoxAPI {
param([string]$Endpoint)
$url = "https://${ProxmoxHost}:8006/api2/json${Endpoint}"
$response = & curl.exe -s -k -H "Authorization: PVEAPIToken=${TokenUser}!${TokenName}=${TokenSecret}" $url 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Log "API call failed: $response"
return $null
}
try {
return $response | ConvertFrom-Json
} catch {
Write-Log "Failed to parse API response: $($_.Exception.Message)"
return $null
}
}
# --- Parameters ---
$ProxmoxHost = "pve.example.com"
# API Token credentials
$TokenUser = "SE-CredentialsStore.Username.(pve.example.com)"
$TokenName = "sentri"
$TokenSecret = "SE-CredentialsStore.Password.(pve.example.com)"
# --- Test Connection ---
Write-Log "Connecting to Proxmox at $ProxmoxHost..."
$test = Invoke-ProxmoxAPI -Endpoint "/nodes"
if ($null -eq $test) {
Write-Log "ERROR: Could not connect to Proxmox. Check host/token and try again."
return
}
# --- Get Cluster Status ---
$clusterStatus = Invoke-ProxmoxAPI -Endpoint "/cluster/resources"
if ($null -eq $clusterStatus) {
Write-Log "ERROR: Failed to fetch cluster resources."
return
}
# --- Get Nodes Info ---
$nodes = $clusterStatus.data | Where-Object { $_.type -eq "node" }
$storages = $clusterStatus.data | Where-Object { $_.type -eq "storage" }
Write-Log "================ Proxmox Cluster Status ================"
foreach ($node in $nodes) {
$nodeMemGB = [math]::Round($node.maxmem / 1GB, 1)
$nodeUptimeDays = [math]::Round($node.uptime / 86400, 1)
Write-Log "[ONLINE] Node: $($node.node) | vCPU: $($node.maxcpu) | RAM: $nodeMemGB GB | Uptime: $nodeUptimeDays days"
}
# --- Get VMs (QEMU) ---
$qemuVMs = Invoke-ProxmoxAPI -Endpoint "/cluster/resources?type=vm"
if ($null -eq $qemuVMs) {
Write-Log "WARN: Could not fetch VM resources, trying per-node query..."
$allVMs = @()
foreach ($node in $nodes) {
$nodeVMs = Invoke-ProxmoxAPI -Endpoint "/nodes/$($node.node)/qemu"
if ($nodeVMs -and $nodeVMs.data) {
$allVMs += $nodeVMs.data
}
}
} else {
$allVMs = $qemuVMs.data
}
# --- Get Containers (LXC) ---
$lxcContainers = @()
foreach ($node in $nodes) {
$nodeLXC = Invoke-ProxmoxAPI -Endpoint "/nodes/$($node.node)/lxc"
if ($nodeLXC -and $nodeLXC.data) {
$lxcContainers += $nodeLXC.data
}
}
$totalVMs = @($allVMs).Count + @($lxcContainers).Count
Write-Log "================ VM/Container Inventory ($totalVMs total) ================"
# --- Display QEMU VMs ---
if ($allVMs -and @($allVMs).Count -gt 0) {
Write-Log "--- QEMU Virtual Machines ($(@($allVMs).Count)) ---"
foreach ($vm in ($allVMs | Sort-Object status, name)) {
$memGB = [math]::Round($vm.maxmem / 1GB, 1)
$diskGB = [math]::Round($vm.maxdisk / 1GB, 0)
$up = if ($vm.status -eq "running") { " | Uptime: $([math]::Round($vm.uptime / 86400, 1))d" } else { "" }
Write-Log "[$($vm.status.ToUpper().PadRight(8))] ID: $($vm.vmid.ToString().PadLeft(4)) | vCPU: $($vm.maxcpu.ToString().PadLeft(2)) | RAM: $($memGB.ToString().PadLeft(5)) GB | Disk: $($diskGB.ToString().PadLeft(5)) GB | Node: $($vm.node) | Name: $($vm.name)$up"
}
} else {
Write-Log "--- QEMU Virtual Machines (0) ---"
Write-Log " No QEMU VMs found in cluster."
}
# --- Display LXC Containers ---
if ($lxcContainers -and @($lxcContainers).Count -gt 0) {
Write-Log "--- LXC Containers ($(@($lxcContainers).Count)) ---"
foreach ($ct in ($lxcContainers | Sort-Object status, name)) {
$memGB = [math]::Round($ct.maxmem / 1GB, 1)
$diskGB = [math]::Round($ct.maxdisk / 1GB, 0)
$up = if ($ct.status -eq "running") { " | Uptime: $([math]::Round($ct.uptime / 86400, 1))d" } else { "" }
Write-Log "[$($ct.status.ToUpper().PadRight(8))] ID: $($ct.vmid.ToString().PadLeft(4)) | vCPU: $($ct.maxcpu.ToString().PadLeft(2)) | RAM: $($memGB.ToString().PadLeft(5)) GB | Disk: $($diskGB.ToString().PadLeft(5)) GB | Node: $($ct.node) | Name: $($ct.name)$up"
}
} else {
Write-Log "--- LXC Containers (0) ---"
Write-Log " No LXC containers found in cluster."
}
# --- Summary ---
$runningVMs = @($allVMs | Where-Object { $_.status -eq "running" }).Count
$runningLXC = @($lxcContainers | Where-Object { $_.status -eq "running" }).Count
$totalRunning = $runningVMs + $runningLXC
Write-Log "================ Inventory Summary ================"
Write-Log "Total VMs/Containers: $totalVMs"
Write-Log "Currently Running: $totalRunning"
Write-Log "Offline/Stopped: $($totalVMs - $totalRunning)"
Write-Log "Cluster Nodes: $($nodes.Count)"
Write-Log "Storage Pools: $($storages.Count)"
# --- Pass data downstream ---
$vmsJson = @{
qemu = @($allVMs | ForEach-Object { @{ id = $_.vmid; name = $_.name; status = $_.status; node = $_.node } })
lxc = @($lxcContainers | ForEach-Object { @{ id = $_.vmid; name = $_.name; status = $_.status; node = $_.node } })
}
$store = ($vmsJson.qemu + $vmsJson.lxc).name -join ","
Write-Log "Store: $store"Run it across your fleet
This script runs as-is on a single host. Paste it into ServerEngine to schedule it, run it on a whole server group in parallel, and keep the credentials out of the file — see the scripts documentation and the credential store.
More in Proxmox VE
Ready when you are.
Try ServerEngine free for 7 days.