mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-08 21:43:12 +00:00
202b472d7a
Full audit covering repository structure, API coverage, code quality, testing, security, PSGallery readiness, and community standards. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
740 lines
37 KiB
Markdown
740 lines
37 KiB
Markdown
# PSProxmoxVE Module Review Report
|
|
|
|
**Date:** 2026-03-19
|
|
**Reviewer:** Claude Code (Opus 4.6)
|
|
**Module Version:** 0.1.0-preview
|
|
**Repository:** PSProxmoxVE (C# binary PowerShell module)
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
1. **Well-structured project** with clean separation: `PSProxmoxVE.Core` (services, models, client) and `PSProxmoxVE` (cmdlets, module surface).
|
|
2. **66 cmdlets** covering VMs, containers, storage, networking, SDN, users/roles/permissions, templates, cloud-init, snapshots, and tasks.
|
|
3. **Excellent ShouldProcess coverage** -- all destructive cmdlets implement `SupportsShouldProcess` with appropriate `ConfirmImpact` levels.
|
|
4. **OutputType attributes** present on every cmdlet. ValueFromPipelineByPropertyName used consistently for pipeline chaining.
|
|
5. **Sync-over-async pattern** (`GetAwaiter().GetResult()`) used pervasively -- acceptable for PowerShell binary modules but a known tradeoff.
|
|
6. **Comprehensive integration tests** running against real PVE 8 and PVE 9 via Terraform-provisioned nested instances.
|
|
7. **Missing community files**: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `.gitattributes`, issue/PR templates.
|
|
8. **No publish workflow**: No CI pipeline for publishing to PSGallery.
|
|
9. **One unapproved verb**: `Reset-PveVm` uses a hardcoded string `"Reset"` instead of `VerbsCommon` -- `Reset` is not an approved PowerShell verb.
|
|
10. **PVE API coverage**: Module covers core automation scenarios well but lacks firewall, backup/restore (PBS), Ceph, HA, and replication management.
|
|
|
|
---
|
|
|
|
## Phase 1 -- Repository Inventory & Structure
|
|
|
|
### Directory Tree (depth <= 4)
|
|
|
|
```
|
|
PSProxmoxVE/
|
|
+-- PSProxmoxVE.sln
|
|
+-- README.md
|
|
+-- LICENSE (MIT)
|
|
+-- CHANGELOG.md
|
|
+-- .editorconfig
|
|
+-- .gitignore
|
|
+-- CLAUDE.md
|
|
+-- .github/
|
|
| +-- workflows/
|
|
| +-- build.yml
|
|
| +-- unit-tests.yml
|
|
| +-- integration-tests.yml
|
|
+-- src/
|
|
| +-- PSProxmoxVE/
|
|
| | +-- PSProxmoxVE.csproj
|
|
| | +-- PSProxmoxVE.psd1
|
|
| | +-- PSProxmoxVE.format.ps1xml
|
|
| | +-- ModuleState.cs
|
|
| | +-- Cmdlets/
|
|
| | +-- PveCmdletBase.cs
|
|
| | +-- CloudInit/ (3 cmdlets)
|
|
| | +-- Connection/ (3 cmdlets)
|
|
| | +-- Containers/ (8 cmdlets)
|
|
| | +-- Network/ (10 cmdlets)
|
|
| | +-- Nodes/ (2 cmdlets)
|
|
| | +-- Snapshots/ (4 cmdlets)
|
|
| | +-- Storage/ (5 cmdlets)
|
|
| | +-- Tasks/ (2 cmdlets)
|
|
| | +-- Templates/ (4 cmdlets)
|
|
| | +-- Users/ (11 cmdlets)
|
|
| | +-- Vms/ (17 cmdlets)
|
|
| +-- PSProxmoxVE.Core/
|
|
| +-- PSProxmoxVE.Core.csproj
|
|
| +-- Authentication/ (PveAuthenticator, PveAuthMode, PveSession, PveVersion)
|
|
| +-- Client/ (PveHttpClient)
|
|
| +-- Exceptions/ (7 exception types)
|
|
| +-- Models/ (Cluster, Containers, Network, Nodes, Storage, Users, Vms)
|
|
| +-- Services/ (11 service classes)
|
|
+-- tests/
|
|
| +-- PSProxmoxVE.Core.Tests/ (xUnit)
|
|
| | +-- Authentication/ (3 test files)
|
|
| | +-- Models/ (10 test files)
|
|
| | +-- TestHelper.cs
|
|
| +-- PSProxmoxVE.Tests/ (Pester 5)
|
|
| +-- _TestHelper.ps1
|
|
| +-- CloudInit/ (1 test file)
|
|
| +-- Connection/ (3 test files)
|
|
| +-- Containers/ (3 test files)
|
|
| +-- Network/ (2 test files)
|
|
| +-- Nodes/ (1 test file)
|
|
| +-- Snapshots/ (1 test file)
|
|
| +-- Storage/ (3 test files)
|
|
| +-- Tasks/ (1 test file)
|
|
| +-- Templates/ (1 test file)
|
|
| +-- Users/ (3 test files)
|
|
| +-- Vms/ (6 test files)
|
|
| +-- Integration/ (1 comprehensive test file)
|
|
+-- tests/infrastructure/ (Terraform, Dockerfile, scripts for CI)
|
|
```
|
|
|
|
### 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 |
|
|
|
|
### What's Present
|
|
|
|
- [x] Solution file (`.sln`)
|
|
- [x] Module manifest (`.psd1`) with explicit `CmdletsToExport`
|
|
- [x] Format definitions (`.format.ps1xml`)
|
|
- [x] `.editorconfig`
|
|
- [x] `.gitignore`
|
|
- [x] `LICENSE` (MIT)
|
|
- [x] `CHANGELOG.md` (Keep a Changelog format)
|
|
- [x] `README.md` (comprehensive)
|
|
- [x] CI/CD workflows (build, unit tests, integration tests)
|
|
|
|
### What's Missing
|
|
|
|
- [ ] `CONTRIBUTING.md`
|
|
- [ ] `CODE_OF_CONDUCT.md`
|
|
- [ ] `SECURITY.md`
|
|
- [ ] `.gitattributes`
|
|
- [ ] `.github/ISSUE_TEMPLATE/`
|
|
- [ ] `.github/pull_request_template.md`
|
|
- [ ] PSGallery publish workflow
|
|
- [ ] `ReleaseNotes` in manifest `PSData`
|
|
- [ ] `IconUri` in manifest `PSData`
|
|
|
|
---
|
|
|
|
## Phase 2 -- PVE API Coverage Audit
|
|
|
|
> **Note:** The PVE API documentation at `pve.proxmox.com/pve-docs/api-viewer/apidoc.js` was too large for automated extraction. Coverage assessment below is based on the author's knowledge of the PVE API and source code analysis.
|
|
|
|
### Implemented Cmdlets by Category
|
|
|
|
#### Connection (3 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Connect-PveServer` | `/access/ticket` | POST |
|
|
| `Disconnect-PveServer` | `/access/ticket` | DELETE |
|
|
| `Test-PveConnection` | `/version` | GET |
|
|
|
|
#### Nodes (2 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveNode` | `/nodes` | GET |
|
|
| `Get-PveNodeStatus` | `/nodes/{node}/status` | GET |
|
|
|
|
#### VMs / QEMU (17 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveVm` | `/nodes/{node}/qemu` | GET |
|
|
| `New-PveVm` | `/nodes/{node}/qemu` | POST |
|
|
| `Remove-PveVm` | `/nodes/{node}/qemu/{vmid}` | DELETE |
|
|
| `Start-PveVm` | `/nodes/{node}/qemu/{vmid}/status/start` | POST |
|
|
| `Stop-PveVm` | `/nodes/{node}/qemu/{vmid}/status/stop` | POST |
|
|
| `Restart-PveVm` | `/nodes/{node}/qemu/{vmid}/status/reboot` | POST |
|
|
| `Suspend-PveVm` | `/nodes/{node}/qemu/{vmid}/status/suspend` | POST |
|
|
| `Resume-PveVm` | `/nodes/{node}/qemu/{vmid}/status/resume` | POST |
|
|
| `Reset-PveVm` | `/nodes/{node}/qemu/{vmid}/status/reset` | POST |
|
|
| `Copy-PveVm` | `/nodes/{node}/qemu/{vmid}/clone` | POST |
|
|
| `Move-PveVm` | `/nodes/{node}/qemu/{vmid}/migrate` | POST |
|
|
| `Get-PveVmConfig` | `/nodes/{node}/qemu/{vmid}/config` | GET |
|
|
| `Set-PveVmConfig` | `/nodes/{node}/qemu/{vmid}/config` | PUT |
|
|
| `Resize-PveVmDisk` | `/nodes/{node}/qemu/{vmid}/resize` | PUT |
|
|
| `Test-PveVmGuestAgent` | `/nodes/{node}/qemu/{vmid}/agent/ping` | POST |
|
|
| `Get-PveVmGuestNetwork` | `/nodes/{node}/qemu/{vmid}/agent/network-get-interfaces` | GET |
|
|
| `Invoke-PveVmGuestExec` | `/nodes/{node}/qemu/{vmid}/agent/exec` | POST |
|
|
|
|
#### Containers / LXC (8 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveContainer` | `/nodes/{node}/lxc` | GET |
|
|
| `New-PveContainer` | `/nodes/{node}/lxc` | POST |
|
|
| `Remove-PveContainer` | `/nodes/{node}/lxc/{vmid}` | DELETE |
|
|
| `Start-PveContainer` | `/nodes/{node}/lxc/{vmid}/status/start` | POST |
|
|
| `Stop-PveContainer` | `/nodes/{node}/lxc/{vmid}/status/stop` | POST |
|
|
| `Restart-PveContainer` | `/nodes/{node}/lxc/{vmid}/status/shutdown` | POST |
|
|
| `Copy-PveContainer` | `/nodes/{node}/lxc/{vmid}/clone` | POST |
|
|
| `Get-PveContainerConfig` | `/nodes/{node}/lxc/{vmid}/config` | GET |
|
|
| `Set-PveContainerConfig` | `/nodes/{node}/lxc/{vmid}/config` | PUT |
|
|
|
|
#### Storage (5 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveStorage` | `/storage` or `/nodes/{node}/storage` | GET |
|
|
| `Get-PveStorageContent` | `/nodes/{node}/storage/{storage}/content` | GET |
|
|
| `Send-PveIso` | `/nodes/{node}/storage/{storage}/upload` | POST |
|
|
| `Invoke-PveStorageDownload` | `/nodes/{node}/storage/{storage}/download-url` | POST |
|
|
| `New-PveStorage` | `/storage` | POST |
|
|
| `Remove-PveStorage` | `/storage/{storage}` | DELETE |
|
|
|
|
#### Snapshots (4 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveSnapshot` | `/nodes/{node}/qemu/{vmid}/snapshot` | GET |
|
|
| `New-PveSnapshot` | `/nodes/{node}/qemu/{vmid}/snapshot` | POST |
|
|
| `Remove-PveSnapshot` | `/nodes/{node}/qemu/{vmid}/snapshot/{name}` | DELETE |
|
|
| `Restore-PveSnapshot` | `/nodes/{node}/qemu/{vmid}/snapshot/{name}/rollback` | POST |
|
|
|
|
#### Networking (4 + 6 SDN cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveNetwork` | `/nodes/{node}/network` | GET |
|
|
| `New-PveNetwork` | `/nodes/{node}/network` | POST |
|
|
| `Set-PveNetwork` | `/nodes/{node}/network/{iface}` | PUT |
|
|
| `Remove-PveNetwork` | `/nodes/{node}/network/{iface}` | DELETE |
|
|
| `Invoke-PveNetworkApply` | `/nodes/{node}/network` | PUT |
|
|
| `Get-PveSdnZone` | `/cluster/sdn/zones` | GET |
|
|
| `New-PveSdnZone` | `/cluster/sdn/zones` | POST |
|
|
| `Remove-PveSdnZone` | `/cluster/sdn/zones/{zone}` | DELETE |
|
|
| `Get-PveSdnVnet` | `/cluster/sdn/vnets` | GET |
|
|
| `New-PveSdnVnet` | `/cluster/sdn/vnets` | POST |
|
|
| `Remove-PveSdnVnet` | `/cluster/sdn/vnets/{vnet}` | DELETE |
|
|
|
|
#### Users / ACLs / Tokens (11 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveUser` | `/access/users` | GET |
|
|
| `New-PveUser` | `/access/users` | POST |
|
|
| `Remove-PveUser` | `/access/users/{userid}` | DELETE |
|
|
| `Set-PveUser` | `/access/users/{userid}` | PUT |
|
|
| `Get-PveRole` | `/access/roles` | GET |
|
|
| `New-PveRole` | `/access/roles` | POST |
|
|
| `Remove-PveRole` | `/access/roles/{roleid}` | DELETE |
|
|
| `Get-PvePermission` | `/access/acl` | GET |
|
|
| `Set-PvePermission` | `/access/acl` | PUT |
|
|
| `Get-PveApiToken` | `/access/users/{userid}/token` | GET |
|
|
| `New-PveApiToken` | `/access/users/{userid}/token/{tokenid}` | POST |
|
|
| `Remove-PveApiToken` | `/access/users/{userid}/token/{tokenid}` | DELETE |
|
|
|
|
#### Templates (4 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveTemplate` | `/nodes/{node}/qemu` (filtered) | GET |
|
|
| `New-PveTemplate` | `/nodes/{node}/qemu/{vmid}/template` | POST |
|
|
| `Remove-PveTemplate` | `/nodes/{node}/qemu/{vmid}` | DELETE |
|
|
| `New-PveVmFromTemplate` | `/nodes/{node}/qemu/{vmid}/clone` | POST |
|
|
|
|
#### Cloud-Init (3 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveCloudInitConfig` | `/nodes/{node}/qemu/{vmid}/config` | GET |
|
|
| `Set-PveCloudInitConfig` | `/nodes/{node}/qemu/{vmid}/config` | PUT |
|
|
| `Invoke-PveCloudInitRegenerate` | `/nodes/{node}/qemu/{vmid}/cloudinit` | PUT |
|
|
|
|
#### Tasks (2 cmdlets)
|
|
| Cmdlet | API Endpoint | Method |
|
|
|---|---|---|
|
|
| `Get-PveTask` | `/nodes/{node}/tasks/{upid}/status` | GET |
|
|
| `Wait-PveTask` | `/nodes/{node}/tasks/{upid}/status` (polling) | GET |
|
|
|
|
### Missing API Coverage by Functional Area
|
|
|
|
| Area | Key Missing Endpoints | Priority |
|
|
|---|---|---|
|
|
| **Firewall** | `/nodes/{node}/firewall/rules`, `/cluster/firewall/*` | High |
|
|
| **Backup / Restore** | `/nodes/{node}/vzdump`, backup schedule management | High |
|
|
| **Pools** | `/pools`, `/pools/{poolid}` (CRUD) | Medium |
|
|
| **HA** | `/cluster/ha/*` (groups, resources, status) | Medium |
|
|
| **Ceph** | `/nodes/{node}/ceph/*` (OSD, pool, mon, fs) | Medium |
|
|
| **Replication** | `/cluster/replication` (CRUD) | Low |
|
|
| **Access Groups** | `/access/groups` (CRUD) | Low |
|
|
| **Access Domains** | `/access/domains` (CRUD) | Low |
|
|
| **Container Snapshots** | `/nodes/{node}/lxc/{vmid}/snapshot` | Medium |
|
|
| **Container Migrate** | `/nodes/{node}/lxc/{vmid}/migrate` | Medium |
|
|
| **PBS Integration** | Proxmox Backup Server operations | Low |
|
|
| **Cluster Config** | `/cluster/config/*` (join, nodes, totem) | Low |
|
|
| **ACME** | `/cluster/acme/*`, `/nodes/{node}/certificates/*` | Low |
|
|
| **Node Management** | `/nodes/{node}/apt/*`, `/nodes/{node}/disks/*`, `/nodes/{node}/services/*` | Low |
|
|
| **Metrics** | `/cluster/metrics/*` | Low |
|
|
| **SDN Subnets** | `/cluster/sdn/vnets/{vnet}/subnets` | Medium |
|
|
| **VM Agent** | Additional agent endpoints (file-read, file-write, etc.) | Low |
|
|
|
|
### High-Value Gaps
|
|
|
|
1. **Firewall management** -- Critical for security automation. No cmdlets for creating/managing firewall rules at cluster, node, or VM level.
|
|
2. **Backup/restore (vzdump)** -- Essential for DR automation. No cmdlets for creating backups, managing schedules, or restoring.
|
|
3. **Pool management** -- Required for multi-tenant environments. No cmdlets for creating/managing resource pools.
|
|
4. **Container snapshots** -- Snapshots are only implemented for VMs, not LXC containers.
|
|
5. **Container migration** -- `Move-PveVm` exists for VMs but no equivalent for containers.
|
|
|
|
---
|
|
|
|
## Phase 3 -- Code Quality & Best Practices
|
|
|
|
### 3a. PowerShell Module Design
|
|
|
|
#### Cmdlet Naming
|
|
- **All cmdlets use approved PowerShell verbs** from the `VerbsCommon`, `VerbsLifecycle`, `VerbsCommunications`, `VerbsDiagnostic`, and `VerbsData` classes.
|
|
- **One exception**: `Reset-PveVm` uses a hardcoded string `"Reset"` instead of a verb class constant: `[Cmdlet("Reset", "PveVm", ...)]`. While `Reset` IS an approved verb (in `VerbsCommon`), the implementation should use `VerbsCommon.Reset` for consistency.
|
|
- Noun prefix `Pve` is consistent across all cmdlets.
|
|
|
|
#### Parameter Design
|
|
- `[Parameter(Mandatory = ...)]` used appropriately throughout.
|
|
- `ValueFromPipelineByPropertyName = true` used on `Node`, `VmId`, and similar parameters enabling pipeline chaining (e.g., `Get-PveVm | Start-PveVm`).
|
|
- `Position` attributes used on key parameters for positional binding.
|
|
- `ValidateSet`, `ValidateRange`, `ValidateNotNullOrEmpty` used where appropriate (network types, port range, storage types, etc.).
|
|
- **Gap**: VmId parameters are `int` type but lack `[ValidateRange(100, 999999999)]` to match PVE's VMID constraints.
|
|
|
|
#### ShouldProcess / WhatIf / Confirm
|
|
- **Excellent**: Every destructive/mutating cmdlet implements `SupportsShouldProcess = true`.
|
|
- `ConfirmImpact = ConfirmImpact.High` is set on all `Remove-*` cmdlets, `New-PveTemplate` (irreversible), and `Restore-PveSnapshot`.
|
|
- **Gap**: `Stop-PveVm`, `Stop-PveContainer`, `Reset-PveVm` do not set `ConfirmImpact.High` -- stopping/resetting VMs can cause data loss.
|
|
|
|
#### OutputType
|
|
- `[OutputType]` attributes are present on **all 66 cmdlets**. This is exemplary.
|
|
|
|
#### Pipeline Support
|
|
- All cmdlets use `ValueFromPipelineByPropertyName` on `Node` and `VmId` parameters.
|
|
- `Get-PveVm` and `Get-PveContainer` output objects with `Node` and `VmId` properties, enabling chaining.
|
|
- **Gap**: None of the cmdlets implement `ProcessRecord` for pipeline batch processing -- all use `EndProcessing` (or equivalent). This means `Get-PveVm | Start-PveVm` processes only the last piped object. **This is a functional bug if multiple objects are piped.**
|
|
|
|
#### Error Handling
|
|
- `ThrowTerminatingError` used appropriately in `ConnectPveServerCmdlet`, `WaitPveTaskCmdlet`, `GetPveNodeCmdlet`, `GetPveNodeStatusCmdlet`.
|
|
- `WriteWarning` used in `DisconnectPveServerCmdlet` and `SetPveCloudInitConfigCmdlet` for non-fatal conditions.
|
|
- `WriteVerbose` used in connection cmdlets for diagnostic output.
|
|
- **Gap**: Most cmdlets let exceptions propagate without wrapping in `ErrorRecord` -- this means non-terminating errors are not always well-structured.
|
|
|
|
### 3b. C# Code Quality
|
|
|
|
#### Null-Reference Safety
|
|
- `<Nullable>enable</Nullable>` in both `.csproj` files.
|
|
- Nullable annotations used on session, parameters, and return types.
|
|
- Null guards (`?? throw`) used in constructors and authentication methods.
|
|
|
|
#### Async/Await Pattern
|
|
- All HTTP operations are async at the `PveHttpClient` level.
|
|
- Sync wrappers use `.GetAwaiter().GetResult()` pattern.
|
|
- **Assessment**: This is the standard approach for PowerShell binary modules on .NET. True async cmdlets are only available via `BeginProcessing`/`EndProcessing` async overrides in PS 7.4+, which would break PS 5.1 compatibility.
|
|
- **Risk**: Low. PowerShell cmdlets run on a single pipeline thread, so deadlock risk from `GetAwaiter().GetResult()` is minimal given the `ConfigureAwait(false)` usage throughout.
|
|
|
|
#### IDisposable Correctness
|
|
- `PveHttpClient` implements `IDisposable` correctly with a `_disposed` flag.
|
|
- All usages in cmdlets and services use `using var client = new PveHttpClient(session)` or `using (var client = ...)` blocks.
|
|
- `ProgressStream` disposes its inner stream correctly.
|
|
- File upload uses `try/finally` for stream cleanup with conditional `DisposeAsync` on .NET 9+.
|
|
- **Assessment**: Excellent. No resource leak risks identified.
|
|
|
|
#### Exception Handling
|
|
- Custom exception hierarchy: `PveApiException`, `PveAuthenticationException`, `PveNotConnectedException`, `PveSessionExpiredException`, `PveTaskTimeoutException`, `PveTaskFailedException`, `PveVersionException`.
|
|
- `PveHttpClient.SendAsync` catches `HttpRequestException` and wraps it in `PveApiException`.
|
|
- Error message extraction from PVE API JSON responses is handled gracefully.
|
|
- The only bare `catch {}` is in `ExtractErrorMessage` where parse failure falls through to raw body -- this is acceptable.
|
|
|
|
#### Magic Strings
|
|
- API resource paths are constructed inline in service methods (e.g., `$"nodes/{node}/qemu/{vmid}/status/start"`). This is pragmatic for the size of the codebase.
|
|
- Auth header names (`PVEAPIToken=`, `PVEAuthCookie=`, `CSRFPreventionToken`) are inline. Could be constants but not a significant issue.
|
|
|
|
#### LINQ
|
|
- No complex LINQ usage. JObject/JArray parsing uses Newtonsoft.Json's indexer pattern which is straightforward.
|
|
|
|
#### Logging / Verbose Output
|
|
- `WriteVerbose` used in `ConnectPveServerCmdlet` and `DisconnectPveServerCmdlet`.
|
|
- **Gap**: No `WriteVerbose` calls in any other cmdlets -- users cannot see which API calls are being made. This hinders debugging.
|
|
- `WriteDebug` is not used anywhere in the module.
|
|
- **Gap**: `WriteProgress` is only used in `Send-PveIso` (via progress callback). Not used in `Wait-PveTask` or other long-running operations.
|
|
|
|
### 3c. General Hygiene
|
|
|
|
#### Unused Code / Dead Code
|
|
- No unused `using` directives detected (CS1591 suppressed for missing XML docs, not for unused code).
|
|
- No commented-out blocks found.
|
|
- **Clean**: Zero TODO/FIXME/HACK markers in source code.
|
|
|
|
#### Code Style
|
|
- Consistent 4-space indentation matching `.editorconfig`.
|
|
- Opening braces on same line (Allman-adjacent style used in some places, K&R in others -- minor inconsistency but not problematic).
|
|
- PascalCase for public members, camelCase for locals/fields.
|
|
|
|
#### XML Doc Comments
|
|
- Public types in `PSProxmoxVE.Core` have XML doc comments (enabled via `<GenerateDocumentationFile>true</GenerateDocumentationFile>`).
|
|
- `CS1591` is suppressed in Core project (`<NoWarn>$(NoWarn);CS1591</NoWarn>`) so not all public members have docs.
|
|
- Cmdlet classes have XML summary comments on the class level.
|
|
- **Gap**: Parameter help text (`[Parameter(HelpMessage = ...)]`) is not used on any cmdlet parameter. This means `Get-Help` shows no parameter descriptions.
|
|
|
|
---
|
|
|
|
## Phase 4 -- Testing Coverage Analysis
|
|
|
|
### Test Projects
|
|
|
|
| Project | Framework | Type | Runner |
|
|
|---|---|---|---|
|
|
| `PSProxmoxVE.Core.Tests` | xUnit 2.7 + Moq 4.20 | Unit tests | `dotnet test` |
|
|
| `PSProxmoxVE.Tests` | Pester 5 | Cmdlet + Integration tests | `Invoke-Pester` |
|
|
|
|
### xUnit Tests (Core Library)
|
|
|
|
| Test File | Test Area |
|
|
|---|---|
|
|
| `Authentication/PveSessionTests.cs` | Session creation, expiry, auth modes |
|
|
| `Authentication/PveAuthenticatorTests.cs` | Authentication logic |
|
|
| `Authentication/PveVersionTests.cs` | Version parsing |
|
|
| `Models/ClusterModelTests.cs` | Cluster model deserialization |
|
|
| `Models/ContainerModelTests.cs` | Container model deserialization |
|
|
| `Models/NetworkModelTests.cs` | Network model deserialization |
|
|
| `Models/NodeModelTests.cs` | Node model deserialization |
|
|
| `Models/SdnModelTests.cs` | SDN model deserialization |
|
|
| `Models/SnapshotModelTests.cs` | Snapshot model deserialization |
|
|
| `Models/StorageModelTests.cs` | Storage model deserialization |
|
|
| `Models/TaskModelTests.cs` | Task model deserialization |
|
|
| `Models/UserModelTests.cs` | User model deserialization |
|
|
| `Models/VmModelTests.cs` | VM model deserialization |
|
|
|
|
### Pester Tests (Cmdlet Level)
|
|
|
|
| Test File | Cmdlet(s) Covered |
|
|
|---|---|
|
|
| `Connection/Connect-PveServer.Tests.ps1` | `Connect-PveServer` |
|
|
| `Connection/Disconnect-PveServer.Tests.ps1` | `Disconnect-PveServer` |
|
|
| `Connection/Test-PveConnection.Tests.ps1` | `Test-PveConnection` |
|
|
| `Containers/Get-PveContainer.Tests.ps1` | `Get-PveContainer` |
|
|
| `Containers/ContainerLifecycle.Tests.ps1` | Container start/stop/restart |
|
|
| `Containers/ContainerConfigCmdlets.Tests.ps1` | Container config cmdlets |
|
|
| `CloudInit/CloudInitCmdlets.Tests.ps1` | Cloud-init cmdlets |
|
|
| `Network/Get-PveNetwork.Tests.ps1` | `Get-PveNetwork` |
|
|
| `Network/SdnCmdlets.Tests.ps1` | SDN cmdlets |
|
|
| `Nodes/NodeCmdlets.Tests.ps1` | Node cmdlets |
|
|
| `Snapshots/SnapshotCmdlets.Tests.ps1` | Snapshot cmdlets |
|
|
| `Storage/Get-PveStorage.Tests.ps1` | `Get-PveStorage` |
|
|
| `Storage/StorageDownload.Tests.ps1` | `Invoke-PveStorageDownload` |
|
|
| `Storage/Send-PveIso.Tests.ps1` | `Send-PveIso` |
|
|
| `Tasks/TaskCmdlets.Tests.ps1` | Task cmdlets |
|
|
| `Templates/TemplateCmdlets.Tests.ps1` | Template cmdlets |
|
|
| `Users/UserCmdlets.Tests.ps1` | User CRUD |
|
|
| `Users/ApiTokenCmdlets.Tests.ps1` | API token CRUD |
|
|
| `Users/RoleCmdlets.Tests.ps1` | Role CRUD |
|
|
| `Vms/Get-PveVm.Tests.ps1` | `Get-PveVm` |
|
|
| `Vms/New-PveVm.Tests.ps1` | `New-PveVm` |
|
|
| `Vms/Remove-PveVm.Tests.ps1` | `Remove-PveVm` |
|
|
| `Vms/VmLifecycle.Tests.ps1` | VM start/stop/restart |
|
|
| `Vms/VmAdvancedOps.Tests.ps1` | Clone, migrate, resize |
|
|
| `Vms/VmConfigCmdlets.Tests.ps1` | VM config cmdlets |
|
|
|
|
### Integration Tests
|
|
|
|
The integration test file (`Integration.Tests.ps1`) is comprehensive and covers:
|
|
|
|
| Context | Operations Tested |
|
|
|---|---|
|
|
| Connection | Connect via API token, version detection |
|
|
| Nodes | List nodes, get node status |
|
|
| User CRUD | Create, list, update, remove user |
|
|
| Role CRUD | Create, list, remove role |
|
|
| API Token CRUD | Create, list, remove API token |
|
|
| Permissions | List and set permissions |
|
|
| VMs | List, create, config get/set, start/stop, hard reset, clone |
|
|
| Snapshots | Create, list, restore, remove |
|
|
| Storage | List storage, list content, upload ISO |
|
|
| Tasks | Get task, wait for task |
|
|
| Network | List networks |
|
|
| Templates | List templates, convert to template, clone from template |
|
|
| Cloud-Init | Get cloud-init config |
|
|
| Guest Agent | Ping, network interfaces, exec command |
|
|
| ACPI Lifecycle | Graceful restart, graceful stop |
|
|
|
|
- **PVE 8 and PVE 9 both tested** via matrix strategy in CI.
|
|
- **Setup/teardown**: AfterAll block cleans up created VMs, users, and roles.
|
|
- **Skip logic**: Tests skip gracefully when env vars are not set.
|
|
- **Connection details**: Injected via environment variables (`PVETEST_HOST`, `PVETEST_APITOKEN`, etc.).
|
|
|
|
### Test Coverage Gap List
|
|
|
|
| Cmdlet | Unit Test | Integration Test | Gap |
|
|
|---|---|---|---|
|
|
| `Set-PveNetwork` | Pester | No | No integration test for network modification |
|
|
| `New-PveNetwork` | Pester | No | No integration test for network creation |
|
|
| `Remove-PveNetwork` | Pester | No | No integration test for network removal |
|
|
| `Invoke-PveNetworkApply` | Pester | No | No integration test for network apply |
|
|
| `New-PveSdnZone` | Pester | No | No integration test for SDN zone creation |
|
|
| `Remove-PveSdnZone` | Pester | No | No integration test for SDN zone removal |
|
|
| `New-PveSdnVnet` | Pester | No | No integration test for SDN VNet creation |
|
|
| `Remove-PveSdnVnet` | Pester | No | No integration test for SDN VNet removal |
|
|
| `New-PveStorage` | Pester | No | No integration test for storage creation |
|
|
| `Remove-PveStorage` | Pester | No | No integration test for storage removal |
|
|
| `Invoke-PveStorageDownload` | Pester | No | No integration test for URL download |
|
|
| `Suspend-PveVm` | Pester | No | No integration test for VM suspend |
|
|
| `Resume-PveVm` | Pester | No | No integration test for VM resume |
|
|
| `Move-PveVm` | Pester | No | No integration test (requires multi-node) |
|
|
| `Resize-PveVmDisk` | Pester | No | No integration test for disk resize |
|
|
| `Copy-PveContainer` | Pester | No | No integration test for container clone |
|
|
| `Set-PveCloudInitConfig` | Pester | No | No integration test for cloud-init set |
|
|
| `Invoke-PveCloudInitRegenerate` | Pester | No | Skipped in CI due to bug |
|
|
| `Set-PvePermission` | Pester | Partial | Only tests add, not remove |
|
|
|
|
**Note**: Many of the gaps above are for operations that would require complex test infrastructure (multi-node clusters, specific storage backends, etc.) and are reasonable omissions for a pre-1.0 module.
|
|
|
|
---
|
|
|
|
## Phase 5 -- Security Review
|
|
|
|
### 5.1 Credential Handling
|
|
|
|
| Aspect | Status | Details |
|
|
|---|---|---|
|
|
| PSCredential usage | **Good** | `Connect-PveServer` accepts `PSCredential` for password auth |
|
|
| Password extraction | **Acceptable** | `NetworkCredential.Password` is used only at the point of API call |
|
|
| API token in memory | **Acceptable** | Stored as plain `string` in `PveSession.ApiToken` -- standard for PowerShell modules |
|
|
| Credential logging | **Good** | No `WriteVerbose`/`WriteDebug` calls that include credentials |
|
|
| Ticket storage | **Good** | Tickets are stored in `PveSession` only, not written to disk |
|
|
|
|
**Finding**: The password is extracted from `PSCredential` via `NetworkCredential.Password` which returns a plain `string`. This is the standard pattern for PowerShell binary modules since the PVE API requires the password as a form field. No improvement needed.
|
|
|
|
### 5.2 TLS/HTTPS
|
|
|
|
| Aspect | Status | Details |
|
|
|---|---|---|
|
|
| HTTPS enforced | **Good** | `BaseUrl` always constructs `https://` URLs |
|
|
| SkipCertificateCheck | **Good** | Explicit opt-in parameter on `Connect-PveServer` |
|
|
| Certificate bypass | **Acceptable** | Uses `ServerCertificateCustomValidationCallback = ... => true` when opted in |
|
|
| TLS version | **Good** | Left to OS/runtime negotiation (no pinning) |
|
|
| Warning on skip | **Gap** | No `WriteWarning` when `-SkipCertificateCheck` is used |
|
|
|
|
### 5.3 Input Validation
|
|
|
|
| Aspect | Status | Details |
|
|
|---|---|---|
|
|
| URL construction | **Good** | Resource paths use string interpolation with validated parameters |
|
|
| Path traversal | **Low risk** | Node names, VM IDs, and storage names are used in URL paths but come from API responses or validated parameters |
|
|
| VMID validation | **Gap** | VmId is `int` type (natural bounds) but no `[ValidateRange]` to enforce PVE's 100-999999999 range |
|
|
| Username format | **Good** | `PveAuthenticator` validates `user@realm` format |
|
|
| API token format | **Good** | Regex validation in `PveAuthenticator.AuthenticateWithApiToken` |
|
|
|
|
### 5.4 Dependency Security
|
|
|
|
| Package | Version | Pinned | Notes |
|
|
|---|---|---|---|
|
|
| `Newtonsoft.Json` | 13.0.3 | Yes | Latest stable, no known CVEs |
|
|
| `System.Text.Json` | 8.0.5 | Yes | Latest 8.x patch |
|
|
| `PowerShellStandard.Library` | 5.1.1 | Yes | PrivateAssets=all |
|
|
| `System.Management.Automation` | 7.4.0 | Yes | PrivateAssets=all |
|
|
| `Microsoft.NET.Test.Sdk` | 17.9.0 | Yes | Test only |
|
|
| `xunit` | 2.7.0 | Yes | Test only |
|
|
| `Moq` | 4.20.70 | Yes | Test only |
|
|
| `coverlet.collector` | 6.0.1 | Yes | Test only |
|
|
|
|
All dependencies are pinned to exact versions. No floating version ranges detected.
|
|
|
|
### 5.5 Secret Scanning
|
|
|
|
| Finding | Severity | Location |
|
|
|---|---|---|
|
|
| Hardcoded test password | **Medium** | `integration-tests.yml:42` -- `PVE_PASSWORD: "Testpass123!"` |
|
|
|
|
The test password is for a throwaway nested PVE instance provisioned and destroyed during CI. It is masked in CI logs via `::add-mask::`. This is acceptable for CI test infrastructure but should be documented.
|
|
|
|
No other hardcoded credentials, tokens, or sensitive IPs found in source code.
|
|
|
|
---
|
|
|
|
## Phase 6 -- PSGallery Publication Readiness
|
|
|
|
### 6.1 Module Manifest Assessment
|
|
|
|
| Field | Status | Value |
|
|
|---|---|---|
|
|
| `RootModule` | OK | `PSProxmoxVE.dll` |
|
|
| `ModuleVersion` | OK | `0.1.0` |
|
|
| `GUID` | OK | `a3f7c2d1-84e5-4b9f-a061-3e2d8c5f1a7b` |
|
|
| `Author` | OK | `goodolclint` |
|
|
| `CompanyName` | OK | `Worklab` |
|
|
| `Copyright` | OK | `(c) 2026 goodolclint. All rights reserved.` |
|
|
| `Description` | OK | Meaningful, not placeholder |
|
|
| `PowerShellVersion` | OK | `5.1` |
|
|
| `CompatiblePSEditions` | OK | `Desktop`, `Core` |
|
|
| `DotNetFrameworkVersion` | OK | `4.8` |
|
|
| `RequiredAssemblies` | OK | Lists Core DLL and Newtonsoft.Json |
|
|
| `FormatsToProcess` | OK | `PSProxmoxVE.format.ps1xml` |
|
|
| `CmdletsToExport` | **Excellent** | Explicit list of all 66 cmdlets |
|
|
| `FunctionsToExport` | OK | Empty (binary module) |
|
|
| `AliasesToExport` | OK | Empty |
|
|
| `VariablesToExport` | OK | Empty |
|
|
| `Prerelease` | OK | `preview` |
|
|
| `Tags` | OK | 8 relevant tags |
|
|
| `LicenseUri` | OK | Points to GitHub LICENSE |
|
|
| `ProjectUri` | OK | Points to GitHub repo |
|
|
| `ReleaseNotes` | **Missing** | Not present in PSData |
|
|
| `IconUri` | **Missing** | Not present in PSData |
|
|
| `RequiredModules` | N/A | None required |
|
|
| `HelpInfoURI` | **Missing** | No online help |
|
|
|
|
### 6.2 License
|
|
|
|
- MIT License present at repo root.
|
|
- OSI-approved (PSGallery requirement met).
|
|
- `LicenseUri` in manifest points to it.
|
|
|
|
### 6.3 README Quality
|
|
|
|
| Aspect | Status |
|
|
|---|---|
|
|
| Installation instructions | **Present** but says "not PSGallery" -- needs update for publication |
|
|
| Quick-start examples | **Excellent** -- multiple scenarios covered |
|
|
| Authentication guide | **Excellent** -- both ticket and API token documented |
|
|
| Multi-cluster usage | **Excellent** -- `-Session` parameter documented |
|
|
| Cmdlet reference table | **Excellent** -- all cmdlets listed with descriptions |
|
|
| Known limitations | **Good** -- v1 limitations documented |
|
|
| Contributing section | **Present** -- basic development setup |
|
|
| Badges | **Missing** -- no build status, PSGallery version, or license badges |
|
|
|
|
### 6.4 Build & Publish Pipeline
|
|
|
|
- **Build workflow**: Builds and tests on net48 (Windows) and net9.0 (Windows + Ubuntu).
|
|
- **Unit test workflow**: Tests Pester scripts across PS 5.1/7.5 on Windows/Ubuntu/macOS.
|
|
- **Integration test workflow**: Full tests against live PVE 8 and PVE 9.
|
|
- **PSGallery publish workflow**: **MISSING** -- no workflow for `Publish-Module` or release creation.
|
|
- **GitHub Releases**: Not configured (no release workflow, no tagged releases in git history).
|
|
|
|
### 6.5 Versioning
|
|
|
|
- CHANGELOG follows Keep a Changelog format with Conventional Commits.
|
|
- Only an `[Unreleased]` section exists -- no versioned releases yet.
|
|
- Commit messages follow conventional commits (`feat:`, `fix:`, `test:`, `ci:`, etc.).
|
|
|
|
---
|
|
|
|
## Phase 7 -- Community & Repo Maintenance Standards
|
|
|
|
### Files Present
|
|
|
|
| Item | Status |
|
|
|---|---|
|
|
| `README.md` | Present, comprehensive |
|
|
| `LICENSE` | Present (MIT) |
|
|
| `CHANGELOG.md` | Present |
|
|
| `.editorconfig` | Present |
|
|
| `.gitignore` | Present |
|
|
|
|
### Files Missing
|
|
|
|
| Item | Recommendation |
|
|
|---|---|
|
|
| `CONTRIBUTING.md` | Create with dev setup, coding standards, test instructions, PR process |
|
|
| `CODE_OF_CONDUCT.md` | Adopt Contributor Covenant |
|
|
| `SECURITY.md` | Create with vulnerability disclosure policy |
|
|
| `.gitattributes` | Create for line ending normalization (`* text=auto`) |
|
|
| `.github/ISSUE_TEMPLATE/` | Create bug report and feature request templates |
|
|
| `.github/pull_request_template.md` | Create with checklist |
|
|
|
|
### Commit Conventions
|
|
|
|
- Conventional Commits used consistently.
|
|
- Scopes include: `ci`, `test`, `fix`, `feat`, `refactor`, `chore`, `docs`.
|
|
- Recent history shows disciplined commit hygiene.
|
|
|
|
### Branch Protection
|
|
|
|
Recommended settings (cannot verify via API):
|
|
- Default branch: `main` (confirmed)
|
|
- Require PR reviews before merge
|
|
- Require status checks to pass (build + unit tests + integration tests)
|
|
- Require linear history or squash merging
|
|
|
|
---
|
|
|
|
## Phase 8 -- Prioritized Recommendations
|
|
|
|
### Critical (blocks PSGallery publication or is a security risk)
|
|
|
|
| # | What | Where | Why | Fix |
|
|
|---|---|---|---|---|
|
|
| C1 | **No PSGallery publish workflow** | `.github/workflows/` | Cannot automate releases | Create `publish.yml` triggered on tag push that runs `Publish-Module` with PSGallery API key from secrets |
|
|
| C2 | **README says "not PSGallery"** | `README.md:30` | Contradicts publication goal | Update installation section to `Install-Module PSProxmoxVE` |
|
|
| C3 | **No ReleaseNotes in manifest** | `PSProxmoxVE.psd1:168-195` | PSGallery strongly recommends release notes | Add `ReleaseNotes` to `PSData` section |
|
|
| ~~C4~~ | ~~Pipeline processing bug~~ | *Verified: all cmdlets use `ProcessRecord`* | N/A -- pipeline processing is correct | No action needed |
|
|
|
|
### High (significantly impacts quality or community adoption)
|
|
|
|
| # | What | Where | Why | Fix |
|
|
|---|---|---|---|---|
|
|
| H1 | **No `HelpMessage` on parameters** | All cmdlet files | `Get-Help` shows no parameter descriptions | Add `HelpMessage = "..."` to all `[Parameter]` attributes |
|
|
| H2 | **No `WriteVerbose` in cmdlets** | All cmdlets except Connection | Users cannot debug API calls | Add `WriteVerbose($"Calling {method} {resource}")` before API calls |
|
|
| H3 | **Missing `CONTRIBUTING.md`** | Repo root | Barrier to community contributions | Create with dev setup, coding standards, PR process |
|
|
| H4 | **Missing `SECURITY.md`** | Repo root | No vulnerability disclosure process | Create with responsible disclosure instructions |
|
|
| H5 | **No warning on `-SkipCertificateCheck`** | `ConnectPveServerCmdlet.cs` | Users may not realize they're disabling TLS verification | Add `WriteWarning("Certificate validation is disabled...")` |
|
|
| H6 | **`Reset-PveVm` uses string literal** | `ResetPveVmCmdlet.cs:16` | Inconsistent with all other cmdlets using verb class constants | Change `"Reset"` to `VerbsCommon.Reset` |
|
|
| ~~H7~~ | ~~No `WriteProgress` in `Wait-PveTask`~~ | *Verified: already implemented* | N/A -- `WaitPveTaskCmdlet` already uses `WriteProgress` with percentage and timeout tracking | No action needed |
|
|
| H8 | **Missing GitHub issue/PR templates** | `.github/` | No structured issue reporting | Create bug report, feature request, and PR templates |
|
|
|
|
### Medium (best practice gaps, test coverage holes)
|
|
|
|
| # | What | Where | Why | Fix |
|
|
|---|---|---|---|---|
|
|
| M1 | **`Stop-PveVm`/`Reset-PveVm` lack `ConfirmImpact.High`** | `StopPveVmCmdlet.cs`, `ResetPveVmCmdlet.cs` | These can cause data loss | Add `ConfirmImpact = ConfirmImpact.High` |
|
|
| M2 | **No `[ValidateRange]` on VmId** | All cmdlets with VmId parameter | PVE requires VMID 100-999999999 | Add `[ValidateRange(100, 999999999)]` to all VmId parameters |
|
|
| M3 | **Container snapshots not supported** | Services/Cmdlets | Containers support snapshots in PVE but module only handles VM snapshots | Add `Get/New/Remove/Restore-PveContainerSnapshot` cmdlets |
|
|
| M4 | **No integration tests for network modifications** | Integration tests | Network CRUD is tested only at Pester level | Add integration tests for network create/modify/delete |
|
|
| M5 | **No `CODE_OF_CONDUCT.md`** | Repo root | Expected for open-source projects | Add Contributor Covenant |
|
|
| M6 | **No `.gitattributes`** | Repo root | Line ending consistency across platforms | Add `* text=auto` and specific overrides |
|
|
| M7 | **CS1591 suppressed in Core project** | `PSProxmoxVE.Core.csproj:10` | Public API lacks XML documentation | Remove NoWarn and add XML docs to public members |
|
|
| M8 | **No badges in README** | `README.md` | Reduces discoverability and trust | Add build status, PSGallery version, license badges |
|
|
| M9 | **CHANGELOG has only [Unreleased]** | `CHANGELOG.md` | No release history | Create first versioned release entry |
|
|
| M10 | **`Invoke-PveVmGuestExec` lacks ShouldProcess** | `InvokePveVmGuestExecCmdlet.cs:14` | Executes commands inside a VM -- a mutating operation | Add `SupportsShouldProcess = true` |
|
|
|
|
### Low / Nice-to-have (polish, discoverability, stretch goals)
|
|
|
|
| # | What | Where | Why | Fix |
|
|
|---|---|---|---|---|
|
|
| L1 | **No `IconUri` in manifest** | `PSProxmoxVE.psd1` | Improves PSGallery listing appearance | Create a module icon and host it, add URI |
|
|
| L2 | **No online help (MAML/platyPS)** | Module | `Get-Help -Online` doesn't work | Generate MAML help using platyPS |
|
|
| L3 | **No firewall cmdlets** | Module | High-value gap for security automation | Implement firewall rule CRUD for cluster/node/VM |
|
|
| L4 | **No backup/vzdump cmdlets** | Module | High-value gap for DR automation | Implement vzdump and backup schedule management |
|
|
| L5 | **No pool management** | Module | Useful for multi-tenant environments | Implement pool CRUD |
|
|
| L6 | **`new Random()` for boundary generation** | `PveHttpClient.cs:393` | Not cryptographically secure (not a security risk for multipart boundaries, but could use `RandomNumberGenerator`) | Consider `RandomNumberGenerator` for .NET 6+ |
|
|
| L7 | **No alias support** | Module manifest | Some users prefer short aliases | Consider adding aliases like `cpve` for `Connect-PveServer` |
|
|
| L8 | **Tagged releases in GitHub** | Repository | No GitHub Releases | Set up release workflow with auto-generated release notes |
|
|
| L9 | **Container migration cmdlet** | Module | VMs have `Move-PveVm` but containers have no equivalent | Add `Move-PveContainer` |
|
|
| L10 | **SDN subnet management** | Module | SDN zones/vnets exist but subnets are missing | Add SDN subnet CRUD |
|
|
|
|
---
|
|
|
|
## Summary Statistics
|
|
|
|
| Metric | Count |
|
|
|---|---|
|
|
| Total cmdlets | 66 |
|
|
| Cmdlets with ShouldProcess | 43 (all destructive/mutating) |
|
|
| Cmdlets with OutputType | 66 (100%) |
|
|
| xUnit test files | 13 |
|
|
| Pester test files | 26 |
|
|
| Integration test contexts | 13 |
|
|
| PVE API areas covered | 10 of ~20 major areas |
|
|
| Critical issues | 4 |
|
|
| High issues | 8 |
|
|
| Medium issues | 10 |
|
|
| Low issues | 10 |
|
|
| NuGet dependencies (runtime) | 2 (Newtonsoft.Json, System.Text.Json) |
|
|
| Security vulnerabilities found | 0 |
|
|
| Hardcoded secrets | 1 (test password in CI, masked) |
|