Public/VM/New-VergeVM.ps1
|
function New-VergeVM { <# .SYNOPSIS Creates a new virtual machine in VergeOS. .DESCRIPTION New-VergeVM creates a new virtual machine with the specified configuration. The VM is created in a stopped state by default. Use -PowerOn to start the VM immediately after creation. .PARAMETER Name The name of the new VM. Must be unique and 1-128 characters. .PARAMETER Description An optional description for the VM. .PARAMETER CPUCores The number of CPU cores for the VM. Default is 1. Range: 1-1024. .PARAMETER RAM The amount of RAM in MB. Default is 1024. Range: 256-1048576. Value will be normalized to 256 MB increments. .PARAMETER OSFamily The operating system family. Valid values: Linux, Windows, FreeBSD, Other. Default is Linux. .PARAMETER Cluster The name or key of the cluster to place the VM in. .PARAMETER CPUType The CPU type to emulate. Common values include: - host: Use host CPU (best performance) - qemu64: Generic QEMU CPU - Haswell, Broadwell, Skylake-Server: Intel CPU types - EPYC, EPYC-Rome, EPYC-Milan: AMD CPU types .PARAMETER MachineType The machine/chipset type. Default is 'pc-q35-10.0' (Q35 + ICH9). Use 'pc' for legacy i440FX compatibility. .PARAMETER UEFI Enable UEFI boot instead of legacy BIOS. .PARAMETER SecureBoot Enable Secure Boot (requires UEFI). .PARAMETER GuestAgent Enable QEMU Guest Agent support for enhanced VM management. .PARAMETER Console The console type. Valid values: VNC, Spice, Serial, None. Default is VNC. .PARAMETER Video The video card type. Valid values: Standard, Cirrus, VMware, QXL, Virtio, None. Default is Standard. .PARAMETER BootOrder The boot device order. Valid values: - DiskCD: Disk, then CD-ROM (default) - DiskCDNetwork: Disk, CD-ROM, Network - CDDisk: CD-ROM, then Disk - NetworkDisk: Network, then Disk - Network: Network only - Disk: Disk only - CD: CD-ROM only .PARAMETER SnapshotProfile The name or key of a snapshot profile to assign. .PARAMETER Enabled Whether the VM is enabled. Default is $true. .PARAMETER PowerOn Start the VM immediately after creation. .PARAMETER PassThru Return the created VM object. .PARAMETER Server The VergeOS connection to use. Defaults to the current default connection. .EXAMPLE New-VergeVM -Name "WebServer01" Creates a VM with default settings (1 CPU, 1GB RAM, Linux). .EXAMPLE New-VergeVM -Name "WebServer01" -CPUCores 4 -RAM 8192 -OSFamily Linux Creates a Linux VM with 4 cores and 8GB RAM. .EXAMPLE New-VergeVM -Name "WinServer" -CPUCores 4 -RAM 16384 -OSFamily Windows -UEFI -PowerOn Creates a Windows VM with UEFI and starts it immediately. .EXAMPLE New-VergeVM -Name "Database01" -CPUCores 8 -RAM 32768 -GuestAgent -SnapshotProfile "Hourly" -PassThru Creates a VM with guest agent and snapshot profile, returning the VM object. .EXAMPLE $vmParams = @{ Name = "AppServer" CPUCores = 4 RAM = 8192 OSFamily = "Linux" UEFI = $true GuestAgent = $true Cluster = "Production" } New-VergeVM @vmParams -PassThru Creates a VM using splatting for cleaner parameter passing. .OUTPUTS None by default. Verge.VM when -PassThru is specified. .NOTES The VM is created without drives or NICs. Use New-VergeDrive and New-VergeNIC to add storage and networking after creation. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [ValidateLength(1, 128)] [string]$Name, [Parameter()] [ValidateLength(0, 2048)] [string]$Description, [Parameter()] [ValidateRange(1, 1024)] [int]$CPUCores = 1, [Parameter()] [ValidateRange(256, 1048576)] [int]$RAM = 1024, [Parameter()] [ValidateSet('Linux', 'Windows', 'FreeBSD', 'Other')] [string]$OSFamily = 'Linux', [Parameter()] [string]$Cluster, [Parameter()] [string]$CPUType, [Parameter()] [string]$MachineType, [Parameter()] [switch]$UEFI, [Parameter()] [switch]$SecureBoot, [Parameter()] [switch]$GuestAgent, [Parameter()] [ValidateSet('VNC', 'Spice', 'Serial', 'None')] [string]$Console = 'VNC', [Parameter()] [ValidateSet('Standard', 'Cirrus', 'VMware', 'QXL', 'Virtio', 'None')] [string]$Video = 'Standard', [Parameter()] [ValidateSet('DiskCD', 'DiskCDNetwork', 'CDDisk', 'NetworkDisk', 'Network', 'Disk', 'CD')] [string]$BootOrder = 'DiskCD', [Parameter()] [string]$SnapshotProfile, [Parameter()] [bool]$Enabled = $true, [Parameter()] [switch]$PowerOn, [Parameter()] [switch]$PassThru, [Parameter()] [object]$Server ) begin { # Resolve connection if (-not $Server) { $Server = $script:DefaultConnection } if (-not $Server) { throw [System.InvalidOperationException]::new( 'Not connected to VergeOS. Use Connect-VergeOS to establish a connection.' ) } # Map friendly names to API values $osFamilyMap = @{ 'Linux' = 'linux' 'Windows' = 'windows' 'FreeBSD' = 'freebsd' 'Other' = 'other' } $consoleMap = @{ 'VNC' = 'vnc' 'Spice' = 'spice' 'Serial' = 'serial' 'None' = 'none' } $videoMap = @{ 'Standard' = 'std' 'Cirrus' = 'cirrus' 'VMware' = 'vmware' 'QXL' = 'qxl' 'Virtio' = 'virtio' 'None' = 'none' } $bootOrderMap = @{ 'DiskCD' = 'cd' 'DiskCDNetwork' = 'cdn' 'CDDisk' = 'dc' 'NetworkDisk' = 'nc' 'Network' = 'n' 'Disk' = 'c' 'CD' = 'd' } } process { # Normalize RAM to 256 MB increments $normalizedRAM = [Math]::Ceiling($RAM / 256) * 256 if ($normalizedRAM -ne $RAM) { Write-Verbose "RAM normalized from $RAM MB to $normalizedRAM MB (256 MB increments)" } # Build request body with required and specified fields $body = @{ name = $Name cpu_cores = $CPUCores ram = $normalizedRAM os_family = $osFamilyMap[$OSFamily] enabled = $Enabled console = $consoleMap[$Console] video = $videoMap[$Video] boot_order = $bootOrderMap[$BootOrder] } # Add optional parameters if ($Description) { $body['description'] = $Description } if ($UEFI) { $body['uefi'] = $true } if ($SecureBoot) { if (-not $UEFI) { Write-Warning "Secure Boot requires UEFI. Enabling UEFI automatically." $body['uefi'] = $true } $body['secure_boot'] = $true } if ($GuestAgent) { $body['guest_agent'] = $true } if ($CPUType) { $body['cpu_type'] = $CPUType } if ($MachineType) { $body['machine_type'] = $MachineType } # Resolve cluster if specified if ($Cluster) { # Check if it's a numeric key or name if ($Cluster -match '^\d+$') { $body['cluster'] = [int]$Cluster } else { # Look up cluster by name try { $clusterResponse = Invoke-VergeAPI -Method GET -Endpoint 'clusters' -Query @{ filter = "name eq '$Cluster'" fields = '$key,name' } -Connection $Server if ($clusterResponse -and $clusterResponse.'$key') { $body['cluster'] = $clusterResponse.'$key' } elseif ($clusterResponse -is [array] -and $clusterResponse.Count -gt 0) { $body['cluster'] = $clusterResponse[0].'$key' } else { throw "Cluster '$Cluster' not found" } } catch { throw "Failed to resolve cluster '$Cluster': $($_.Exception.Message)" } } } # Resolve snapshot profile if specified if ($SnapshotProfile) { if ($SnapshotProfile -match '^\d+$') { $body['snapshot_profile'] = [int]$SnapshotProfile } else { try { $profileResponse = Invoke-VergeAPI -Method GET -Endpoint 'snapshot_profiles' -Query @{ filter = "name eq '$SnapshotProfile'" fields = '$key,name' } -Connection $Server if ($profileResponse -and $profileResponse.'$key') { $body['snapshot_profile'] = $profileResponse.'$key' } elseif ($profileResponse -is [array] -and $profileResponse.Count -gt 0) { $body['snapshot_profile'] = $profileResponse[0].'$key' } else { throw "Snapshot profile '$SnapshotProfile' not found" } } catch { throw "Failed to resolve snapshot profile '$SnapshotProfile': $($_.Exception.Message)" } } } # Confirm action $actionDescription = "Create VM '$Name' with $CPUCores CPU(s), $($normalizedRAM)MB RAM, $OSFamily" if ($PSCmdlet.ShouldProcess($Name, 'Create VM')) { try { Write-Verbose "Creating VM '$Name'" $response = Invoke-VergeAPI -Method POST -Endpoint 'vms' -Body $body -Connection $Server # Get the created VM key $vmKey = $response.'$key' if (-not $vmKey -and $response.key) { $vmKey = $response.key } Write-Verbose "VM '$Name' created with Key: $vmKey" # Power on if requested if ($PowerOn -and $vmKey) { Write-Verbose "Powering on VM '$Name'" $powerBody = @{ vm = $vmKey action = 'poweron' } Invoke-VergeAPI -Method POST -Endpoint 'vm_actions' -Body $powerBody -Connection $Server | Out-Null } if ($PassThru -and $vmKey) { # Return the created VM Start-Sleep -Milliseconds 500 Get-VergeVM -Key $vmKey -Server $Server } } catch { $errorMessage = $_.Exception.Message if ($errorMessage -match 'already in use') { throw "A VM with the name '$Name' already exists." } throw "Failed to create VM '$Name': $errorMessage" } } } } |