Scripts/Restore/Restore-CohesityRemoteFileV2.ps1

function Restore-CohesityRemoteFileV2 {
    <#
        .SYNOPSIS
        Restores the specified files or folders from a remote backup.
        .DESCRIPTION
        Restores the specified files or folders from a remote backup. This commandlet supports only source with environment type VMware/Physical/Isilon.
        .NOTES
        Published by Cohesity.
        .LINK
        https://cohesity.github.io/cohesity-powershell-module/#/README
        .EXAMPLE
        Restore-CohesityRemoteFileV2 -TaskName "restore-file-vm" -FileName /C/data/file.txt -JobId 1234 -SourceId 843 -TargetSourceId 856 -RestoreMethod AutoDeploy -TargetVMCredential (Get-Credential)
        Restores the specified file/folder to the target VM with specified source id from the latest external target backup.
        .EXAMPLE
        Restore-CohesityRemoteFileV2 -FileName "/C/myFolder" -NewBaseDirectory "C:\temp\restore" -JobId 61592 -SourceId 3517
        Restores the specified file/folder in the same server from the latest external target backup.
        .EXAMPLE
        Restore-CohesityRemoteFileV2 -FileName "/C/myFolder" -NewBaseDirectory "C:\temp\restore" -JobId 61592 -SourceId 3517 -SnapshotId "exchjik"
        Restores the specified file/folder in the same server from the specified external target backup.
    #>


    [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $True, ConfirmImpact = "High")]
    Param(
        # Specifies if the Restore Task should continue even if the restore of some files and folders fails.
        # If specified, the Restore Task ignores errors and restores as many files and folders as possible.
        # By default, the Restore Task stops restoring if any operation fails.
        [Parameter(Mandatory = $false)]
        [switch]$ContinueOnError,
        # Specifies whether encryption should be enabled during recovery.
        [Parameter(Mandatory = $false)]
        [switch]$EncryptionEnabled,
        # Specifies the full names of the files or folders to be restored.
        [Parameter(Mandatory = $true)]
        [string]$FileName,
        # Specifies the job id that backed up the files and will be used for this restore.
        [Parameter(Mandatory = $true)]
        [ValidateRange(1, [long]::MaxValue)]
        [long]$JobId,
        [Parameter(Mandatory = $false)]
        # Specifies an optional base directory where the specified files and folders will be restored.
        # By default, files and folders are restored to their original path.
        [string]$NewBaseDirectory,
        # Specifies that any existing files and folders should not be overwritten during the restore.
        # By default, value is false.
        [Parameter(Mandatory = $false)]
        [switch]$OverwriteExisting,
        # Specifies that the Restore Task should not preserve the original attributes of the files and folders.
        # By default, value is false.
        [Parameter(Mandatory = $false)]
        [switch]$PreserveAttributes,
        # Specifies the method to recover files and folders. Method shoulb be any one of - ExistingAgent, AutoDeploy, VMTools.
        [Parameter(Mandatory = $False)][ValidateSet('ExistingAgent', 'AutoDeploy', 'VMTools')]
        [string]$RecoverMethod = 'ExistingAgent',
        # Specifies whether to save success files or not. Default value is false.
        [Parameter(Mandatory = $false)]
        [switch]$SaveSuccessFiles,
        # Specifies the snapshot id.
        # If not specified, the latest remote backup will be used.
        [Parameter(Mandatory = $false)]
        [string]$SnapshotId,
        # Specifies the id of the original protection source (that was backed up) containing the files and folders.
        [Parameter(Mandatory = $true)]
        [ValidateRange(1, [long]::MaxValue)]
        [long]$SourceId,
        # Specifies the id of the target source where the files and folders are to be restored.
        [Parameter(Mandatory = $false)]
        [ValidateRange(1, [long]::MaxValue)]
        [long]$TargetSourceId,
        # Specifies the credentials for the target VM. This is mandatory if the recoverMethod is AutoDeploy or VMTools.
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $TargetVMCredential,
        # Specifies the name of the Restore Task.
        [Parameter(Mandatory = $false)]
        [string]$TaskName = "Recover_File_" + (Get-Date -UFormat "%b_%d_%Y_%I_%M_%p")
    )
    Begin {
    }

    Process {
        $recoverMethodObj = @{
            'ExistingAgent' = 'UseExistingAgent';
            'AutoDeploy'    = 'AutoDeploy';
            'VMTools'       = 'UseHypervisorApis'
        }

        $clusterURL = '/v2/clusters'
        $clusterResult = Invoke-RestApi -Method Get -Uri $clusterURL
        $clusterId = $clusterResult.id
        $clusterIncarnationId = $clusterResult.incarnationId
        $protectionGroupId = "${clusterId}:${clusterIncarnationId}:${jobId}"

        # Check whether specified job is valid or not
        $protectionGroupUrl = "/v2/data-protect/protection-groups?ids=${protectionGroupId}"
        $protectionGroupObj = Invoke-RestApi -Method Get -Uri $protectionGroupUrl
        if (-not $protectionGroupObj.protectionGroups) {
            $errorMsg = "Cannot proceed, the job with id '$JobId' is not found."
            Write-Output $errorMsg
            CSLog -Message $errorMsg -Severity 2
            return
        }
        $protectionGroup = $protectionGroupObj.protectionGroups[0]

        # Validate provided file/folder
        $searchParams = @{
            fileParams         = @{
                searchString       = $FileName;
                sourceEnvironments = @(
                    $protectionGroup.environment
                );
                objectIds          = @(
                    $SourceId
                )
            }
            objectType         = "Files"
            protectionGroupIds = @(
                $protectionGroupId
            )
        }

        $file = $FileName
        $searchUrl = "/v2/data-protect/search/indexed-objects"
        $searchPayload = $searchParams | ConvertTo-Json -Depth 100
        $searchResult = Invoke-RestApi -Method Post -Uri $searchUrl -Body $searchPayload

        $fileObj = $searchResult.files | Where-Object { ("{0}{1}" -f $_.path, $_.name) -eq $file -or ("{0}/{1}" -f $_.path, $_.name) -eq $file -or ("{0}/{1}/" -f $_.path, $_.name) -eq $file }
        if (!$fileObj) {
            $errorMsg = "file $file not found"
            Write-Output $errorMsg
            CSLog -Message $errorMsg
            return
        }

        <#
            Check if snapshot detail is provided.
            If not, collect the latest recoverable job run information.
        #>

        if ($SnapshotId -ne $null) {
            $snapshotInfo = Find-CohesityRemoteFileSnapshot -FileName $FileName -JobId $JobId -SourceId $SourceId
            if ($snapshotInfo -and $snapshotInfo.Length -ne 0) {
                $snapshotObj = $snapshotInfo[0]

                if ($snapshotObj) {
                    $SnapshotId = $snapshotObj.objectSnapshotid
                }
                else {
                    $errorMsg = "Could not fetch remote snapshot information for the file $FileName"
                    Write-Output $errorMsg
                    CSLog -Message $errorMsg -Severity 2
                    return
                }
            }
            else {
                $errorMsg = "Could not fetch snapshot information for the file $FileName"
                Write-Output $errorMsg
                CSLog -Message $errorMsg -Severity 2
                return
            }
        }
            
        # Construct payload for restore
        $absolutePath = $(if ($fileObj[0].path -eq '/') { "/{0}" -f $fileObj[0].name } else { "{0}/{1}" -f $fileObj[0].path, $fileObj[0].name })
        $isDirectory = $(if ($fileObj[0].type -eq 'Directory') { $true } else { $false })

        $recoverToOriginalPath = $true
        if ($NewBaseDirectory) {
            $recoverToOriginalPath = $false
        }

        $restoreToNewSource = $false
        if ($TargetSourceId -and $TargetSourceId -ne $SourceId) {
            $restoreToNewSource = $true
            if (!$NewBaseDirectory ) {
                $NewBaseDirectory = "/tmp/recover_files_/" + (Get-Date -UFormat "%b_%d_%Y_%I_%M_%p")
            }
        }

        $payload = @{
            name                = $TaskName
            snapshotEnvironment = $protectionGroup.environment
        }

        # Source with environment type VMware
        switch ($protectionGroup.environment) {
            'kVMware' {
                $payload['vmwareParams'] = @{}
                $payload['vmwareParams']['objects'] = @(
                    @{
                        snapshotId = $SnapshotId
                    }
                )
                $payload['vmwareParams']['recoveryAction'] = 'RecoverFiles'
                $payload['vmwareParams']['recoverFileAndFolderParams'] = @{
                    filesAndFolders    = @(
                        @{
                            absolutePath = $absolutePath;
                            isDirectory  = $isDirectory
                        }
                    );
                    targetEnvironment  = 'kVMware';
                    vmwareTargetParams = @{
                        recoverToOriginalTarget = !$restoreToNewSource;
                        overwriteExisting       = $OverwriteExisting.IsPresent;
                        preserveAttributes      = $PreserveAttributes.IsPresent;
                        continueOnError         = $ContinueOnError.IsPresent                            
                    };
                }

                if ($recoverMethod -ne 'ExistingAgent') {
                    if (!$TargetVMCredential) {
                        $warningMsg = "VM credentials required for 'AutoDeploy' and 'VMTools' restore methods."
                        Write-Warning $warningMsg
                        $username = Read-Host -Prompt "Enter username of VM"
                        $password = Read-Host -Prompt "Enter password for VM user ($username)" -AsSecureString
                        $TargetVMCredential = New-Object System.Management.Automation.PSCredential ($userName, $password)
                    }
                }

                if ($restoreToNewSource) {
                    $payload.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams['newTargetConfig'] = @{
                        absolutePath  = $NewBaseDirectory;
                        recoverMethod = $recoverMethodObj[$recoverMethod];
                        targetVm      = @{
                            id = $TargetSourceId
                        }
                    }
                    if ($recoverMethod -ne 'ExistingAgent') {
                        $payload.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.newTargetConfig["targetVmCredentials"] = @{
                            username = $TargetVMCredential.UserName;
                            password = $TargetVMCredential.GetNetworkCredential().Password
                        }
                    }
                }
                else {
                    $payload.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams['originalTargetConfig'] = @{
                        recoverMethod         = $recoverMethodObj[$recoverMethod];
                        recoverToOriginalPath = $recoverToOriginalPath;
                        alternatePath         = $(if (!$recoverToOriginalPath) { $NewBaseDirectory } else { $null });
                    }
                    if ($recoverMethod -ne 'ExistingAgent') {
                        $payload.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.originalTargetConfig["targetVmCredentials"] = @{
                            username = $TargetVMCredential.UserName;
                            password = $TargetVMCredential.GetNetworkCredential().Password
                        }
                    }
                }
            }
            'kPhysical' {
                $payload['physicalParams'] = @{}
                $payload['physicalParams']['objects'] = @(
                    @{
                        snapshotId = $SnapshotId
                    }
                )
                $payload['physicalParams']['recoveryAction'] = 'RecoverFiles'
                $payload['physicalParams']['recoverFileAndFolderParams'] = @{
                    filesAndFolders      = @(
                        @{
                            absolutePath = $absolutePath;
                            isDirectory  = $isDirectory
                        }
                    );
                    targetEnvironment    = 'kPhysical';
                    physicalTargetParams = @{
                        recoverTarget             = @{
                            id = $(if ($TargetSourceId) { $TargetSourceId } else { $SourceId });
                        };
                        restoreToOriginalPaths    = $recoverToOriginalPath;
                        overwriteExisting         = $OverwriteExisting.IsPresent;
                        preserveAttributes        = $PreserveAttributes.IsPresent;
                        continueOnError           = $ContinueOnError.IsPresent;
                        saveSuccessFiles          = $SaveSuccessFiles.IsPresent;
                        alternateRestoreDirectory = $(if (!$recoverToOriginalPath) { $NewBaseDirectory } else { $null });
                    };
                }
            }
            'kIsilon' {
                $payload['isilonParams'] = @{}
                $payload['isilonParams']['objects'] = @(
                    @{
                        snapshotId = $SnapshotId
                    }
                )
                $payload['isilonParams']['recoveryAction'] = 'RecoverFiles'
                $payload['isilonParams']['recoverFileAndFolderParams'] = @{
                    filesAndFolders    = @(
                        @{
                            absolutePath = $absolutePath;
                            isDirectory  = $isDirectory
                        }
                    );
                    targetEnvironment  = 'kIsilon';
                    isilonTargetParams = @{
                        recoverToNewSource = $restoreToNewSource
                    };
                }

                if ($restoreToNewSource) {
                    $payload.isilonParams.recoverFileAndFolderParams.isilonTargetParams['newSourceConfig'] = @{
                        overwriteExistingFile  = $OverwriteExisting.IsPresent;
                        preserveFileAttributes = $PreserveAttributes.IsPresent;
                        continueOnError        = $ContinueOnError.IsPresent;
                        encryptionEnabled      = $EncryptionEnabled.IsPresent;
                        volume                 = @{
                            id = $TargetSourceId
                        }
                        alternatePath          = $NewBaseDirectory
                    }
                }
                else {
                    $payload.isilonParams.recoverFileAndFolderParams.isilonTargetParams['originalSourceConfig'] = @{
                        alternatePath          = $(if (!$recoverToOriginalPath) { $NewBaseDirectory } else { $null });
                        overwriteExistingFile  = $OverwriteExisting.IsPresent;
                        preserveFileAttributes = $PreserveAttributes.IsPresent;
                        continueOnError        = $ContinueOnError.IsPresent;
                        encryptionEnabled      = $encryptionEnabled.IsPresent;
                        recoverToOriginalPath  = $recoverToOriginalPath;
                    }
                }
            }
        }

        $restoreURL = '/v2/data-protect/recoveries'
        $payloadJson = $payload | ConvertTo-Json -Depth 100

        $resp = Invoke-RestApi -Method 'Post' -Uri $restoreURL -Body $payloadJson
        if ($Global:CohesityAPIStatus.StatusCode -eq 201) {
            $resp
        }
        else {
            $errorMsg = $Global:CohesityAPIStatus.ErrorMessage + ", File operation : Failed to recover."
            Write-Output $errorMsg
            CSLog -Message $errorMsg
        }
    }
    End {
    }
}