Public/VM/Restore-VergeVMSnapshot.ps1
|
function Restore-VergeVMSnapshot { <# .SYNOPSIS Restores a VergeOS virtual machine snapshot. .DESCRIPTION Restore-VergeVMSnapshot restores a VM from a snapshot. By default, it creates a NEW VM from the snapshot (clone). Use -ReplaceOriginal to revert the original VM to the snapshot state (destructive - all changes since snapshot are lost). .PARAMETER Snapshot A snapshot object from Get-VergeVMSnapshot. Accepts pipeline input. .PARAMETER SnapshotKey The key (ID) of the snapshot to restore. Alternative to pipeline input. .PARAMETER VMName The name of the VM to get the snapshot from. .PARAMETER VMKey The key (ID) of the VM to get the snapshot from. .PARAMETER Name The name of the snapshot to restore. Use with -VMName or -VMKey. .PARAMETER ReplaceOriginal Restore OVER the original VM instead of creating a new VM. WARNING: This reverts the original VM to snapshot state. All changes made after the snapshot was taken will be LOST. The VM must be powered off for this to work. .PARAMETER NewName The name for the restored VM (only used when creating a new VM). If not specified, defaults to "<SnapshotName> restored". .PARAMETER PowerOn Power on the VM after restoration. .PARAMETER PassThru Return the VM object after restoration. .PARAMETER Server The VergeOS connection to use. Defaults to the current default connection. .EXAMPLE Restore-VergeVMSnapshot -VMName "WebServer01" -Name "Pre-Update" Creates a new VM named "Pre-Update restored" from the snapshot. .EXAMPLE Restore-VergeVMSnapshot -VMName "WebServer01" -Name "Pre-Update" -ReplaceOriginal Reverts WebServer01 to the "Pre-Update" snapshot state (VM must be off). .EXAMPLE Restore-VergeVMSnapshot -VMName "WebServer01" -Name "Pre-Update" -NewName "WebServer01-Restored" Creates a new VM with a custom name from the snapshot. .EXAMPLE Get-VergeVMSnapshot -VMName "WebServer01" -Name "Pre-Update" | Restore-VergeVMSnapshot -PowerOn Restores using pipeline input and powers on the new VM. .EXAMPLE Restore-VergeVMSnapshot -SnapshotKey 123 -NewName "RecoveredVM" Restores a snapshot directly by its key to a new VM. .EXAMPLE Get-VergeVMSnapshot -VMName "Database01" | Sort-Object Created -Descending | Select-Object -First 1 | Restore-VergeVMSnapshot -ReplaceOriginal Reverts Database01 to its most recent snapshot. .OUTPUTS None by default. Verge.VM when -PassThru is specified. .NOTES Default behavior creates a NEW VM from the snapshot (original VM unchanged). Use -ReplaceOriginal to revert the original VM to snapshot state. When using -ReplaceOriginal, the VM must be powered off. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High', DefaultParameterSetName = 'BySnapshot')] [OutputType([PSCustomObject])] param( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'BySnapshot')] [PSTypeName('Verge.VMSnapshot')] [PSCustomObject]$Snapshot, [Parameter(Mandatory, ParameterSetName = 'BySnapshotKey')] [int]$SnapshotKey, [Parameter(Mandatory, ParameterSetName = 'ByVMAndName')] [string]$VMName, [Parameter(Mandatory, ParameterSetName = 'ByVMKeyAndName')] [int]$VMKey, [Parameter(Mandatory, ParameterSetName = 'ByVMAndName')] [Parameter(Mandatory, ParameterSetName = 'ByVMKeyAndName')] [string]$Name, [Parameter()] [switch]$ReplaceOriginal, [Parameter()] [ValidateLength(1, 128)] [string]$NewName, [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.' ) } } process { # Resolve snapshot based on parameter set $targetSnapshot = switch ($PSCmdlet.ParameterSetName) { 'BySnapshot' { $Snapshot } 'BySnapshotKey' { # Query the snapshot directly by key $queryParams = @{ filter = "`$key eq $SnapshotKey" fields = '$key,name,description,created,expires,quiesced,created_manually,machine,snap_machine' } $snapResponse = Invoke-VergeAPI -Method GET -Endpoint 'machine_snapshots' -Query $queryParams -Connection $Server if ($snapResponse) { $snap = if ($snapResponse -is [array]) { $snapResponse[0] } else { $snapResponse } # Build a minimal snapshot object [PSCustomObject]@{ PSTypeName = 'Verge.VMSnapshot' Key = [int]$snap.'$key' Name = $snap.name SnapMachineKey = $snap.snap_machine MachineKey = $snap.machine } } } 'ByVMAndName' { Get-VergeVMSnapshot -VMName $VMName -Name $Name -Server $Server | Select-Object -First 1 } 'ByVMKeyAndName' { Get-VergeVMSnapshot -VMKey $VMKey -Name $Name -Server $Server | Select-Object -First 1 } } if (-not $targetSnapshot) { Write-Error -Message "Snapshot not found" -ErrorId 'SnapshotNotFound' return } # Validate we have the snap_machine key if (-not $targetSnapshot.SnapMachineKey) { Write-Error -Message "Snapshot '$($targetSnapshot.Name)' does not have a valid snap_machine reference" -ErrorId 'InvalidSnapshot' return } # Handle -ReplaceOriginal mode (restore over source VM) if ($ReplaceOriginal) { # Get the original VM to check its state $originalVM = if ($targetSnapshot._VM) { $targetSnapshot._VM } elseif ($targetSnapshot.VMKey) { Get-VergeVM -Key $targetSnapshot.VMKey -Server $Server } else { $null } if (-not $originalVM) { Write-Error -Message "Cannot find original VM for snapshot '$($targetSnapshot.Name)'" -ErrorId 'OriginalVMNotFound' return } # VM must be powered off for in-place restore if ($originalVM.PowerState -eq 'Running') { Write-Error -Message "Cannot restore over VM '$($originalVM.Name)': VM must be powered off. Use Stop-VergeVM first." -ErrorId 'VMRunning' return } # Build action body for in-place restore $body = @{ vm = $targetSnapshot.SnapMachineKey action = 'restore' } $warningMessage = "This will REVERT VM '$($originalVM.Name)' to snapshot '$($targetSnapshot.Name)'. All changes since this snapshot will be PERMANENTLY LOST." Write-Warning $warningMessage if ($PSCmdlet.ShouldProcess($originalVM.Name, "Revert to snapshot '$($targetSnapshot.Name)' from $($targetSnapshot.Created)")) { try { Write-Verbose "Reverting VM '$($originalVM.Name)' to snapshot '$($targetSnapshot.Name)'" $response = Invoke-VergeAPI -Method POST -Endpoint 'vm_actions' -Body $body -Connection $Server Write-Verbose "Restore command sent for VM '$($originalVM.Name)'" if ($PowerOn) { Write-Verbose "Powering on restored VM..." Start-Sleep -Seconds 2 $powerBody = @{ vm = $originalVM.Key action = 'poweron' } Invoke-VergeAPI -Method POST -Endpoint 'vm_actions' -Body $powerBody -Connection $Server | Out-Null } if ($PassThru) { Start-Sleep -Seconds 2 Get-VergeVM -Key $originalVM.Key -Server $Server } } catch { Write-Error -Message "Failed to restore VM '$($originalVM.Name)': $($_.Exception.Message)" -ErrorId 'RestoreFailed' } } } else { # Default mode: Clone snapshot to NEW VM # Determine the name for the restored VM $restoredVMName = if ($NewName) { $NewName } else { "$($targetSnapshot.Name) restored" } # Build action body - clone the snapshot VM (snap_machine) $body = @{ vm = $targetSnapshot.SnapMachineKey action = 'clone' params = @{ name = $restoredVMName } } $sourceDesc = if ($targetSnapshot.VMName) { "VM '$($targetSnapshot.VMName)'" } else { "snapshot" } if ($PSCmdlet.ShouldProcess($restoredVMName, "Create VM from snapshot '$($targetSnapshot.Name)' ($sourceDesc)")) { try { Write-Verbose "Restoring snapshot '$($targetSnapshot.Name)' to new VM '$restoredVMName'" $response = Invoke-VergeAPI -Method POST -Endpoint 'vm_actions' -Body $body -Connection $Server Write-Verbose "Restore command sent, new VM '$restoredVMName' being created" # Get the new VM key from the response $newVMKey = $response.'$key' ?? $response.key if ($PowerOn -and $newVMKey) { Write-Verbose "Powering on restored VM..." Start-Sleep -Seconds 2 $powerBody = @{ vm = $newVMKey action = 'poweron' } Invoke-VergeAPI -Method POST -Endpoint 'vm_actions' -Body $powerBody -Connection $Server | Out-Null } if ($PassThru -and $newVMKey) { Start-Sleep -Seconds 2 Get-VergeVM -Key $newVMKey -Server $Server } elseif ($PassThru) { # Try to find the new VM by name Start-Sleep -Seconds 2 Get-VergeVM -Name $restoredVMName -Server $Server | Select-Object -First 1 } } catch { Write-Error -Message "Failed to restore snapshot '$($targetSnapshot.Name)': $($_.Exception.Message)" -ErrorId 'RestoreFailed' } } } } } |