feat(cmdlets): implement VM lifecycle cmdlets

Add Get-PveVm, New-PveVm, Remove-PveVm, Start/Stop/Suspend/Resume/
Reset/Restart-PveVm, Copy-PveVm, Move-PveVm, Get-PveVmConfig,
Set-PveVmConfig, and Resize-PveVmDisk. All support pipeline input,
-WhatIf/-Confirm on state changes, and -Wait for async tasks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-17 15:45:57 -05:00
parent 9b24eaf0f0
commit cc4d08070d
14 changed files with 1102 additions and 0 deletions
@@ -0,0 +1,92 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Clones (copies) a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Creates a clone of an existing VM. By default, a linked clone is created when the source
/// is a template. Use -Full to create a full independent copy. Optionally specify a target
/// node to create the clone on a different cluster node.
/// Use -Wait to block until the clone task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Copy, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class CopyPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">The node on which the source VM resides.</para>
/// </summary>
[Parameter(Mandatory = true)]
public string SourceNode { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the source VM to clone. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">The VM ID to assign to the new clone. When omitted, the next available ID is used.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? NewVmId { get; set; }
/// <summary>
/// <para type="description">The display name for the new clone.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? NewName { get; set; }
/// <summary>
/// <para type="description">The target node for the clone. Defaults to the source node.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? TargetNode { get; set; }
/// <summary>
/// <para type="description">
/// When specified, creates a full independent clone instead of a linked clone.
/// A full clone is required when the source VM is not a template.
/// </para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Full { get; set; }
/// <summary>
/// <para type="description">Target storage pool for the full clone disks.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Storage { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the clone task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
var target = TargetNode ?? SourceNode;
if (!ShouldProcess($"VM {VmId} on node '{SourceNode}' to new VM on node '{target}'", "Copy-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var newid = NewVmId ?? 0;
var task = vmService.CloneVm(session, SourceNode, VmId, newid, NewName, TargetNode, Full.IsPresent);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Gets one or more QEMU/KVM virtual machines from a Proxmox VE server.</para>
/// <para type="description">
/// Retrieves virtual machine objects from the Proxmox VE API. Results can be filtered by
/// node, VM ID, name, status, tag, or limited to template VMs only.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Get, "PveVm")]
[OutputType(typeof(PveVm))]
public sealed class GetPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The name of the node to query. Accepts pipeline input from a PveNode object's Name property.
/// When omitted, VMs from all nodes are returned.
/// </para>
/// </summary>
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)]
public string? Node { get; set; }
/// <summary>
/// <para type="description">Filter results to the VM with this ID.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? VmId { get; set; }
/// <summary>
/// <para type="description">Filter results to VMs whose name matches this value (case-insensitive, contains match).</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Name { get; set; }
/// <summary>
/// <para type="description">Filter results to VMs in the specified status (e.g., "running", "stopped").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Status { get; set; }
/// <summary>
/// <para type="description">Filter results to VMs that have the specified tag (substring match against the semicolon-separated tags field).</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Tag { get; set; }
/// <summary>
/// <para type="description">When specified, returns only VMs that are marked as templates.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter TemplatesOnly { get; set; }
protected override void ProcessRecord()
{
var session = GetSession();
var service = new VmService();
IEnumerable<PveVm> vms = service.GetVms(session, Node);
if (VmId.HasValue)
vms = vms.Where(v => v.VmId == VmId.Value);
if (!string.IsNullOrEmpty(Name))
vms = vms.Where(v => v.Name != null &&
v.Name.IndexOf(Name, System.StringComparison.OrdinalIgnoreCase) >= 0);
if (!string.IsNullOrEmpty(Status))
vms = vms.Where(v => string.Equals(v.Status, Status, System.StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(Tag))
vms = vms.Where(v => v.Tags != null &&
v.Tags.IndexOf(Tag, System.StringComparison.OrdinalIgnoreCase) >= 0);
if (TemplatesOnly.IsPresent)
vms = vms.Where(v => v.Template == 1);
foreach (var vm in vms)
WriteObject(vm);
}
}
}
@@ -0,0 +1,41 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Gets the configuration of a QEMU/KVM virtual machine.</para>
/// <para type="description">
/// Retrieves the full hardware and metadata configuration of the specified virtual machine
/// from the Proxmox VE API, including CPU, memory, disk, network, and cloud-init settings.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Get, "PveVmConfig")]
[OutputType(typeof(PveVmConfig))]
public sealed class GetPveVmConfigCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM whose configuration to retrieve. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
protected override void ProcessRecord()
{
var session = GetSession();
var vmService = new VmService();
var config = vmService.GetVmConfig(session, Node, VmId);
WriteObject(config);
}
}
}
@@ -0,0 +1,73 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Migrates a QEMU/KVM virtual machine to a different Proxmox VE node.</para>
/// <para type="description">
/// Performs a live or offline migration of the specified virtual machine to the target node.
/// Use -Online for live migration (VM remains running). Use -Wait to block until the
/// migration task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Move, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class MovePveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM currently resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to migrate. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">The destination node to migrate the VM to.</para>
/// </summary>
[Parameter(Mandatory = true)]
public string TargetNode { get; set; } = string.Empty;
/// <summary>
/// <para type="description">
/// When specified, performs a live migration so the VM remains running during migration.
/// Requires shared storage between the source and target nodes.
/// </para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Online { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the migration task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} from node '{Node}' to node '{TargetNode}'", "Move-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.MigrateVm(session, Node, VmId, TargetNode, Online.IsPresent);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Creates a new QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Creates a new virtual machine using the Proxmox VE API. Supports common hardware
/// configuration options. Use -Wait to block until the creation task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.New, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class NewPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">The node on which to create the VM.</para>
/// </summary>
[Parameter(Mandatory = true, Position = 0)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The VM ID to assign. When omitted, the next available ID is used.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? VmId { get; set; }
/// <summary>
/// <para type="description">The display name of the VM.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Name { get; set; }
/// <summary>
/// <para type="description">Memory size in MiB.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Memory { get; set; }
/// <summary>
/// <para type="description">Number of CPU cores per socket.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Cores { get; set; }
/// <summary>
/// <para type="description">Number of CPU sockets.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Sockets { get; set; }
/// <summary>
/// <para type="description">Emulated CPU type (e.g., "host", "x86-64-v2-AES").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? CpuType { get; set; }
/// <summary>
/// <para type="description">BIOS type: "seabios" (default) or "ovmf" (UEFI).</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Bios { get; set; }
/// <summary>
/// <para type="description">Emulated machine type (e.g., "q35", "i440fx").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Machine { get; set; }
/// <summary>
/// <para type="description">Size of the primary disk (e.g., "32G").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? DiskSize { get; set; }
/// <summary>
/// <para type="description">Storage pool for the primary disk (e.g., "local-lvm").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? DiskStorage { get; set; }
/// <summary>
/// <para type="description">Disk format (e.g., "raw", "qcow2").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? DiskFormat { get; set; }
/// <summary>
/// <para type="description">Network interface model (e.g., "virtio", "e1000").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Network { get; set; }
/// <summary>
/// <para type="description">Network bridge to attach to (e.g., "vmbr0").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Bridge { get; set; }
/// <summary>
/// <para type="description">Guest OS type hint (e.g., "l26" for Linux 2.6+, "win10").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? OsType { get; set; }
/// <summary>
/// <para type="description">When specified, starts the VM after creation.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Start { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the creation task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM on node '{Node}'", "New-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var config = new Dictionary<string, object>();
if (VmId.HasValue)
config["vmid"] = VmId.Value;
if (!string.IsNullOrEmpty(Name))
config["name"] = Name!;
if (Memory.HasValue)
config["memory"] = Memory.Value;
if (Cores.HasValue)
config["cores"] = Cores.Value;
if (Sockets.HasValue)
config["sockets"] = Sockets.Value;
if (!string.IsNullOrEmpty(CpuType))
config["cpu"] = CpuType!;
if (!string.IsNullOrEmpty(Bios))
config["bios"] = Bios!;
if (!string.IsNullOrEmpty(Machine))
config["machine"] = Machine!;
if (!string.IsNullOrEmpty(OsType))
config["ostype"] = OsType!;
if (!string.IsNullOrEmpty(DiskStorage) && !string.IsNullOrEmpty(DiskSize))
{
var diskValue = $"{DiskStorage}:{DiskSize}";
if (!string.IsNullOrEmpty(DiskFormat))
diskValue += $",format={DiskFormat}";
config["virtio0"] = diskValue;
}
if (!string.IsNullOrEmpty(Bridge))
{
var netModel = string.IsNullOrEmpty(Network) ? "virtio" : Network!;
config["net0"] = $"{netModel},bridge={Bridge}";
}
if (Start.IsPresent)
config["start"] = "1";
var task = vmService.CreateVm(session, Node, config);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,75 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Removes a QEMU/KVM virtual machine from a Proxmox VE node.</para>
/// <para type="description">
/// Deletes a virtual machine and, optionally, all associated disk images.
/// This operation is destructive and requires confirmation unless -Force is specified.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Remove, "PveVm",
SupportsShouldProcess = true,
ConfirmImpact = ConfirmImpact.High)]
[OutputType(typeof(PveTask))]
public sealed class RemovePveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to remove. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">
/// When specified, also removes the VM from HA resource configuration and replication jobs.
/// </para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Purge { get; set; }
/// <summary>
/// <para type="description">
/// When specified, bypasses locks and forces removal even if a lock is set on the VM.
/// </para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Force { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the removal task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Remove-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.RemoveVm(session, Node, VmId, Purge.IsPresent);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,59 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Resets (hard-resets) a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Sends a hard-reset signal to the specified virtual machine via the Proxmox VE API.
/// This is equivalent to pressing the physical reset button on a machine — no graceful
/// shutdown occurs. Use Restart-PveVm for a graceful reboot.
/// Use -Wait to block until the reset task completes.
/// </para>
/// </summary>
[Cmdlet("Reset", "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class ResetPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to reset. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the reset task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Reset-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.ResetVm(session, Node, VmId);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,77 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Resizes a disk attached to a QEMU/KVM virtual machine.</para>
/// <para type="description">
/// Resizes the specified disk on a virtual machine via the Proxmox VE API.
/// Use an absolute size (e.g., "50G") to set a fixed size, or a relative size
/// prefixed with "+" (e.g., "+10G") to grow the disk by that amount.
/// Disk shrinking is not supported by Proxmox VE.
/// Use -Wait to block until the resize task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Resize, "PveVmDisk", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class ResizePveVmDiskCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM whose disk should be resized. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">
/// The disk slot to resize (e.g., "virtio0", "scsi0", "ide0", "sata0").
/// </para>
/// </summary>
[Parameter(Mandatory = true)]
public string Disk { get; set; } = string.Empty;
/// <summary>
/// <para type="description">
/// The new size for the disk. Use an absolute value (e.g., "50G") to set a specific size,
/// or a "+" prefix (e.g., "+10G") to grow the disk by the specified amount.
/// </para>
/// </summary>
[Parameter(Mandatory = true)]
public string Size { get; set; } = string.Empty;
/// <summary>
/// <para type="description">When specified, waits for the resize task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"Disk '{Disk}' on VM {VmId} (node '{Node}') to size '{Size}'", "Resize-PveVmDisk"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.ResizeDisk(session, Node, VmId, Disk, Size);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,71 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Gracefully restarts a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Performs a graceful shutdown of the VM followed by a start, via the Proxmox VE API.
/// A configurable timeout controls how long to wait for the guest to shut down cleanly
/// before the operation is considered failed. Use -Wait to block until both tasks complete.
/// </para>
/// </summary>
[Cmdlet(VerbsLifecycle.Restart, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class RestartPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to restart. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">
/// Timeout in seconds for the graceful shutdown phase. Defaults to 60 seconds.
/// </para>
/// </summary>
[Parameter(Mandatory = false)]
public int Timeout { get; set; } = 60;
/// <summary>
/// <para type="description">When specified, waits for both shutdown and start tasks to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Restart-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var taskService = new TaskService();
// Graceful shutdown
var shutdownTask = vmService.ShutdownVm(session, Node, VmId, Timeout);
if (Wait.IsPresent)
taskService.WaitForTask(session, shutdownTask.Node, shutdownTask.Upid, null, null, null);
// Start
var startTask = vmService.StartVm(session, Node, VmId);
if (Wait.IsPresent)
startTask = taskService.WaitForTask(session, startTask.Node, startTask.Upid, null, null, null);
WriteObject(startTask);
}
}
}
@@ -0,0 +1,57 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Resumes a suspended QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Resumes execution of a previously suspended virtual machine via the Proxmox VE API.
/// Use -Wait to block until the resume task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsLifecycle.Resume, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class ResumePveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to resume. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the resume task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Resume-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.ResumeVm(session, Node, VmId);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,118 @@
using System.Collections.Generic;
using System.Management.Automation;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Updates the configuration of a QEMU/KVM virtual machine.</para>
/// <para type="description">
/// Modifies one or more configuration settings of the specified virtual machine via the
/// Proxmox VE API. Only the parameters explicitly provided are changed; all other settings
/// are left untouched. The operation is synchronous and produces no output.
/// </para>
/// </summary>
[Cmdlet(VerbsCommon.Set, "PveVmConfig", SupportsShouldProcess = true)]
public sealed class SetPveVmConfigCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to configure. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">Number of CPU cores per socket.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Cores { get; set; }
/// <summary>
/// <para type="description">Number of CPU sockets.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Sockets { get; set; }
/// <summary>
/// <para type="description">Memory size in MiB.</para>
/// </summary>
[Parameter(Mandatory = false)]
public int? Memory { get; set; }
/// <summary>
/// <para type="description">Emulated CPU type (e.g., "host", "x86-64-v2-AES").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? CpuType { get; set; }
/// <summary>
/// <para type="description">Human-readable description / notes for the VM.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Description { get; set; }
/// <summary>
/// <para type="description">Semicolon-separated list of tags to assign to the VM.</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Tags { get; set; }
/// <summary>
/// <para type="description">BIOS type: "seabios" or "ovmf".</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Bios { get; set; }
/// <summary>
/// <para type="description">Emulated machine type (e.g., "q35", "i440fx").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? Machine { get; set; }
/// <summary>
/// <para type="description">Guest OS type hint (e.g., "l26", "win10").</para>
/// </summary>
[Parameter(Mandatory = false)]
public string? OsType { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Set-PveVmConfig"))
return;
var session = GetSession();
var vmService = new VmService();
var config = new Dictionary<string, object>();
if (Cores.HasValue)
config["cores"] = Cores.Value;
if (Sockets.HasValue)
config["sockets"] = Sockets.Value;
if (Memory.HasValue)
config["memory"] = Memory.Value;
if (!string.IsNullOrEmpty(CpuType))
config["cpu"] = CpuType!;
if (!string.IsNullOrEmpty(Description))
config["description"] = Description!;
if (!string.IsNullOrEmpty(Tags))
config["tags"] = Tags!;
if (!string.IsNullOrEmpty(Bios))
config["bios"] = Bios!;
if (!string.IsNullOrEmpty(Machine))
config["machine"] = Machine!;
if (!string.IsNullOrEmpty(OsType))
config["ostype"] = OsType!;
vmService.SetVmConfig(session, Node, VmId, config);
}
}
}
@@ -0,0 +1,57 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Starts a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Sends a start command to the specified virtual machine via the Proxmox VE API.
/// Use -Wait to block until the start task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsLifecycle.Start, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class StartPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to start. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the start task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Start-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.StartVm(session, Node, VmId);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,59 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Stops (powers off) a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Sends a stop command to the specified virtual machine via the Proxmox VE API.
/// This is equivalent to pulling the power plug — the VM is stopped immediately without
/// a graceful shutdown. Use Restart-PveVm or a guest-initiated shutdown for graceful stops.
/// Use -Wait to block until the stop task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsLifecycle.Stop, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class StopPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to stop. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the stop task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Stop-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.StopVm(session, Node, VmId);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}
@@ -0,0 +1,58 @@
using System.Management.Automation;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
namespace PSProxmoxVE.Cmdlets.Vms
{
/// <summary>
/// <para type="synopsis">Suspends a QEMU/KVM virtual machine on a Proxmox VE node.</para>
/// <para type="description">
/// Suspends (pauses) the specified virtual machine via the Proxmox VE API.
/// The VM state is preserved in memory. Use Resume-PveVm to resume execution.
/// Use -Wait to block until the suspend task completes.
/// </para>
/// </summary>
[Cmdlet(VerbsLifecycle.Suspend, "PveVm", SupportsShouldProcess = true)]
[OutputType(typeof(PveTask))]
public sealed class SuspendPveVmCmdlet : PveCmdletBase
{
/// <summary>
/// <para type="description">
/// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property.
/// </para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public string Node { get; set; } = string.Empty;
/// <summary>
/// <para type="description">The ID of the VM to suspend. Accepts pipeline input.</para>
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)]
public int VmId { get; set; }
/// <summary>
/// <para type="description">When specified, waits for the suspend task to complete before returning.</para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Suspend-PveVm"))
return;
var session = GetSession();
var vmService = new VmService();
var task = vmService.SuspendVm(session, Node, VmId);
if (Wait.IsPresent)
{
var taskService = new TaskService();
task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null);
}
WriteObject(task);
}
}
}