← script library

Proxmox VM Inventory with PowerShell and the API

Proxmox VEAugust 17, 2026

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.

Ready when you are.

Try ServerEngine free for 7 days.