* ci: take review instructions from the default branch, and gate self-approval Moves the review prompt out of claude-code-review.yml into .github/review-prompt.md, materialized from the default branch at review time. Prompt edits then neither trip claude-code-action's anti-tamper gate nor influence the review of the PR making them. PR #131 is what prompted this: 38 files of ADR migration were blocked from review by a four-line prompt edit. A security review of the first draft found the naive materialization was itself exploitable, and that is fixed here rather than shipped: - git show "origin/$DEFAULT_BRANCH:..." takes an UNQUALIFIED refname, and gitrevisions(7) resolves refs/tags/<name> before refs/remotes/<name>. With fetch-depth: 0 fetching all tags, a tag named "origin/main" would supply the review instructions for every PR, exiting 0 with only a stderr warning. Reproduced end to end. Now resolves refs/remotes/origin/<default> to a SHA, logs it, and reads by SHA. - CLAUDE.md is materialized from the default branch too. The prompt judges against its Key Conventions list, so reading it from the PR checkout let a PR edit the list to permit its own violation. - A PR touching review-prompt.md, .github/workflows/ or CLAUDE.md now has any claude[bot] APPROVED dismissed via the API and the check failed. Prose alone cannot protect the root of trust. - The sentinel is grepped in the materialize step rather than only asserted by the model it protects. - Fork PRs are skipped, not failed. A required check permanently red on outside contributions trains the operator to override red checks. Also drops track_progress and gh pr comment so the review body and inline comments are the only channel, and adds actions: read plus gh pr checks / gh run view so the reviewer can verify build and test claims against CI's own result. It deliberately gets no build or test tools: those execute PR-authored code in a job that can approve the PR. ADR 0025 records the decision. * ci: fix three defects found in second-opinion review - Job-level 'actions: read' was missing. An explicit permissions block sets every unlisted permission to none, so additional_permissions: actions: read on the action alone granted nothing and the reviewer could not have read the check runs it was just told to verify claims against. Athena has both; only the action-level half was copied. - The governance detector checked review-prompt.md, .github/workflows/ and CLAUDE.md, while review-prompt.md told the reviewer DECISIONS.md and docs/decisions/ were mechanically covered too. A PR adding an ADR could escape the guard it was promised to be under. Detector now covers both, which means every ADR PR needs operator approval — that is the intended reading of ADR 0023, since the reviewer defers to ADRs as precedent. - The formal-review check counted ANY historical claude[bot] review, so a re-run after a new push went green on a verdict about the previous commit. Now scoped to the head SHA. Pre-existing, fixed here because the file was already open. Adds a fork-notice job. A skipped job reports its required check as PASSING, so skipping the review on fork PRs made them green with nothing reviewed and no trace of why; the notice puts it in the run summary. Prompt: pending checks are a race, so say unverified rather than reporting a queued check as a failure; and defer-to-operator maps to --comment, not --request-changes. * ci: withhold approval on review-governing PRs, not the review itself Operator ruling: Claude should still review the protected files and report what it finds; only the power to approve is reserved. The gate failed the check unconditionally whenever a PR touched a governance path, even when the reviewer had correctly submitted COMMENTED. That discarded a review that was wanted, and made a red check the normal outcome for a whole class of PR — training exactly the merge-past-red habit the fork-notice change exists to avoid. Now: a COMMENTED deferral passes, with a notice saying the check is green because the reviewer behaved, not because the PR is approved. Merge still waits for the operator, since COMMENTED does not satisfy branch protection. The step fails only when claude[bot] actually approved — that approval is dismissed and the red check records the disobedience. ADRs stay in the protected set, per the same ruling. * ci: close a green-with-live-approval hole and widen the governance detector Third security pass. The two that mattered: - Both review queries were unpaginated. GitHub returns 30 reviews oldest-first, so an approval submitted now sits on page 2 of any PR that already has 30 review objects — the withhold step would find nothing, print 'reviewer behaved correctly' and exit 0 green while the approval stood and satisfied branch protection. Reachable without an attacker: every inline comment creates a review object. Both queries now --paginate. - The detector covered CLAUDE.md but not .claude/, .mcp.json, AGENTS.md, or nested CLAUDE.md. .claude/settings.json is tracked and .gitignore had no claude entry, so a PR could add .claude/settings.local.json - which outranks settings.json - carrying env (redirect model traffic) or hooks (arbitrary shell in the job holding the approval token). Those are read by the runtime before the model starts, so no prompt-level rule can defend against them. Added to the detector and to .gitignore. Also: dismissal now matches .user.type == 'Bot' rather than the claude[bot] login literal, so an approval from another App is not invisible; the test fixtures, psd1 and CHANGELOG join the detector, since the prompt already reserved release tagging to the operator and did not enforce it; a failed dismissal says so loudly instead of aborting silently under bash -e and claiming success; the pre-review notice no longer promises a red check on the path that goes green; show-ref --verify replaces rev-parse, which still DWIMs on a ref that does not exist; and a concurrency group stops two runs interleaving dismissals. Dropped the '@claude re-review' suggestion from the fail-closed message: it pointed at an ungated workflow on exactly the PRs where approval is reserved. ADR 0025 now states the premise the whole design rests on - that code-owner review is off, deliberately, because enabling it would end bot merging - and records claude.yml as an open second path to a binding approval. * ci: narrow CODEOWNERS so code-owner review becomes usable Operator's suggestion, and it is better than what ADR 0025 previously recorded. CODEOWNERS was '* @goodolclint'. At that breadth 'Require review from Code Owners' is unusable — it would demand the operator on every PR and end the verdict-gated merge loop — which is why the setting is off and why the self-approval guard had to live in the workflow. Narrowed to the governance and release paths only, matching the detector. An ordinary PR has no code owner and an automated approval still merges it; a PR touching what governs review or what gets published requires the operator. That makes the setting safe to enable, and GitHub then enforces the property better than the workflow step can: not one-shot, no pagination limit, no bot identity to match, no dismissal permission needed, and it covers an approval from any source — including claude.yml, the ungated second path the dismissal step cannot see. Enabling the setting is the operator's action, not this commit's. Until then the workflow gate remains load-bearing, and it stays either way as defence-in-depth. Both files carry a keep-in-sync note; drift is silent in the direction that matters. ADR 0025 records the edge case: GitHub does not let an author approve their own PR, so an operator-authored governance PR would need admin enforcement toggled or to go through the bot. * ci: stop the green-path notice claiming more than it checked Third-party re-review: the empty-id branch announced 'It reviewed and deferred, as intended', but an empty list only means no automated APPROVED was found. It cannot distinguish a deferral from CHANGES_REQUESTED, from no verdict, or from no review at all — that a formal review exists at this head SHA is established by the verify step, not this one. The notice now says what was actually checked, and says plainly that green does not mean approved or adequately reviewed. ADR 0025 said a prompt edit 'gets a red check', contradicting its own statement two paragraphs earlier that a deferral passes. Corrected, and it now records the one governance path that genuinely gets no review: this workflow itself, where the action's anti-tamper gate means there is no verdict to observe. --------- Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
PSProxmoxVE
A production-grade C# binary PowerShell module for managing Proxmox VE environments.
Supported Proxmox VE Versions
| PVE Version | Status |
|---|---|
| 9.x (current, 9.1.6+) | Primary target — fully supported |
| 8.x | Supported |
| 7.x (7.0+) | Best-effort — core cmdlets work, newer features emit clear version errors |
| 6.x and older | Not supported — hard blocked by version checks |
Version Gating Policy
The module uses a two-tier version check for cmdlets that require newer PVE APIs:
- Hard block (introduced version): The API endpoint doesn't exist — the cmdlet emits a terminating error with a clear message like "This operation requires Proxmox VE 8.1 or later."
- Warning (default version): The feature exists but may not be enabled by default — a warning is emitted but the command proceeds, allowing users who manually enabled the feature to succeed.
Most cmdlets target endpoints available since PVE 4.2 and require no version check. Cmdlets that need newer APIs include SDN (introduced 6.2, default 8.0+), cloud-init management (7.2+), container interfaces (8.1+), VM disk import (8.1+), and pool management (8.1+ for update/delete).
Prerequisites
- PowerShell 5.1 (Windows PowerShell) or 7.2+
- OS: Windows, Linux, macOS
- Network: HTTPS access to Proxmox VE API (default port 8006)
.NET Compatibility
The module ships as a single netstandard2.0 assembly, which runs on both .NET Framework and .NET Core/.NET:
| PowerShell Version | Runtime | Status |
|---|---|---|
| 5.1 (Windows PowerShell) | .NET Framework 4.8 | Fully supported |
| 7.2, 7.4, 7.5 | .NET 8.0 / 10.0 | Fully supported |
Installation
# Install from the PowerShell Gallery
Install-Module -Name PSProxmoxVE -Scope CurrentUser
# Or install a prerelease version
Install-Module -Name PSProxmoxVE -Scope CurrentUser -AllowPrerelease
# Verify installation
Get-Module -ListAvailable PSProxmoxVE
Import-Module PSProxmoxVE
Quick Start
Connect to a Proxmox VE Server
# Using username/password (ticket-based authentication)
$cred = Get-Credential -UserName 'root@pam'
Connect-PveServer -Server 'pve.example.com' -Credential $cred -SkipCertificateCheck
# Using API token
Connect-PveServer -Server 'pve.example.com' -ApiToken 'root@pam!mytoken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Verify connection
Test-PveConnection -Detailed
List and Manage VMs
# List all VMs
Get-PveVm
# List VMs on a specific node
Get-PveVm -Node 'pve1'
# Filter by status
Get-PveVm -Status 'running'
# Start/stop VMs
Get-PveVm -Name 'my-vm' | Start-PveVm -Wait
Get-PveVm -Name 'my-vm' | Stop-PveVm -Wait
# Clone a VM
Get-PveVm -VmId 100 | Copy-PveVm -NewVmId 200 -NewName 'my-clone' -Full -Wait
# Get VM configuration
Get-PveVm -VmId 100 | Get-PveVmConfig
Upload Files
# Upload a local ISO file to Proxmox storage
Send-PveFile -Node 'pve1' -Storage 'local' -Path './ubuntu-24.04-live-server-amd64.iso' -Wait
# Upload a disk image for VM import
Send-PveFile -Node 'pve1' -Storage 'local' -Path './disk.qcow2' -ContentType 'import' -Wait
Note:
Send-PveFileimplements a workaround for a long-standing Proxmox API multipart parsing bug (bugzilla 7389). Standard multipart HTTP libraries (including .NET'sMultipartFormDataContent) add sub-headers that Proxmox'spveproxymishandles, resulting in corrupt uploads. This cmdlet constructs the multipart body manually to ensure correct uploads where other tools may produce corrupt files.
Work with Snapshots
# List snapshots
Get-PveVm -VmId 100 | Get-PveSnapshot
# Create a snapshot
Get-PveVm -VmId 100 | New-PveSnapshot -Name 'before-upgrade' -Description 'Snapshot before OS upgrade' -Wait
# Rollback
Get-PveVm -VmId 100 | Get-PveSnapshot | Where-Object Name -eq 'before-upgrade' | Restore-PveSnapshot -Wait
Cloud-Init Configuration
# Get current cloud-init config
Get-PveVm -VmId 100 | Get-PveCloudInitConfig
# Set cloud-init config
Get-PveVm -VmId 100 | Set-PveCloudInitConfig -Hostname 'web01' -User 'admin' -SshKeys @('ssh-ed25519 AAAA...') -IpConfig 'ip=dhcp' -Wait
# Regenerate cloud-init image after changes
Get-PveVm -VmId 100 | Invoke-PveCloudInitRegenerate -Wait
Authentication Guide
Username/Password (Ticket-Based)
Ticket authentication uses Proxmox's built-in session system. The module POSTs to /api2/json/access/ticket and stores the returned ticket cookie and CSRF token.
- Format:
user@realm(e.g.,root@pam,admin@pve,user@mydomain) - Expiry: Tickets expire after 2 hours. The module detects expiry and prompts you to reconnect.
- Realms: Supports all Proxmox realms —
pam,pve, custom LDAP/AD realms. - When to use: Interactive sessions, ad-hoc management tasks.
$cred = Get-Credential -UserName 'admin@pve'
Connect-PveServer -Server 'pve.example.com' -Credential $cred
API Token
API tokens provide persistent, non-expiring authentication. They are the recommended approach for automation.
- Format:
USER@REALM!TOKENID=UUID(e.g.,root@pam!automation=12345678-abcd-efgh-ijkl-123456789012) - No expiry: Tokens remain valid until explicitly revoked.
- When to use: Automation, scripts, CI/CD pipelines.
Creating an API token in the PVE UI:
- Navigate to Datacenter → Permissions → API Tokens
- Click Add
- Select the user, enter a token ID, optionally uncheck "Privilege Separation"
- Copy the token value — it is shown only once
Connect-PveServer -Server 'pve.example.com' -ApiToken 'root@pam!automation=12345678-abcd-efgh-ijkl-123456789012'
Multi-Cluster Usage
Every cmdlet accepts an optional -Session parameter. This enables managing multiple Proxmox VE clusters simultaneously:
# Connect to two clusters
$prod = Connect-PveServer -Server 'pve-prod.example.com' -ApiToken $prodToken -PassThru
$dev = Connect-PveServer -Server 'pve-dev.example.com' -ApiToken $devToken -PassThru
# Query each cluster explicitly
$prodVms = Get-PveVm -Session $prod
$devVms = Get-PveVm -Session $dev
# The last Connect-PveServer call sets the default session
# So Get-PveVm without -Session uses $dev
Get-PveVm # Uses $dev session
SDN Management
Software-Defined Networking (SDN) features require Proxmox VE 8.0 or later.
# List SDN zones and VNets
Get-PveSdnZone
Get-PveSdnVnet
# Create a new zone
New-PveSdnZone -Zone 'myzone' -Type 'simple'
# Create a VNet
New-PveSdnVnet -Vnet 'myvnet' -Zone 'myzone' -Tag 100
If connected to a PVE server below version 8.0, SDN cmdlets will throw a clear error:
SDN management requires Proxmox VE 8.0 or later. Connected server is version 7.4.
Cmdlet Reference
Connection
| Cmdlet | Description |
|---|---|
Connect-PveServer |
Establish a session to a Proxmox VE server |
Disconnect-PveServer |
Close the active session |
Test-PveConnection |
Test if the current session is valid |
Nodes
| Cmdlet | Description |
|---|---|
Get-PveNode |
List cluster nodes |
Get-PveNodeStatus |
Get detailed node status |
Virtual Machines
| Cmdlet | Description |
|---|---|
Get-PveVm |
List VMs with optional filters |
New-PveVm |
Create a new VM |
Remove-PveVm |
Delete a VM |
Start-PveVm |
Start a VM |
Stop-PveVm |
Stop a VM (hard) |
Restart-PveVm |
Graceful restart (shutdown + start) |
Suspend-PveVm |
Suspend a VM |
Resume-PveVm |
Resume a suspended VM |
Reset-PveVm |
Hard reset a VM |
Copy-PveVm |
Clone a VM (full or linked) |
Move-PveVm |
Migrate a VM to another node |
Get-PveVmConfig |
Get VM configuration |
Set-PveVmConfig |
Modify VM configuration |
Resize-PveVmDisk |
Resize a VM disk |
Import-PveVmDisk |
Import a disk image (qcow2, raw, vmdk, OVA) into a VM |
Import-PveOva |
Import an OVA appliance as a new VM (parses OVF, uploads, creates VM, imports disks) |
Containers
| Cmdlet | Description |
|---|---|
Get-PveContainer |
List LXC containers |
New-PveContainer |
Create a new container |
Remove-PveContainer |
Delete a container |
Start-PveContainer |
Start a container |
Stop-PveContainer |
Stop a container |
Restart-PveContainer |
Restart a container |
Copy-PveContainer |
Clone a container |
Move-PveContainer |
Migrate a container to another node |
Get-PveContainerConfig |
Get container configuration |
Set-PveContainerConfig |
Modify container configuration |
Get-PveContainerSnapshot |
List container snapshots |
New-PveContainerSnapshot |
Create a container snapshot |
Remove-PveContainerSnapshot |
Delete a container snapshot |
Restore-PveContainerSnapshot |
Rollback to a container snapshot |
Storage
| Cmdlet | Description |
|---|---|
Get-PveStorage |
List storage pools |
Get-PveStorageContent |
List storage content (ISOs, images, etc.) |
Send-PveFile |
Upload a file (ISO, disk image, template) to storage |
Invoke-PveStorageDownload |
Download a URL to storage (server-side) |
New-PveStorage |
Create a storage pool |
Set-PveStorage |
Update a storage pool configuration |
Remove-PveStorage |
Remove a storage pool |
Snapshots
| Cmdlet | Description |
|---|---|
Get-PveSnapshot |
List VM snapshots |
New-PveSnapshot |
Create a snapshot |
Remove-PveSnapshot |
Delete a snapshot |
Restore-PveSnapshot |
Rollback to a snapshot |
Network
| Cmdlet | Description |
|---|---|
Get-PveNetwork |
List network interfaces |
New-PveNetwork |
Create a network interface |
Set-PveNetwork |
Modify a network interface |
Remove-PveNetwork |
Delete a network interface |
Invoke-PveNetworkApply |
Apply pending network changes |
SDN (PVE 8.0+)
| Cmdlet | Description |
|---|---|
Get-PveSdnZone |
List SDN zones |
New-PveSdnZone |
Create an SDN zone |
Remove-PveSdnZone |
Delete an SDN zone |
Get-PveSdnVnet |
List SDN VNets |
New-PveSdnVnet |
Create an SDN VNet |
Remove-PveSdnVnet |
Delete an SDN VNet |
Get-PveSdnSubnet |
List SDN subnets for a VNet |
New-PveSdnSubnet |
Create an SDN subnet |
Remove-PveSdnSubnet |
Delete an SDN subnet |
Users & Permissions
| Cmdlet | Description |
|---|---|
Get-PveUser |
List users |
New-PveUser |
Create a user |
Remove-PveUser |
Delete a user |
Set-PveUser |
Modify a user |
Get-PveRole |
List roles |
New-PveRole |
Create a role |
Remove-PveRole |
Delete a role |
Get-PvePermission |
List permissions |
Set-PvePermission |
Set a permission |
Get-PveApiToken |
List API tokens for a user |
New-PveApiToken |
Create an API token |
Remove-PveApiToken |
Delete an API token |
Templates
| Cmdlet | Description |
|---|---|
Get-PveTemplate |
List VM templates |
New-PveTemplate |
Convert a VM to a template |
Remove-PveTemplate |
Delete a template |
New-PveVmFromTemplate |
Create a VM from a template |
Cloud-Init
| Cmdlet | Description |
|---|---|
Get-PveCloudInitConfig |
Get cloud-init configuration |
Set-PveCloudInitConfig |
Set cloud-init configuration |
Invoke-PveCloudInitRegenerate |
Regenerate cloud-init image |
Tasks
| Cmdlet | Description |
|---|---|
Get-PveTask |
Get task status |
Get-PveTaskList |
List tasks on a node with optional filters |
Stop-PveTask |
Cancel a running task |
Wait-PveTask |
Wait for a task to complete |
Firewall
| Cmdlet | Description |
|---|---|
Get-PveFirewallRule |
List firewall rules (cluster/node/VM/container) |
New-PveFirewallRule |
Create a firewall rule |
Set-PveFirewallRule |
Update a firewall rule |
Remove-PveFirewallRule |
Delete a firewall rule |
Get-PveFirewallGroup |
List security groups or group rules |
New-PveFirewallGroup |
Create a security group |
Remove-PveFirewallGroup |
Delete a security group |
Get-PveFirewallAlias |
List firewall IP aliases |
New-PveFirewallAlias |
Create a firewall IP alias |
Set-PveFirewallAlias |
Update a firewall IP alias |
Remove-PveFirewallAlias |
Delete a firewall IP alias |
Get-PveFirewallIpSet |
List firewall IP sets |
New-PveFirewallIpSet |
Create a firewall IP set |
Remove-PveFirewallIpSet |
Delete a firewall IP set |
Get-PveFirewallIpSetEntry |
List entries in an IP set |
New-PveFirewallIpSetEntry |
Add an entry to an IP set |
Set-PveFirewallIpSetEntry |
Update an IP set entry |
Remove-PveFirewallIpSetEntry |
Remove an entry from an IP set |
Get-PveFirewallOptions |
Get firewall options |
Set-PveFirewallOptions |
Set firewall options |
Get-PveFirewallRef |
List firewall references (aliases, IP sets) |
Backup
| Cmdlet | Description |
|---|---|
New-PveBackup |
Create an ad-hoc backup (vzdump) |
Get-PveBackupJob |
List scheduled backup jobs |
New-PveBackupJob |
Create a scheduled backup job |
Set-PveBackupJob |
Update a scheduled backup job |
Remove-PveBackupJob |
Delete a scheduled backup job |
Get-PveBackupInfo |
Find VMs/containers not covered by backup jobs |
SDN — IPAM / DNS / Controllers (PVE 8.0+)
| Cmdlet | Description |
|---|---|
Get-PveSdnIpam |
List SDN IPAM plugins |
New-PveSdnIpam |
Create an SDN IPAM plugin |
Set-PveSdnIpam |
Update an SDN IPAM plugin |
Remove-PveSdnIpam |
Remove an SDN IPAM plugin |
Get-PveSdnDns |
List SDN DNS plugins |
New-PveSdnDns |
Create an SDN DNS plugin |
Set-PveSdnDns |
Update an SDN DNS plugin |
Remove-PveSdnDns |
Remove an SDN DNS plugin |
Get-PveSdnController |
List SDN controllers |
New-PveSdnController |
Create an SDN controller |
Set-PveSdnController |
Update an SDN controller |
Remove-PveSdnController |
Remove an SDN controller |
Invoke-PveSdnApply |
Apply pending SDN configuration changes |
Set-PveSdnZone |
Update an SDN zone |
Set-PveSdnVnet |
Update an SDN VNet |
Set-PveSdnSubnet |
Update an SDN subnet |
Cluster
| Cmdlet | Description |
|---|---|
Get-PveClusterResource |
List all resources (VMs, containers, nodes, storage) cluster-wide |
Pools
| Cmdlet | Description |
|---|---|
Get-PvePool |
List resource pools |
New-PvePool |
Create a resource pool |
Set-PvePool |
Update a resource pool |
Remove-PvePool |
Delete a resource pool |
VM Disk Operations
| Cmdlet | Description |
|---|---|
Move-PveVmDisk |
Move a VM disk to a different storage |
Remove-PveVmDisk |
Detach and optionally delete a VM disk |
Guest Agent Extensions
| Cmdlet | Description |
|---|---|
Get-PveVmGuestOsInfo |
Get guest OS information via QEMU agent |
Get-PveVmGuestFsInfo |
Get guest filesystem information |
Read-PveVmGuestFile |
Read a file from inside a guest VM |
Write-PveVmGuestFile |
Write a file inside a guest VM |
Set-PveVmGuestPassword |
Change a user password inside a guest VM |
Invoke-PveVmGuestFsTrim |
TRIM guest VM filesystems |
Additional Container Operations
| Cmdlet | Description |
|---|---|
Suspend-PveContainer |
Suspend (freeze) a container |
Resume-PveContainer |
Resume a suspended container |
Resize-PveContainerDisk |
Resize a container disk/volume |
New-PveContainerTemplate |
Convert a container to a template |
Move-PveContainerVolume |
Move a container volume to a different storage |
Get-PveContainerInterface |
Get container network interfaces |
Storage Content
| Cmdlet | Description |
|---|---|
Get-PveStorageStatus |
Get storage usage statistics |
Remove-PveStorageContent |
Delete a volume, backup, or ISO from storage |
Set-PveStorageContent |
Update volume notes/properties |
New-PveStorageDisk |
Allocate a new empty disk image |
Node Operations
| Cmdlet | Description |
|---|---|
Get-PveNodeConfig |
Get node configuration |
Set-PveNodeConfig |
Update node configuration |
Get-PveNodeDns |
Get node DNS configuration |
Set-PveNodeDns |
Update node DNS configuration |
Start-PveNodeVms |
Start all VMs on a node |
Stop-PveNodeVms |
Stop all VMs on a node |
Access — Groups & Domains
| Cmdlet | Description |
|---|---|
Get-PveGroup |
List user groups |
New-PveGroup |
Create a user group |
Set-PveGroup |
Update a user group |
Remove-PveGroup |
Delete a user group |
Get-PveDomain |
List authentication realms (PAM, LDAP, AD, OpenID) |
New-PveDomain |
Create an authentication realm |
Set-PveDomain |
Update an authentication realm |
Remove-PveDomain |
Delete an authentication realm |
Set-PvePassword |
Change a user's password |
Set-PveApiToken |
Update an API token |
Set-PveRole |
Update a role's privileges |
Known Limitations
- No automatic retries: Failed API calls are not retried. Implement your own retry logic if needed.
- Integration tests require live node: Integration tests require a dedicated Proxmox VE test node. See
tests/PSProxmoxVE.Tests/Integration/README.md. - No Ceph management: Ceph pool/OSD/monitor management is not included in v1.
- No PBS integration: Proxmox Backup Server operations are not included in v1.
- Task waiting:
Wait-PveTaskpolls with a minimum 1-second interval. For high-frequency monitoring, use the PVE web UI.
Contributing
- Clone the repository
- Open
PSProxmoxVE.slnin your IDE - Build:
dotnet build - Run unit tests — import the local build first, or an installed copy of the module shadows
it and the suite reports failures against correct code:
pwsh -Command "Import-Module ./src/PSProxmoxVE/bin/Debug/netstandard2.0/PSProxmoxVE.psd1 -Force; Invoke-Pester tests/PSProxmoxVE.Tests/ -ExcludeTagFilter Integration" - Run integration tests (provisions nested PVE, x86 only): see
CLAUDE.md, "Local dev environment"
Commit Convention
This project uses Conventional Commits:
feat:— new featurefix:— bug fixtest:— test additions or changesci:— CI/CD changesdocs:— documentation changesrefactor:— code refactoring