Scripts/Restore/Restore-CohesityFileV2.ps1
function Restore-CohesityFileV2 { <# .SYNOPSIS Restores the specified files or folders from a previous backup based on Cohesity V2 Rest APIs .DESCRIPTION Restores the specified files or folders from a previous backup based on Cohesity V2 Rest APIs This script offers the -noIndex ($isDirectory = $True) parameter If the VM is not indexed. In this case, Most of the time the file/folder requested to restore is from a job run that is still in the indexing process using V2 apis. Restore is throwing errors if the VM is not indexed while using Restore-CohesityFile cmdlet which is based on V1 apis If the VM files/folders are indexed properly then use the Restore-CohesityFile cmdlet directly .NOTES Published by Cohesity .LINK https://cohesity.github.io/cohesity-powershell-module/#/README .EXAMPLE Restore-CohesityFileV2 -sourceVM "SQL-UT-VM2" -targetVM "SQL-UT-VM2" -fileNames "C:\.rnd" -restorePath "C:\" -wait .EXAMPLE Restore-CohesityFileV2 -sourceVM "SQL-UT-VM2" -targetVM "SQL-UT-VMD2" -fileNames "C:\Users\" -restorePath "C:\temp\" -wait .EXAMPLE Restore-CohesityFileV2 -sourceVM "SQL-UT-VM2" -targetVM "SQL-UT-VM2" -fileNames "C:\.rnd" -restorePath "C:\" -taskName "Test_task" -wait #> [CmdletBinding(DefaultParameterSetName = "Default", SupportsShouldProcess = $True, ConfirmImpact = "High")] param ( [Parameter(Mandatory = $True)] # Name of the Source VM, the filles need to be picked up for restore [string]$sourceVM, [Parameter(Mandatory = $False)] # Name of the Target V, the files need to be restored [string]$targetVM, [Parameter(Mandatory = $False)] # One or more file paths to be restored [array]$fileNames, [Parameter(Mandatory = $False)] # Task name of the restore job [string]$taskName, [Parameter(Mandatory = $False)] # text file of file paths [string]$fileList, [Parameter(Mandatory = $False)] # UserName for VMTools, applicable only when restoreMethod selected as VMTools [string]$vmUser, [Parameter(Mandatory = $False)] # Password for vm tools, applicable only when restoreMethod selected as VMTools [string]$vmPwd, [Parameter(Mandatory = $False)] # Alternate path to restore files in the target VM [string]$restorePath, [Parameter(Mandatory = $False)][ValidateSet('ExistingAgent','AutoDeploy','VMTools')] # Different categories of the restore Job through different tools [string]$restoreMethod = 'ExistingAgent', [Parameter(Mandatory = $False)] # Wait for completion and report status. Script may delay to identify the status of the job status [switch]$wait, [Parameter(Mandatory = $False)] # Show available run dates [switch]$showVersions, [Parameter(Mandatory = $False)] # Restore from specified run ID [string]$runId, [Parameter(Mandatory = $False)] # Restore from latest backup before date [string]$olderThan, [Parameter(Mandatory = $False)] # Restore from backup 'n' days ago [int]$daysAgo, # Specify the the VM file and folders are already indexed [Parameter(Mandatory = $False)] [switch]$noIndex, [Parameter(Mandatory = $False)] [switch]$localOnly, #Restore recovery fileandfolder - target environment [Parameter(Mandatory = $False)] [string]$targetEnvironment, #Restore recovery fileandfolder - recover to original target [Parameter(Mandatory = $False)] [switch]$recoverToOriginalTarget, #Restore recovery fileandfolder - overwrite existing [Parameter(Mandatory = $False)] [switch]$overwriteExisting, #Restore recovery fileandfolder - preserveAttributes [Parameter(Mandatory = $False)] [switch]$preserveAttributes, #Restore recovery fileandfolder - continue on error over operation [Parameter(Mandatory = $False)] [switch]$continueOnError, #Restore recovery fileandfolder - encryption enable/disable [Parameter(Mandatory = $False)] [switch]$encryptionEnabled ) Begin { } Process { $restoreMethods = @{ 'ExistingAgent' = 'UseExistingAgent'; 'AutoDeploy' = 'AutoDeploy'; 'VMTools' = 'UseHypervisorApis' } # gather file names $files = @() if($fileList -and (Test-Path $fileList -PathType Leaf)) { $files += Get-Content $fileList | Where-Object {$_ -ne ''} } elseif($fileList) { Write-Warning "File $fileList not found!" return } if($fileNames) { $files += $fileNames } if($files.Length -eq 0) { $errorMsg = "No files selected for restore" Write-Output $errorMsg CSLog -Message $errorMsg return } # convert to unix style file paths $files = [string[]]$files | ForEach-Object {("/" + $_.Replace('\','/').replace(':','')).Replace('//','/')} # find source object $searchURL = "/v2/data-protect/search/protected-objects?snapshotActions=RecoverFiles&searchString=$sourceVM&environments=kVMware" $objects = Invoke-RestApi -Method Get -Uri $searchURL $object = $objects.objects | Where-Object name -eq $sourceVM if(!$object) { $errorMsg = "VM $sourceVM not found" Write-Output $errorMsg CSLog -Message $errorMsg return } # get snapshots $objectId = $object[0].id $groupId = $object[0].latestSnapshotsInfo[0].protectionGroupId $url = "/v2/data-protect/objects/$objectId/snapshots?protectionGroupIds=$groupId" $snapshots = Invoke-RestApi -Method Get -Uri $url if($localOnly) { $snapshots.snapshots = $snapshots.snapshots | Where-Object {$_.snapshotTargetType -eq 'Local'} } # list versions if($showVersions) { $snapshots.snapshots | Select-Object -Property @{label='runId'; expression={$_.runInstanceId}}, @{label='runDate'; expression={usecsToDate $_.runStartTimeUsecs}} return } # version selection if($daysAgo -gt 0) { # set olderThan to X days ago $thisMorning = Get-Date -Hour 0 -Minute 00 -Second 00 $olderThan = $thisMorning.AddDays(-($daysAgo - 1)) } if($runId) { # select specific run ID $snapshot = $snapshots.snapshots | Where-Object runInstanceId -eq $runId if(! $snapshot) { $errorMsg = "runId $runId not found" Write-Output $errorMsg CSLog -Message $errorMsg return } $snapshotId = $snapshot.id } elseif($olderThan) { # select lastest run before olderThan date $olderThanUsecs = dateToUsecs $olderThan $olderSnapshots = $snapshots.snapshots | Where-Object {$olderThanUsecs -gt $_.runStartTimeUsecs} if($olderSnapshots) { $snapshotId = $olderSnapshots[-1].id } else{ $errorMsg = "Oldest snapshot is $(usecsToDate $snapshots.snapshots[0].runStartTimeUsecs)" Write-Output $errorMsg CSLog -Message $errorMsg return } } else { # use latest run $snapshotId = $snapshots.snapshots[-1].id } if (!$taskName) { $dateString = get-date -UFormat '%Y-%m-%d_%H-%M-%S' $taskName = "Recover_$dateString" } if (!$targetEnvironment) { $targetEnvironment = "kVMware" } if ($targetEnvironment -eq "kPhysical") { $errorMsg = "Functionality not implemented for Physical sources" Write-Output $errorMsg CSLog -Message $errorMsg return } $restoreParams = @{ "snapshotEnvironment" = "kVMware"; "name" = $taskName; "vmwareParams" = @{ "objects" = @( @{ "snapshotId" = $snapshotId } ); "recoveryAction" = "RecoverFiles"; "recoverFileAndFolderParams" = @{ "filesAndFolders" = @(); "targetEnvironment" = $targetEnvironment; "vmwareTargetParams" = @{ "recoverToOriginalTarget" = $recoverToOriginalTarget.IsPresent; "overwriteExisting" = $overwriteExisting.IsPresent; "preserveAttributes" = $preserveAttributes.IsPresent; "continueOnError" = $continueOnError.IsPresent; "encryptionEnabled" = $encryptionEnabled.IsPresent } } } } # set VM credentials if($restoreMethod -ne 'ExistingAgent') { if(!$vmUser) { $errorMsg = "VM credentials required for 'AutoDeploy' and 'VMTools' restore methods" Write-Output $errorMsg CSLog -Message $errorMsg return } if(!$vmPwd) { $secureString = Read-Host -Prompt "Enter password for VM user ($vmUser)" -AsSecureString $vmPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $secureString )) } $vmCredentials = @{ "username" = $vmUser; "password" = $vmPwd } } # find target object if($targetVM) { if(!$restorePath) { $errorMsg = "restorePath required when restoring to alternate target VM" Write-Output $errorMsg CSLog -Message $errorMsg return } $vms = Get-CohesityVmwareVM $targetObject = $vms | where-object name -eq $targetVM if(!$targetObject) { $errorMsg = "VM $targetVM not found" Write-Output $errorMsg CSLog -Message $errorMsg return } $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.recoverToOriginalTarget = $false $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams['newTargetConfig']= @{ "targetVm" = @{ "id" = $targetObject[0].id }; "recoverMethod" = $restoreMethods[$restoreMethod]; "absolutePath" = $restorePath; } if($restoreMethod -ne 'ExistingAgent') { $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.newTargetConfig["targetVmCredentials"] = $vmCredentials } } else { # original target config $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams["originalTargetConfig"] = @{ "recoverMethod" = $restoreMethods[$restoreMethod]; } if($restorePath) { $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.originalTargetConfig.recoverToOriginalPath = $false $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.originalTargetConfig["alternatePath"] = $restorePath } else { $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.originalTargetConfig["recoverToOriginalPath"] = $true } if($restoreMethod -ne 'ExistingAgent') { $restoreParams.vmwareParams.recoverFileAndFolderParams.vmwareTargetParams.originalTargetConfig["targetVmCredentials"] = $vmCredentials } } # find files for restore foreach($file in $files) { if($noIndex) { if($file[-1] -eq '/') { $isDirectory = $True } else { $isDirectory = $false } $restoreParams.vmwareParams.recoverFileAndFolderParams.filesAndFolders += @{ "absolutePath" = $file; "isDirectory" = $isDirectory } } else { $searchParams = @{ "fileParams" = @{ "searchString" = $file; "sourceEnvironments" = @( "kVMware" ); "objectIds" = @( $objectId ) }; "objectType" = "Files" } $url = "/v2/data-protect/search/indexed-objects" $searchJson = $searchParams | ConvertTo-Json -Depth 100 $search = Invoke-RestApi -Method Post -Uri $url -Body $searchJson $thisFile = $search.files | Where-Object {("{0}/{1}" -f $_.path, $_.name) -eq $file -or ("{0}/{1}/" -f $_.path, $_.name) -eq $file} if(!$thisFile) { $errorMsg = "file $file not found" Write-Output $errorMsg CSLog -Message $errorMsg } else { if($file[-1] -eq '/') { $isDirectory = $True $absolutePath = "{0}/{1}/" -f $thisFile[0].path, $thisFile[0].name } else { $isDirectory = $false $absolutePath = "{0}/{1}" -f $thisFile[0].path, $thisFile[0].name } $restoreParams.vmwareParams.recoverFileAndFolderParams.filesAndFolders += @{ "absolutePath" = $absolutePath; "isDirectory" = $isDirectory } } } } # perform restore if($restoreParams.vmwareParams.recoverFileAndFolderParams.filesAndFolders.Count -gt 0) { $url = "/v2/data-protect/recoveries" $restoreJson = $restoreParams | ConvertTo-Json -Depth 100 $restoreTask = Invoke-RestApi -Method Post -Uri $url -Body $restoreJson $restoreTaskId = $restoreTask.id if($wait) { # After getting the response, job status "Running" is being found after some delay. # Hence the following delay is required. Otherwise "Running" state will be not # detected at all by the script due to backend limitation $PollingForAPI = 3 Start-Sleep $PollingForAPI while($restoreTask.status -eq "Running") { $PollingForAPI = 5 Start-Sleep $PollingForAPI $url = "/v2/data-protect/recoveries/$($restoreTaskId)?includeTenants=true" $restoreTask = Invoke-RestApi -Method Get -Uri $url $restoreTask } if($restoreTask.status -eq 'Succeeded') { Write-Output "Restore $($restoreTask.status)" $restoreTask } else { Write-Output "Restore $($restoreTask.status): $($restoreTask.messages -join ', ')" $restoreTask } } else { Write-Output "Restore $($restoreTask.status): $($restoreTask.messages -join ', ')" $restoreTask } } else { $errorMsg = "No files found for restore" Write-Output $errorMsg CSLog -Message $errorMsg } } End { } } |