PowerShell to the Proxmox API part 2

In this continuation from part 1, I turn this script into a function that is much more usable day-to-day.

What improvements have been made? It’s a function now, so that makes it reusable without hardcoding each value. The function will save most values, like TokenUser and TokenSecret so you don’t have to re-enter them each time you run it. Note that importantly, that since TokenSecret is a SecureString, it is encrypted when saved to XML. Also, with the function, you can specify an individual VM ID, or don’t specify an ID to return all VMs on that node.

How does it work?

The first time you run it, you will need to specify parameters TokenUser, TokenSecret, Node, PveServer. This example will securely save those parameters. The parameters are using splatting to make them easier to read, and the example prompts for the TokenSecret so it won’t be listed in plaintext.

if (-not $Secret) { $Secret = Read-Host "Enter the TokenSecret" -AsSecureString }

# Example use:
$PveVmParams = @{
    TokenUser   = "username@pam!prov"                    # example: "username@pam!tokenname"
    TokenSecret = $Secret                                # example: "11111111-2222-3333-4444-555555555555"
    VmId        = "100"                                  # ID of VM
    Node        = "pve"                                  # PVE node name
    PveServer   = "192.168.0.123"                        # PVE node IP Address or if using SSL certificate, then use name as in certificate
}

Get-PveVM @PveVmParams

This is the output of the above example:

Name      : highcastle--debian
VmId      : 100
Status    : running
CPUs      : 4
MaxDiskGB : 20
MaxMemGB  : 8
Tags      : env-dev;os-debian
UpDays    : 0
UpHours   : 7
UpMinutes : 43
UpSeconds : 57

Following is another example. The Get-PveVM command is run without any parameters. Since the connection parameters were set and saved in the prior run, they are not needed again, unless they will change. Without “VmID” parameter, it returns all VMs on the node. We filter so we only show those VMs where the status is “running”, and then we format it as a table.

Get-PveVM | Where-Object Status -EQ "running" | Format-Table

This is the output:

Name                       VmId Status  CPUs MaxDiskGB MaxMemGB Tags                UpDays UpHours UpMinutes
----                       ---- ------  ---- --------- -------- ----                ------ ------- ---------
highcastle--debian          100 running    4        20        8 env-dev;os-debian        0       7        47
electricsheep--windows2022  101 running    2        60        4 env-dev;os-windows       0       7        47
active-directory1           102 running    2        60        4 env-prod;os-windows      0       7        47

The function

function Get-PveVM {
    [CmdletBinding()]
    param (
        [string]$TokenUser,
        [SecureString]$TokenSecret,
        [string]$VmId, # Optional
        [string]$Node,
        [string]$PveServer,
        [int]$PvePort = 8006,
        [string]$PveConnectSetingsPath = $env:LOCALAPPDATA + "\PveConnectSettings.XML"
    )

    begin {
        #region SETUP
        
        # import settings
        if (Test-Path -Path $PveConnectSetingsPath) {
            Write-Verbose -Message "Loading settings"
            $settings = Import-Clixml -Path $PveConnectSetingsPath
        }

        # apply imported settings if not specified in parameters
        if (-not $TokenUser) { $TokenUser = $settings.TokenUser }
        if (-not $TokenSecret) { $TokenSecret = $settings.TokenSecret }
        if (-not $Node) { $Node = $settings.Node }
        if (-not $PveServer) { $PveServer = $settings.PveServer }
        if (-not $PvePort) { $PvePort = $settings.PvePort }

        # Save settings
        $settings = @{
            TokenUser   = $TokenUser
            TokenSecret = $TokenSecret
            Node        = $Node
            PveServer   = $PveServer
            PvePort     = $PvePort
        }
        Export-Clixml -Path $PveConnectSetingsPath -InputObject $settings -Force -ErrorAction Continue -Depth 2

        # form URL
        if ($VmId) { $url = "https://${pveserver}:$pveport/api2/json/nodes/$node/qemu/$vmid/status/current" }
        else { $url = "https://${pveserver}:$pveport/api2/json/nodes/$node/qemu" }

        # set up authorization header to use token values
        $pveheader = @{Authorization = "PVEAPIToken=$tokenUser=$((New-Object PSCredential 0, $TokenSecret).GetNetworkCredential().Password)" }

        #endregion
    }
    
    process {
        #region EXCUTE

        # send the API request to PVE
        $result = Invoke-RestMethod -Headers $pveheader -Method Get -Uri $url -ErrorAction Stop
        #endregion

        #region OUTPUT
        # format the result
        $result.data | ForEach-Object {
            $uptimeSeconds = New-TimeSpan -Seconds $_.uptime
            [pscustomobject]@{
                Name      = $_.name
                VmId      = $_.vmid
                Status    = $_.status
                CPUs      = $_.cpus
                MaxDiskGB = $_.maxdisk / 1GB
                MaxMemGB  = $_.maxmem / 1GB
                Tags      = $_.tags
                UpDays    = $uptimeSeconds.Days
                UpHours   = $uptimeSeconds.Hours
                UpMinutes = $uptimeSeconds.Minutes
                UpSeconds = $uptimeSeconds.Seconds
            } 
        }
        #endregion
    }
    
    end {

    }
}