Public/Backup/Restore-VergeTenantFromCloudSnapshot.ps1

function Restore-VergeTenantFromCloudSnapshot {
    <#
    .SYNOPSIS
        Restores a tenant from a cloud (system) snapshot in VergeOS.

    .DESCRIPTION
        Restore-VergeTenantFromCloudSnapshot recovers a tenant from a cloud snapshot.
        This creates a new tenant from the snapshot data, it does not overwrite
        any existing tenant.

        Use Get-VergeCloudSnapshot -IncludeTenants to see available tenants in a snapshot.

    .PARAMETER CloudSnapshot
        A cloud snapshot object from Get-VergeCloudSnapshot. Accepts pipeline input.

    .PARAMETER CloudSnapshotKey
        The key (ID) of the cloud snapshot containing the tenant.

    .PARAMETER CloudSnapshotName
        The name of the cloud snapshot containing the tenant.

    .PARAMETER TenantName
        The name of the tenant within the cloud snapshot to restore.

    .PARAMETER TenantKey
        The key of the tenant within the cloud snapshot (from cloud_snapshot_tenants).

    .PARAMETER NewName
        Optional new name for the restored tenant. If not specified, the original name
        is used (which may conflict with an existing tenant).

    .PARAMETER Server
        The VergeOS connection to use. Defaults to the current default connection.

    .EXAMPLE
        Restore-VergeTenantFromCloudSnapshot -CloudSnapshotName "Daily_20260123" -TenantName "CustomerA"

        Restores the tenant "CustomerA" from the specified cloud snapshot.

    .EXAMPLE
        Get-VergeCloudSnapshot -Name "Midnight*" | Restore-VergeTenantFromCloudSnapshot -TenantName "Production" -NewName "Production-DR"

        Restores the tenant with a new name from the first matching cloud snapshot.

    .OUTPUTS
        PSCustomObject with recovery status information.

    .NOTES
        The recovered tenant will be created as a new tenant. If a tenant with the same name
        already exists, you should specify -NewName to avoid conflicts.

        This operation may take significant time depending on tenant size and complexity.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'BySnapshotName')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'ByObject')]
        [PSTypeName('Verge.CloudSnapshot')]
        [PSCustomObject]$CloudSnapshot,

        [Parameter(Mandatory, ParameterSetName = 'BySnapshotKey')]
        [int]$CloudSnapshotKey,

        [Parameter(Mandatory, ParameterSetName = 'BySnapshotName')]
        [string]$CloudSnapshotName,

        [Parameter(ParameterSetName = 'ByObject')]
        [Parameter(ParameterSetName = 'BySnapshotKey')]
        [Parameter(ParameterSetName = 'BySnapshotName')]
        [string]$TenantName,

        [Parameter()]
        [int]$TenantKey,

        [Parameter()]
        [string]$NewName,

        [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 the cloud snapshot
        $targetSnapshot = switch ($PSCmdlet.ParameterSetName) {
            'ByObject' { $CloudSnapshot }
            'BySnapshotKey' { Get-VergeCloudSnapshot -Key $CloudSnapshotKey -IncludeExpired -Server $Server }
            'BySnapshotName' { Get-VergeCloudSnapshot -Name $CloudSnapshotName -IncludeExpired -Server $Server | Select-Object -First 1 }
        }

        if (-not $targetSnapshot) {
            $identifier = if ($CloudSnapshotKey) { "Key: $CloudSnapshotKey" } else { "Name: $CloudSnapshotName" }
            Write-Error -Message "Cloud snapshot not found ($identifier)" -ErrorId 'CloudSnapshotNotFound'
            return
        }

        $snapshotKey = $targetSnapshot.Key
        $snapshotName = $targetSnapshot.Name

        # Find the tenant within the snapshot
        if (-not $TenantKey -and -not $TenantName) {
            Write-Error -Message "Either -TenantName or -TenantKey must be specified" -ErrorId 'TenantNotSpecified'
            return
        }

        # Query cloud_snapshot_tenants to find the tenant
        $tenantParams = @{
            'fields' = @(
                '$key'
                'name'
                'description'
                'original_key'
                'status'
            ) -join ','
            'filter' = "cloud_snapshot eq $snapshotKey"
        }

        if ($TenantKey) {
            $tenantParams['filter'] += " and `$key eq $TenantKey"
        }
        elseif ($TenantName) {
            $tenantParams['filter'] += " and name eq '$TenantName'"
        }

        try {
            $tenantResponse = Invoke-VergeAPI -Method GET -Endpoint 'cloud_snapshot_tenants' -Query $tenantParams -Connection $Server
            $snapshotTenants = if ($tenantResponse -is [array]) { $tenantResponse } elseif ($tenantResponse) { @($tenantResponse) } else { @() }

            if ($snapshotTenants.Count -eq 0) {
                $tenantIdentifier = if ($TenantKey) { "Key: $TenantKey" } else { "Name: $TenantName" }
                Write-Error -Message "Tenant not found in cloud snapshot '$snapshotName' ($tenantIdentifier)" -ErrorId 'TenantNotFoundInSnapshot'
                return
            }

            foreach ($tenant in $snapshotTenants) {
                $tenantDisplayName = $tenant.name
                $tenantSnapshotKey = $tenant.'$key'

                $restoreName = if ($NewName) { $NewName } else { $tenantDisplayName }

                if ($PSCmdlet.ShouldProcess("Tenant '$tenantDisplayName' from Cloud Snapshot '$snapshotName'", 'Restore')) {
                    Write-Verbose "Restoring tenant '$tenantDisplayName' from cloud snapshot '$snapshotName' (Tenant Snapshot Key: $tenantSnapshotKey)"

                    # Build the recover action
                    $body = @{
                        action = 'recover'
                        params = @{
                            rows = @($tenantSnapshotKey)
                        }
                    }

                    if ($NewName) {
                        $body['params']['name'] = $NewName
                    }

                    try {
                        $response = Invoke-VergeAPI -Method POST -Endpoint 'cloud_snapshot_tenant_actions' -Body $body -Connection $Server

                        # Create output object
                        $output = [PSCustomObject]@{
                            PSTypeName           = 'Verge.CloudSnapshotTenantRestore'
                            CloudSnapshotKey     = $snapshotKey
                            CloudSnapshotName    = $snapshotName
                            TenantSnapshotKey    = $tenantSnapshotKey
                            TenantName           = $tenantDisplayName
                            RestoredAs           = $restoreName
                            Status               = 'Initiated'
                            Response             = $response
                        }

                        if ($response -and $response.task) {
                            $output | Add-Member -MemberType NoteProperty -Name 'TaskKey' -Value $response.task -Force
                        }

                        Write-Output $output
                        Write-Verbose "Tenant restore initiated for '$tenantDisplayName'"
                    }
                    catch {
                        Write-Error -Message "Failed to restore tenant '$tenantDisplayName' from cloud snapshot: $($_.Exception.Message)" -ErrorId 'RestoreTenantFailed'
                    }
                }
            }
        }
        catch {
            Write-Error -Message "Failed to query tenants in cloud snapshot '$snapshotName': $($_.Exception.Message)" -ErrorId 'QuerySnapshotTenantsFailed'
        }
    }
}