Public/VM/New-VergeVMSnapshot.ps1
|
function New-VergeVMSnapshot { <# .SYNOPSIS Creates a snapshot of a VergeOS virtual machine. .DESCRIPTION New-VergeVMSnapshot creates a point-in-time snapshot of a VM that can be used to restore the VM to its current state. Snapshots can be quiesced (requires guest agent) for application-consistent backups. .PARAMETER VM A VM object from Get-VergeVM. Accepts pipeline input. .PARAMETER VMName The name of the VM to snapshot. .PARAMETER VMKey The key (ID) of the VM to snapshot. .PARAMETER Name The name for the snapshot. If not specified, a timestamp-based name is used. .PARAMETER Description An optional description for the snapshot. .PARAMETER Retention How long to keep the snapshot. Accepts timespan strings like '24h', '7d', '1w'. Default is 24 hours (86400 seconds). Use 'Never' for no expiration. .PARAMETER Quiesce Quiesce the VM's disks before taking the snapshot. This temporarily freezes disk activity for a consistent snapshot. Requires the QEMU Guest Agent. .PARAMETER PassThru Return the created snapshot object. .PARAMETER Server The VergeOS connection to use. Defaults to the current default connection. .EXAMPLE New-VergeVMSnapshot -VMName "WebServer01" Creates a snapshot with default 24-hour retention. .EXAMPLE New-VergeVMSnapshot -VMName "WebServer01" -Name "Pre-Update" -Retention "7d" Creates a named snapshot that expires in 7 days. .EXAMPLE New-VergeVMSnapshot -VMName "Database01" -Quiesce -Retention "Never" Creates a quiesced snapshot that never expires. .EXAMPLE Get-VergeVM -Name "Prod-*" | New-VergeVMSnapshot -Name "Maintenance-$(Get-Date -Format 'yyyyMMdd')" Creates snapshots for all production VMs with a dated name. .OUTPUTS None by default. Verge.VMSnapshot when -PassThru is specified. .NOTES Quiesced snapshots require the QEMU Guest Agent to be installed and running. The VM must not be a snapshot itself. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ByVMName')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByVMObject')] [PSTypeName('Verge.VM')] [PSCustomObject]$VM, [Parameter(Mandatory, Position = 0, ParameterSetName = 'ByVMName')] [string]$VMName, [Parameter(Mandatory, ParameterSetName = 'ByVMKey')] [int]$VMKey, [Parameter()] [ValidateLength(1, 128)] [string]$Name, [Parameter()] [ValidateLength(0, 2048)] [string]$Description, [Parameter()] [string]$Retention = '24h', [Parameter()] [switch]$Quiesce, [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.' ) } } process { # Resolve VM based on parameter set $targetVMs = switch ($PSCmdlet.ParameterSetName) { 'ByVMName' { Get-VergeVM -Name $VMName -Server $Server } 'ByVMKey' { Get-VergeVM -Key $VMKey -Server $Server } 'ByVMObject' { $VM } } foreach ($targetVM in $targetVMs) { if (-not $targetVM) { continue } # Check if VM is a snapshot if ($targetVM.IsSnapshot) { Write-Error -Message "Cannot snapshot '$($targetVM.Name)': VM is already a snapshot" -ErrorId 'CannotSnapshotSnapshot' continue } # Parse retention to seconds $retentionSeconds = 86400 # Default 24 hours if ($Retention -eq 'Never' -or $Retention -eq '0') { $retentionSeconds = 0 } elseif ($Retention -match '^(\d+)([smhdw])$') { $value = [int]$Matches[1] $unit = $Matches[2] $retentionSeconds = switch ($unit) { 's' { $value } 'm' { $value * 60 } 'h' { $value * 3600 } 'd' { $value * 86400 } 'w' { $value * 604800 } } } elseif ($Retention -match '^\d+$') { $retentionSeconds = [int]$Retention } else { Write-Error -Message "Invalid retention format '$Retention'. Use format like '24h', '7d', '1w', or 'Never'." -ErrorId 'InvalidRetention' continue } # Generate snapshot name if not provided $snapshotName = if ($Name) { $Name } else { "Snapshot-$(Get-Date -Format 'yyyyMMdd-HHmmss')" } # Calculate expiration timestamp (Unix epoch) # If retention is 0 (Never), set expires to 0 (no expiration) $expiresTimestamp = if ($retentionSeconds -eq 0) { 0 } else { [int][DateTimeOffset]::UtcNow.AddSeconds($retentionSeconds).ToUnixTimeSeconds() } # Build request body for machine_snapshots endpoint $body = @{ machine = $targetVM.MachineKey name = $snapshotName created_manually = $true quiesce = $Quiesce.IsPresent } # Only include expires if there's a retention period if ($expiresTimestamp -gt 0) { $body['expires'] = $expiresTimestamp } # Format retention for display $retentionDisplay = if ($retentionSeconds -eq 0) { 'Never' } elseif ($retentionSeconds -ge 86400) { "$([math]::Round($retentionSeconds / 86400, 1)) days" } elseif ($retentionSeconds -ge 3600) { "$([math]::Round($retentionSeconds / 3600, 1)) hours" } else { "$retentionSeconds seconds" } $quiesceText = if ($Quiesce) { ', quiesced' } else { '' } if ($PSCmdlet.ShouldProcess($targetVM.Name, "Create snapshot '$snapshotName' (retention: $retentionDisplay$quiesceText)")) { try { Write-Verbose "Creating snapshot '$snapshotName' for VM '$($targetVM.Name)'" $response = Invoke-VergeAPI -Method POST -Endpoint 'machine_snapshots' -Body $body -Connection $Server Write-Verbose "Snapshot '$snapshotName' created for VM '$($targetVM.Name)'" if ($PassThru) { # Wait for snapshot to be created and return it Start-Sleep -Seconds 2 Get-VergeVMSnapshot -VMKey $targetVM.Key -Server $Server | Sort-Object Created -Descending | Select-Object -First 1 } } catch { Write-Error -Message "Failed to create snapshot for VM '$($targetVM.Name)': $($_.Exception.Message)" -ErrorId 'SnapshotFailed' } } } } } |