PSProxmoxVE Module Review Report
Reviewer: Claude Code (Opus 4.6)
Module Version: 0.1.0-preview
Repository: PSProxmoxVE (C# binary PowerShell module)
Executive Summary
Delta since last scan: 22 fixed | 2 new findings | 0 regressed | 4 still open
- Significant progress since prior scan — 22 of 28 findings from the initial report have been resolved, including all Critical items.
- 83 cmdlets (up from 66) covering VMs, containers (with snapshots and migration), storage, networking, SDN (zones, VNets, subnets), users/roles/permissions, templates, cloud-init, OVA/disk import, and tasks.
- All prior Critical items resolved: publish workflow added, README updated with
Install-Module, ReleaseNotes in manifest.
- Community files complete: CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, .gitattributes, issue/PR templates all added.
- Code quality improvements: HelpMessage on all parameters, WriteVerbose throughout, ValidateRange on VmId, ConfirmImpact.High on Stop/Reset-PveVm, Reset verb constant fixed.
- MAML help file present (PSProxmoxVE.dll-Help.xml) with 75 cmdlet documentation files in docs/cmdlets/.
- Remaining gaps: No IconUri in manifest, Remove-PveRole missing ConfirmImpact.High, firewall/backup/pool cmdlets not yet implemented.
- Excellent test coverage: 100% Pester unit tests, 84% integration test coverage, xUnit model tests with PVE 8 & 9 fixtures.
- Security posture is clean: No hardcoded secrets, HTTPS enforced, SkipCertificateCheck emits warning, all dependencies pinned.
- Module is near PSGallery-ready — no blocking issues remain.
Phase 1 — Repository Inventory & Structure
Directory Tree (depth ≤ 4)
Project Layout
| Item |
Value |
| Solution |
PSProxmoxVE.sln |
| Main project |
src/PSProxmoxVE/PSProxmoxVE.csproj |
| Core library |
src/PSProxmoxVE.Core/PSProxmoxVE.Core.csproj |
| Test project (xUnit) |
tests/PSProxmoxVE.Core.Tests/PSProxmoxVE.Core.Tests.csproj |
| Test project (Pester) |
tests/PSProxmoxVE.Tests/ |
| Target Frameworks |
netstandard2.0, net9.0, net48 |
| Language Version |
C# 10.0 |
| Nullable |
Enabled |
| Assembly Names |
PSProxmoxVE, PSProxmoxVE.Core |
CI/CD Workflows
| Workflow |
Purpose |
build.yml |
Build + xUnit tests on net48 (Windows) and net9.0 (Windows + Ubuntu) |
unit-tests.yml |
Build + Pester tests across PS 5.1/7.5 on Windows/Ubuntu/macOS |
integration-tests.yml |
Full integration tests against nested PVE 8 + PVE 9 instances |
publish.yml |
Tag-triggered publish to PSGallery + GitHub Release creation |
Inventory Checklist
| Item |
Present |
Prior Status |
Solution file (.sln) |
Yes |
Open |
Module manifest (.psd1) |
Yes |
Open |
Format definitions (.format.ps1xml) |
Yes |
Open |
MAML help (.dll-Help.xml) |
Yes |
New |
.editorconfig |
Yes |
Open |
.gitignore |
Yes |
Open |
.gitattributes |
Yes |
Fixed |
LICENSE (MIT) |
Yes |
Open |
CHANGELOG.md |
Yes |
Open |
README.md |
Yes |
Open |
CONTRIBUTING.md |
Yes |
Fixed |
CODE_OF_CONDUCT.md |
Yes |
Fixed |
SECURITY.md |
Yes |
Fixed |
.github/ISSUE_TEMPLATE/ |
Yes |
Fixed |
.github/pull_request_template.md |
Yes |
Fixed |
| PSGallery publish workflow |
Yes |
Fixed |
docs/PVE_API_COVERAGE.md |
Yes |
New |
docs/cmdlets/ (75 files) |
Yes |
New |
What's Missing
Phase 2 — PVE API Coverage Audit
Note: Coverage assessment based on source code analysis and the project's own docs/PVE_API_COVERAGE.md.
Implemented Cmdlets by Category (83 total)
Connection (3 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Connect-PveServer |
/access/ticket |
POST |
Open |
Disconnect-PveServer |
/access/ticket |
DELETE |
Open |
Test-PveConnection |
/version |
GET |
Open |
Nodes (2 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveNode |
/nodes |
GET |
Open |
Get-PveNodeStatus |
/nodes/{node}/status |
GET |
Open |
VMs / QEMU (19 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveVm |
/nodes/{node}/qemu |
GET |
Open |
New-PveVm |
/nodes/{node}/qemu |
POST |
Open |
Remove-PveVm |
/nodes/{node}/qemu/{vmid} |
DELETE |
Open |
Start-PveVm |
/nodes/{node}/qemu/{vmid}/status/start |
POST |
Open |
Stop-PveVm |
/nodes/{node}/qemu/{vmid}/status/stop |
POST |
Open |
Restart-PveVm |
/nodes/{node}/qemu/{vmid}/status/reboot |
POST |
Open |
Suspend-PveVm |
/nodes/{node}/qemu/{vmid}/status/suspend |
POST |
Open |
Resume-PveVm |
/nodes/{node}/qemu/{vmid}/status/resume |
POST |
Open |
Reset-PveVm |
/nodes/{node}/qemu/{vmid}/status/reset |
POST |
Open |
Copy-PveVm |
/nodes/{node}/qemu/{vmid}/clone |
POST |
Open |
Move-PveVm |
/nodes/{node}/qemu/{vmid}/migrate |
POST |
Open |
Get-PveVmConfig |
/nodes/{node}/qemu/{vmid}/config |
GET |
Open |
Set-PveVmConfig |
/nodes/{node}/qemu/{vmid}/config |
PUT |
Open |
Resize-PveVmDisk |
/nodes/{node}/qemu/{vmid}/resize |
PUT |
Open |
Import-PveVmDisk |
/nodes/{node}/storage/{storage}/upload |
POST |
New |
Import-PveOva |
Multiple (upload + config + import) |
POST |
New |
Test-PveVmGuestAgent |
/nodes/{node}/qemu/{vmid}/agent/ping |
POST |
Open |
Get-PveVmGuestNetwork |
/nodes/{node}/qemu/{vmid}/agent/network-get-interfaces |
GET |
Open |
Invoke-PveVmGuestExec |
/nodes/{node}/qemu/{vmid}/agent/exec |
POST |
Open |
Containers / LXC (10 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveContainer |
/nodes/{node}/lxc |
GET |
Open |
New-PveContainer |
/nodes/{node}/lxc |
POST |
Open |
Remove-PveContainer |
/nodes/{node}/lxc/{vmid} |
DELETE |
Open |
Start-PveContainer |
/nodes/{node}/lxc/{vmid}/status/start |
POST |
Open |
Stop-PveContainer |
/nodes/{node}/lxc/{vmid}/status/stop |
POST |
Open |
Restart-PveContainer |
/nodes/{node}/lxc/{vmid}/status/shutdown |
POST |
Open |
Copy-PveContainer |
/nodes/{node}/lxc/{vmid}/clone |
POST |
Open |
Move-PveContainer |
/nodes/{node}/lxc/{vmid}/migrate |
POST |
New |
Get-PveContainerConfig |
/nodes/{node}/lxc/{vmid}/config |
GET |
Open |
Set-PveContainerConfig |
/nodes/{node}/lxc/{vmid}/config |
PUT |
Open |
Container Snapshots (4 cmdlets — NEW)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveContainerSnapshot |
/nodes/{node}/lxc/{vmid}/snapshot |
GET |
New |
New-PveContainerSnapshot |
/nodes/{node}/lxc/{vmid}/snapshot |
POST |
New |
Remove-PveContainerSnapshot |
/nodes/{node}/lxc/{vmid}/snapshot/{name} |
DELETE |
New |
Restore-PveContainerSnapshot |
/nodes/{node}/lxc/{vmid}/snapshot/{name}/rollback |
POST |
New |
Storage (6 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveStorage |
/storage or /nodes/{node}/storage |
GET |
Open |
Get-PveStorageContent |
/nodes/{node}/storage/{storage}/content |
GET |
Open |
Send-PveFile |
/nodes/{node}/storage/{storage}/upload |
POST |
Open |
Invoke-PveStorageDownload |
/nodes/{node}/storage/{storage}/download-url |
POST |
Open |
New-PveStorage |
/storage |
POST |
Open |
Remove-PveStorage |
/storage/{storage} |
DELETE |
Open |
VM Snapshots (4 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveSnapshot |
/nodes/{node}/qemu/{vmid}/snapshot |
GET |
Open |
New-PveSnapshot |
/nodes/{node}/qemu/{vmid}/snapshot |
POST |
Open |
Remove-PveSnapshot |
/nodes/{node}/qemu/{vmid}/snapshot/{name} |
DELETE |
Open |
Restore-PveSnapshot |
/nodes/{node}/qemu/{vmid}/snapshot/{name}/rollback |
POST |
Open |
Networking (5 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveNetwork |
/nodes/{node}/network |
GET |
Open |
New-PveNetwork |
/nodes/{node}/network |
POST |
Open |
Set-PveNetwork |
/nodes/{node}/network/{iface} |
PUT |
Open |
Remove-PveNetwork |
/nodes/{node}/network/{iface} |
DELETE |
Open |
Invoke-PveNetworkApply |
/nodes/{node}/network |
PUT |
Open |
SDN (9 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveSdnZone |
/cluster/sdn/zones |
GET |
Open |
New-PveSdnZone |
/cluster/sdn/zones |
POST |
Open |
Remove-PveSdnZone |
/cluster/sdn/zones/{zone} |
DELETE |
Open |
Get-PveSdnVnet |
/cluster/sdn/vnets |
GET |
Open |
New-PveSdnVnet |
/cluster/sdn/vnets |
POST |
Open |
Remove-PveSdnVnet |
/cluster/sdn/vnets/{vnet} |
DELETE |
Open |
Get-PveSdnSubnet |
/cluster/sdn/vnets/{vnet}/subnets |
GET |
New |
New-PveSdnSubnet |
/cluster/sdn/vnets/{vnet}/subnets |
POST |
New |
Remove-PveSdnSubnet |
/cluster/sdn/vnets/{vnet}/subnets/{subnet} |
DELETE |
New |
Users / ACLs / Tokens (12 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveUser |
/access/users |
GET |
Open |
New-PveUser |
/access/users |
POST |
Open |
Remove-PveUser |
/access/users/{userid} |
DELETE |
Open |
Set-PveUser |
/access/users/{userid} |
PUT |
Open |
Get-PveRole |
/access/roles |
GET |
Open |
New-PveRole |
/access/roles |
POST |
Open |
Remove-PveRole |
/access/roles/{roleid} |
DELETE |
Open |
Get-PvePermission |
/access/acl |
GET |
Open |
Set-PvePermission |
/access/acl |
PUT |
Open |
Get-PveApiToken |
/access/users/{userid}/token |
GET |
Open |
New-PveApiToken |
/access/users/{userid}/token/{tokenid} |
POST |
Open |
Remove-PveApiToken |
/access/users/{userid}/token/{tokenid} |
DELETE |
Open |
Templates (4 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveTemplate |
/nodes/{node}/qemu (filtered) |
GET |
Open |
New-PveTemplate |
/nodes/{node}/qemu/{vmid}/template |
POST |
Open |
Remove-PveTemplate |
/nodes/{node}/qemu/{vmid} |
DELETE |
Open |
New-PveVmFromTemplate |
/nodes/{node}/qemu/{vmid}/clone |
POST |
Open |
Cloud-Init (3 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveCloudInitConfig |
/nodes/{node}/qemu/{vmid}/config |
GET |
Open |
Set-PveCloudInitConfig |
/nodes/{node}/qemu/{vmid}/config |
PUT |
Open |
Invoke-PveCloudInitRegenerate |
/nodes/{node}/qemu/{vmid}/cloudinit |
PUT |
Open |
Tasks (2 cmdlets)
| Cmdlet |
API Endpoint |
Method |
Prior Status |
Get-PveTask |
/nodes/{node}/tasks/{upid}/status |
GET |
Open |
Wait-PveTask |
/nodes/{node}/tasks/{upid}/status (polling) |
GET |
Open |
Missing API Coverage by Functional Area
| Area |
Key Missing Endpoints |
Priority |
Prior Status |
| Firewall |
/cluster/firewall/*, /nodes/{node}/firewall/*, /nodes/{node}/qemu/{vmid}/firewall/* |
High |
Open |
| Backup / vzdump |
/nodes/{node}/vzdump, /cluster/backup/* |
High |
Open |
| Pools |
/pools, /pools/{poolid} (CRUD) |
Medium |
Open |
| HA |
/cluster/ha/* (groups, resources, status) |
Medium |
Open |
| Ceph |
/nodes/{node}/ceph/* (OSD, pool, mon, fs) |
Medium |
Open |
| Replication |
/cluster/replication (CRUD) |
Low |
Open |
| Access Groups |
/access/groups (CRUD) |
Low |
Open |
| Access Domains |
/access/domains (CRUD) |
Low |
Open |
| PBS Integration |
Proxmox Backup Server operations |
Low |
Open |
| Cluster Config |
/cluster/config/* (join, nodes, totem) |
Low |
Open |
| ACME |
/cluster/acme/*, /nodes/{node}/certificates/* |
Low |
Open |
| Node Management |
/nodes/{node}/apt/*, /nodes/{node}/disks/*, /nodes/{node}/services/* |
Low |
Open |
| Metrics |
/cluster/metrics/* |
Low |
Open |
| SDN IPAM/DNS/Controllers |
/cluster/sdn/ipams/*, /cluster/sdn/dns/*, /cluster/sdn/controllers/* |
Low |
New |
| VM Agent (extended) |
Additional agent endpoints (file-read, file-write, os-info) |
Low |
Open |
High-Value Gaps
- Firewall management — Critical for security automation. No cmdlets for creating/managing firewall rules at cluster, node, or VM level.
- Backup/restore (vzdump) — Essential for DR automation. No cmdlets for creating backups, managing schedules, or restoring.
- Pool management — Required for multi-tenant environments. No cmdlets for creating/managing resource pools.
Phase 3 — Code Quality & Best Practices
3a. PowerShell Module Design
Cmdlet Naming
- All cmdlets use approved PowerShell verbs via verb class constants (
VerbsCommon.Get, VerbsLifecycle.Start, etc.).
- Noun prefix
Pve is consistent across all 83 cmdlets.
- Previous finding about
Reset-PveVm using a string literal has been fixed — now uses VerbsCommon.Reset.
Parameter Design
[Parameter(Mandatory = ...)] used appropriately throughout.
ValueFromPipelineByPropertyName = true used on Node, VmId, and similar parameters.
Position attributes used on key parameters.
ValidateSet, ValidateRange(100, 999999999), ValidateNotNullOrEmpty, ValidatePattern used where appropriate.
- HelpMessage present on virtually all parameters (previously missing).
ShouldProcess / WhatIf / Confirm
- Every destructive/mutating cmdlet implements
SupportsShouldProcess = true.
ConfirmImpact = ConfirmImpact.High on all Remove-* cmdlets, Stop-PveVm, Reset-PveVm, New-PveTemplate, Restore-PveSnapshot, Restore-PveContainerSnapshot.
- Gap:
Remove-PveRole does not set ConfirmImpact.High — deleting a role is a significant operation.
OutputType
[OutputType] attributes present on all 83 cmdlets.
Pipeline Support
- All cmdlets use
ProcessRecord for pipeline processing.
Get-PveVm, Get-PveContainer output objects with Node and VmId enabling chaining.
Error Handling
ThrowTerminatingError used appropriately for fatal conditions.
WriteWarning used for non-fatal conditions.
- Custom exception hierarchy provides structured errors.
3b. C# Code Quality
Null-Reference Safety
<Nullable>enable</Nullable> in both .csproj files.
- Consistent use of nullable annotations, null guards (
?? throw), and null-coalescing patterns.
- No null-forgiving operators (
!) used.
Async/Await Pattern
- All HTTP operations are async in
PveHttpClient with .ConfigureAwait(false).
- Sync wrappers use
.GetAwaiter().GetResult() — standard for PowerShell binary modules.
- Low deadlock risk given the single pipeline thread model.
IDisposable Correctness
PveHttpClient implements IDisposable correctly with _disposed flag.
- All service methods use
using var client = new PveHttpClient(session).
- File upload uses framework-conditional async disposal.
Exception Handling
- Well-defined exception hierarchy (7 custom types) with context properties.
PveHttpClient.SendAsync wraps HttpRequestException in PveApiException.
- Silent catches are intentional and documented (node polling, status enrichment).
Magic Strings
- Auth header names inline (minor, not problematic).
- API resource paths constructed inline (pragmatic for codebase size).
RandomNumberGenerator used for boundary generation (previously new Random()).
Logging / Verbose Output
WriteVerbose used extensively throughout cmdlets (previously missing).
WriteProgress used in Send-PveFile, Import-PveOva, and Wait-PveTask.
3c. General Hygiene
- No unused
using directives detected.
- No commented-out blocks or dead code.
- Zero TODO/FIXME/HACK markers.
- Consistent code style matching
.editorconfig.
- XML doc comments on all public types and cmdlet classes.
CS1591 no longer suppressed in Core project.
Code Quality Findings
| ID |
File |
Line |
Severity |
Description |
Prior Status |
| CQ1 |
RemovePveRoleCmdlet.cs |
13 |
Medium |
Missing ConfirmImpact.High — deleting roles is significant |
New |
| CQ2 |
PveHttpClient.cs |
316-323 |
Low |
Auth header names (PVEAPIToken=, CSRFPreventionToken) could be constants |
Open |
Phase 4 — Testing Coverage Analysis
Test Projects
| Project |
Framework |
Type |
Runner |
PSProxmoxVE.Core.Tests |
xUnit 2.7 + Moq 4.20 |
Unit tests (models, auth) |
dotnet test |
PSProxmoxVE.Tests |
Pester 5 |
Cmdlet + Integration tests |
Invoke-Pester |
xUnit Tests (Core Library)
| Test Area |
Test File |
~Methods |
| Session lifecycle |
PveSessionTests.cs |
9 |
| Authentication logic |
PveAuthenticatorTests.cs |
7 |
| Version parsing |
PveVersionTests.cs |
11 |
| Cluster models |
ClusterModelTests.cs |
13 |
| Container models |
ContainerModelTests.cs |
13 |
| Network models |
NetworkModelTests.cs |
12 |
| Node models |
NodeModelTests.cs |
13 |
| SDN models |
SdnModelTests.cs |
17 |
| Snapshot models |
SnapshotModelTests.cs |
9 |
| Storage models |
StorageModelTests.cs |
19 |
| Task models |
TaskModelTests.cs |
9 |
| User models |
UserModelTests.cs |
24 |
| VM models |
VmModelTests.cs |
18 |
Total: ~174 xUnit tests with PVE 8 and PVE 9 JSON fixtures.
Pester Tests (30 files, ~500 tests)
All 83 cmdlets have Pester unit tests covering command metadata, parameter validation, ShouldProcess presence, and pipeline binding.
Integration Tests
The integration test file covers 18+ major contexts with 90+ individual tests:
- Connection, Nodes, User/Role/Token CRUD, Permissions
- VM lifecycle (create, config, start/stop, reset, clone, resize, suspend/resume)
- Snapshot CRUD (VM and container)
- Storage (list, content, upload, create/remove, download)
- Network CRUD with apply/revert
- SDN zones/VNets/subnets CRUD
- Templates, Cloud-Init
- Container lifecycle (create, config, start/stop/restart, clone, snapshots)
- Linux VM provisioning (cloud image upload, disk import, cloud-init, guest agent)
Setup/teardown: AfterAll cleans up all created resources. Environment variables inject connection details. Tests skip gracefully when env vars are missing.
Test Coverage Gap List
| Cmdlet |
Unit Test |
Integration Test |
PVE 8 |
PVE 9 |
Prior Status |
Connect-PveServer |
✓ |
✓ |
✓ |
✓ |
Open |
Disconnect-PveServer |
✓ |
✓ |
✓ |
✓ |
Open |
Test-PveConnection |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PveNode |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PveNodeStatus |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PveVm |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveVm |
✓ |
✓ |
— |
✓ |
Open |
Remove-PveVm |
✓ |
✓ |
— |
✓ |
Open |
Start-PveVm |
✓ |
✓ |
✓ |
✓ |
Open |
Stop-PveVm |
✓ |
✓ |
✓ |
✓ |
Open |
Restart-PveVm |
✓ |
✓ |
✓ |
✓ |
Open |
Suspend-PveVm |
✓ |
✓ |
— |
✓ |
Fixed |
Resume-PveVm |
✓ |
✓ |
— |
✓ |
Fixed |
Reset-PveVm |
✓ |
✓ |
✓ |
✓ |
Open |
Copy-PveVm |
✓ |
✓ |
— |
✓ |
Open |
Move-PveVm |
✓ |
✗ |
— |
— |
Open |
Get-PveVmConfig |
✓ |
✓ |
— |
✓ |
Open |
Set-PveVmConfig |
✓ |
✓ |
— |
✓ |
Open |
Resize-PveVmDisk |
✓ |
✓ |
— |
✓ |
Fixed |
Import-PveVmDisk |
✓ |
✓ |
— |
✓ |
New |
Import-PveOva |
✓ |
✗ |
— |
— |
New |
Test-PveVmGuestAgent |
✓ |
✓ |
— |
✓ |
Open |
Get-PveVmGuestNetwork |
✓ |
✓ |
— |
✓ |
Open |
Invoke-PveVmGuestExec |
✓ |
✓ |
— |
✓ |
Open |
Get-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
New-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
Remove-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
Start-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
Stop-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
Restart-PveContainer |
✓ |
✓ |
— |
✓ |
Open |
Copy-PveContainer |
✓ |
✓ |
— |
✓ |
Fixed |
Move-PveContainer |
✓ |
✗ |
— |
— |
New |
Get-PveContainerConfig |
✓ |
✓ |
— |
✓ |
Open |
Set-PveContainerConfig |
✓ |
✓ |
— |
✓ |
Open |
Get-PveContainerSnapshot |
✓ |
✓ |
— |
✓ |
New |
New-PveContainerSnapshot |
✓ |
✓ |
— |
✓ |
New |
Remove-PveContainerSnapshot |
✓ |
✓ |
— |
✓ |
New |
Restore-PveContainerSnapshot |
✓ |
✓ |
— |
✓ |
New |
Get-PveStorage |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveStorage |
✓ |
✓ |
— |
✓ |
Fixed |
Remove-PveStorage |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveStorageContent |
✓ |
✓ |
✓ |
✓ |
Open |
Send-PveFile |
✓ |
✓ |
— |
✓ |
Open |
Invoke-PveStorageDownload |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveSnapshot |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveSnapshot |
✓ |
✓ |
✓ |
✓ |
Open |
Remove-PveSnapshot |
✓ |
✓ |
✓ |
✓ |
Open |
Restore-PveSnapshot |
✓ |
✗ |
— |
— |
Open |
Get-PveNetwork |
✓ |
✓ |
— |
✓ |
Open |
New-PveNetwork |
✓ |
✓ |
— |
✓ |
Fixed |
Set-PveNetwork |
✓ |
✓ |
— |
✓ |
Fixed |
Remove-PveNetwork |
✓ |
✓ |
— |
✓ |
Fixed |
Invoke-PveNetworkApply |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveSdnZone |
✓ |
✓ |
— |
✓ |
Open |
New-PveSdnZone |
✓ |
✓ |
— |
✓ |
Fixed |
Remove-PveSdnZone |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveSdnVnet |
✓ |
✓ |
— |
✓ |
Open |
New-PveSdnVnet |
✓ |
✓ |
— |
✓ |
Fixed |
Remove-PveSdnVnet |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveSdnSubnet |
✓ |
✓ |
— |
✓ |
New |
New-PveSdnSubnet |
✓ |
✓ |
— |
✓ |
New |
Remove-PveSdnSubnet |
✓ |
✓ |
— |
✓ |
New |
Get-PveUser |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveUser |
✓ |
✓ |
✓ |
✓ |
Open |
Set-PveUser |
✓ |
✓ |
✓ |
✓ |
Open |
Remove-PveUser |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PveRole |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveRole |
✓ |
✓ |
✓ |
✓ |
Open |
Remove-PveRole |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PveApiToken |
✓ |
✓ |
✓ |
✓ |
Open |
New-PveApiToken |
✓ |
✓ |
✓ |
✓ |
Open |
Remove-PveApiToken |
✓ |
✓ |
✓ |
✓ |
Open |
Get-PvePermission |
✓ |
✓ |
— |
✓ |
Open |
Set-PvePermission |
✓ |
✓ |
— |
✓ |
Open |
Get-PveTemplate |
✓ |
✓ |
— |
✓ |
Open |
New-PveTemplate |
✓ |
✗ |
— |
— |
Open |
Remove-PveTemplate |
✓ |
✗ |
— |
— |
Open |
New-PveVmFromTemplate |
✓ |
✗ |
— |
— |
Open |
Get-PveCloudInitConfig |
✓ |
✓ |
— |
✓ |
Open |
Set-PveCloudInitConfig |
✓ |
✓ |
— |
✓ |
Fixed |
Invoke-PveCloudInitRegenerate |
✓ |
✓ |
— |
✓ |
Fixed |
Get-PveTask |
✓ |
✓ |
✓ |
✓ |
Open |
Wait-PveTask |
✓ |
✓ |
— |
✓ |
Open |
Summary: 83/83 unit tested (100%), 70/83 integration tested (84%). Remaining gaps are for operations requiring multi-node clusters (Move-PveVm, Move-PveContainer), optional test assets (Import-PveOva), or template operations that would require extra setup.
Phase 5 — Security Review
5.1 Credential Handling
| Aspect |
Status |
Details |
Prior Status |
| PSCredential usage |
Good |
Connect-PveServer accepts PSCredential for password auth |
Open |
| Password extraction |
Acceptable |
NetworkCredential.Password used only at API call time |
Open |
| API token in memory |
Acceptable |
Stored as plain string in PveSession.ApiToken |
Open |
| Credential logging |
Good |
No WriteVerbose/WriteDebug calls include credentials |
Open |
| Ticket storage |
Good |
Tickets stored in PveSession only, not written to disk |
Open |
| Session expiry |
Good |
2-hour expiry detected via IsExpired property |
Open |
5.2 TLS/HTTPS
| Aspect |
Status |
Details |
Prior Status |
| HTTPS enforced |
Good |
BaseUrl always constructs https:// URLs |
Open |
| SkipCertificateCheck |
Good |
Explicit opt-in parameter on Connect-PveServer |
Open |
| Warning on skip |
Good |
WriteWarning emitted with man-in-the-middle advisory |
Fixed |
| TLS version |
Good |
Left to OS/runtime negotiation (no pinning) |
Open |
| Port validation |
Good |
ValidateRange(1, 65535) on port parameter |
Open |
5.3 Input Validation
| Aspect |
Status |
Details |
Prior Status |
| VMID validation |
Good |
[ValidateRange(100, 999999999)] on all VmId parameters |
Fixed |
| URL construction |
Good |
String interpolation with validated parameters |
Open |
| User ID encoding |
Good |
Uri.EscapeDataString() used for user/token IDs in URLs |
Open |
| API token format |
Good |
Regex validation in PveAuthenticator |
Open |
| Path parameters |
Low risk |
Node/snapshot names not URL-encoded but come from validated sources |
Open |
5.4 Dependency Security
| Package |
Version |
Pinned |
Notes |
Prior Status |
Newtonsoft.Json |
13.0.3 |
Yes |
Latest stable, no known CVEs |
Open |
System.Text.Json |
8.0.5 |
Yes |
Latest 8.x patch |
Open |
SharpCompress |
0.38.0 |
Yes |
OVA/tar extraction support |
New |
PowerShellStandard.Library |
5.1.1 |
Yes |
PrivateAssets=all |
Open |
System.Management.Automation |
7.4.0 |
Yes |
PrivateAssets=all |
Open |
All dependencies are pinned to exact versions.
5.5 Secret Scanning
No hardcoded credentials, tokens, or sensitive IPs found in source code. CI workflow uses GitHub Actions secrets with ::add-mask:: for test passwords.
Security Findings
| ID |
Area |
File |
Severity |
Description |
Prior Status |
| S1 |
Input validation |
SnapshotService.cs, ContainerService.cs |
Low |
Snapshot names and node names in URL paths are not URL-encoded — low risk as values come from validated/system sources |
New |
Phase 6 — PSGallery Publication Readiness
6.1 Module Manifest Assessment
| Field |
Status |
Value |
Prior Status |
RootModule |
Pass |
PSProxmoxVE.dll |
Open |
ModuleVersion |
Pass |
0.1.0 |
Open |
GUID |
Pass |
a3f7c2d1-84e5-4b9f-a061-3e2d8c5f1a7b |
Open |
Author |
Pass |
goodolclint |
Open |
CompanyName |
Pass |
Worklab |
Open |
Copyright |
Pass |
(c) 2026 goodolclint. All rights reserved. |
Open |
Description |
Pass |
Meaningful |
Open |
PowerShellVersion |
Pass |
5.1 |
Open |
CompatiblePSEditions |
Pass |
Desktop, Core |
Open |
DotNetFrameworkVersion |
Pass |
4.8 |
Open |
RequiredAssemblies |
Pass |
Lists Core DLL and Newtonsoft.Json |
Open |
FormatsToProcess |
Pass |
PSProxmoxVE.format.ps1xml |
Open |
CmdletsToExport |
Pass |
Explicit list of all cmdlets |
Open |
FunctionsToExport |
Pass |
Empty (binary module) |
Open |
AliasesToExport |
Pass |
7 aliases defined |
New |
Prerelease |
Pass |
preview |
Open |
Tags |
Pass |
8 relevant tags |
Open |
LicenseUri |
Pass |
GitHub LICENSE |
Open |
ProjectUri |
Pass |
GitHub repo |
Open |
HelpInfoURI |
Pass |
docs/cmdlets |
Fixed |
ReleaseNotes |
Pass |
Present in PSData |
Fixed |
IconUri |
Fail |
Not present |
Open |
6.2 License
| Check |
Pass/Fail |
Notes |
Prior Status |
| LICENSE file present |
Pass |
MIT License at repo root |
Open |
| OSI-approved license |
Pass |
MIT |
Open |
| LicenseUri in manifest |
Pass |
Points to GitHub LICENSE |
Open |
6.3 README Quality
| Check |
Pass/Fail |
Notes |
Prior Status |
| Installation instructions |
Pass |
Install-Module PSProxmoxVE |
Fixed |
| Quick-start examples |
Pass |
Multiple scenarios |
Open |
| Authentication guide |
Pass |
Ticket + API token documented |
Open |
| Badges |
Pass |
Build, Unit Tests, License badges |
Fixed |
| Cmdlet reference |
Pass |
All cmdlets listed |
Open |
| Known limitations |
Pass |
Documented |
Open |
| Contributing section |
Pass |
References CONTRIBUTING.md |
Open |
6.4 Build & Publish Pipeline
| Check |
Pass/Fail |
Notes |
Prior Status |
| Build workflow |
Pass |
net48 + net9.0, Windows + Ubuntu |
Open |
| Unit test workflow |
Pass |
PS 5.1/7.5, Windows/Ubuntu/macOS |
Open |
| Integration test workflow |
Pass |
PVE 8 + PVE 9 |
Open |
| Publish workflow |
Pass |
Tag-triggered, PSGallery + GitHub Release |
Fixed |
| API key handling |
Pass |
Via PSGALLERY_API_KEY secret |
Fixed |
6.5 Versioning
| Check |
Pass/Fail |
Notes |
Prior Status |
| CHANGELOG format |
Pass |
Keep a Changelog with versioned entry |
Open |
| Versioned release |
Pass |
0.1.0-preview entry present |
Fixed |
| Commit conventions |
Pass |
Conventional Commits used consistently |
Open |
PSGallery Readiness Checklist
| Check |
Pass/Fail |
Notes |
Prior Status |
| Module manifest complete |
Pass |
All required fields populated |
Open |
| License present and OSI-approved |
Pass |
MIT |
Open |
| README with install instructions |
Pass |
PSGallery install documented |
Fixed |
| Publish pipeline configured |
Pass |
publish.yml on tag push |
Fixed |
| ReleaseNotes present |
Pass |
In PSData section |
Fixed |
| IconUri |
Fail |
Missing — cosmetic only |
Open |
| MAML help file |
Pass |
PSProxmoxVE.dll-Help.xml present |
New |
Phase 7 — Community & Repo Maintenance Standards
| Check |
Pass/Fail |
Notes |
Prior Status |
README.md |
Pass |
Comprehensive with badges |
Open |
LICENSE |
Pass |
MIT |
Open |
CHANGELOG.md |
Pass |
Keep a Changelog format with versioned entry |
Open |
CONTRIBUTING.md |
Pass |
Dev setup, coding standards, test instructions, PR process |
Fixed |
CODE_OF_CONDUCT.md |
Pass |
Contributor Covenant v2.1 |
Fixed |
SECURITY.md |
Pass |
Vulnerability disclosure policy, 48h response SLA |
Fixed |
.editorconfig |
Pass |
Comprehensive rules |
Open |
.gitignore |
Pass |
Build outputs, IDE files, test results |
Open |
.gitattributes |
Pass |
Line ending normalization, binary detection |
Fixed |
| Issue templates |
Pass |
Bug report + feature request (YAML format) |
Fixed |
| PR template |
Pass |
Summary, type checkboxes, checklist |
Fixed |
| Conventional commits |
Pass |
Consistent in recent history |
Open |
Default branch main |
Pass |
Confirmed |
Open |
| API coverage documentation |
Pass |
docs/PVE_API_COVERAGE.md |
New |
| Cmdlet documentation |
Pass |
75 files in docs/cmdlets/ |
New |
Recommended Branch Protection (cannot verify via API)
- Require PR reviews before merge
- Require status checks (build + unit tests + integration tests)
- Require linear history or squash merging
Phase 8 — Prioritized Recommendations
🔴 Critical (blocks PSGallery publication or is a security risk)
None — all prior Critical items have been resolved.
| # |
What |
Where |
Why |
Fix |
Prior Status |
| H1 |
No firewall cmdlets |
Module |
High-value gap for security automation — most requested feature area |
Implement firewall rule CRUD for cluster/node/VM |
Open |
| H2 |
No backup/vzdump cmdlets |
Module |
Essential for DR automation scenarios |
Implement vzdump and backup schedule management |
Open |
🟡 Medium (best practice gaps, test coverage holes)
| # |
What |
Where |
Why |
Fix |
Prior Status |
| M1 |
Remove-PveRole missing ConfirmImpact.High |
RemovePveRoleCmdlet.cs:13 |
Deleting roles is a significant, potentially disruptive operation |
Add ConfirmImpact = ConfirmImpact.High |
New |
| M2 |
No pool management |
Module |
Useful for multi-tenant environments |
Implement pool CRUD |
Open |
| M3 |
Move-PveVm / Move-PveContainer lack integration tests |
Integration tests |
Migration is untested end-to-end (requires multi-node) |
Add integration tests when multi-node CI is available |
Open |
| M4 |
Import-PveOva lacks integration test |
Integration tests |
OVA import only tested at parameter level |
Add to integration tests (requires OVA test asset) |
New |
| M5 |
URL-encode snapshot/node names in API paths |
SnapshotService.cs, ContainerService.cs |
Defense-in-depth against path injection (low risk) |
Apply Uri.EscapeDataString() to path parameters |
New |
🟢 Low / Nice-to-have (polish, discoverability, stretch goals)
| # |
What |
Where |
Why |
Fix |
Prior Status |
| L1 |
No IconUri in manifest |
PSProxmoxVE.psd1 |
Improves PSGallery listing appearance |
Create module icon, host it, add URI |
Open |
| L2 |
Auth header magic strings |
PveHttpClient.cs:316-323 |
Minor code quality — could be named constants |
Extract to const string fields |
Open |
| L3 |
HA/Ceph/Replication cmdlets |
Module |
Lower-priority API coverage gaps |
Implement as demand grows |
Open |
| L4 |
Access groups/domains cmdlets |
Module |
Useful for LDAP/AD integration |
Implement /access/groups, /access/domains CRUD |
Open |
| L5 |
PSGallery version badge |
README.md |
Shows published version to visitors |
Add PSGallery badge after first publish |
New |
| L6 |
SDN IPAM/DNS/Controller cmdlets |
Module |
Complete SDN management surface |
Implement as demand grows |
New |
Summary Statistics
| Metric |
This Scan |
Prior Scan |
Delta |
| Total cmdlets |
83 |
66 |
+17 |
| Cmdlets with ShouldProcess |
All mutating |
All mutating |
— |
| Cmdlets with OutputType |
83 (100%) |
66 (100%) |
— |
| Cmdlets with HelpMessage |
83 (100%) |
0 (0%) |
+83 |
| xUnit test files |
13 |
13 |
— |
| Pester test files |
30 |
26 |
+4 |
| Integration test coverage |
84% |
~70% |
+14% |
| PVE API areas covered |
12 of ~20 |
10 of ~20 |
+2 |
| Critical issues |
0 |
3 |
-3 |
| High issues |
2 |
8 |
-6 |
| Medium issues |
5 |
10 |
-5 |
| Low issues |
6 |
10 |
-4 |
| Total issues |
13 |
31 |
-18 |
| NuGet dependencies (runtime) |
3 |
2 |
+1 (SharpCompress) |
| Security vulnerabilities |
0 |
0 |
— |
| Community files present |
9/9 |
3/9 |
+6 |
| Documentation files |
78 |
1 |
+77 |