-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMulti-FLR.ps1
183 lines (168 loc) · 7.94 KB
/
Multi-FLR.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#$DebugPreference = 'Continue'
Add-PSSnapin VeeamPSSnapin
function Test-RestorePath {
<#
.Synopsis
Validates path given to ensure compatibility with restore script.
.Description
This function will accept a string and validate that it is a valid path for to be used in restore script.
.Example
PS C:\> Test-RestorePath 'C:\temp\'
.Example
PS C:\> Test-RestorePath 'C:\temp\document.txt'
.Example
PS C:\> Test-RestorePath '\\servername\c$\'
.Example
PS C:\> Test-RestorePath '\\servername\c$\document.txt'
.Notes
NAME: Test-RestorePath
VERSION: 1.0
AUTHOR: Chris Evans
THANKS: https://github.com/fullenw1 I wouldn't have been able to do this sort of validation without having yours to start from!
#>
Param(
[Parameter(Mandatory)]
#Three regular expressions separated by a pipe meaning:
#Local file | UNC path | Local path | Local drive (root directory)
[ValidatePattern('^[a-zA-Z]:\\\w+|^\\\\\w+\\*|^[a-zA-Z]:\\\w+\\|^[a-zA-Z]:\\')]
#Checking if the parent folder exists
[ValidateScript(
{
<##### DEBUG: Switch block 2 #####>
switch ($PSItem) {
#Matches a root drive (ie. C:\ or Z:\). Break is necessary to avoid the final switch condition from the final switch condition from throwing error as a root directory has no parent.
{ $PSItem -match '^[a-zA-Z]:\\$' } { $True; Write-Debug "Exited switch block 2 via root drive match.`n"; break }
#Checks for a path containing more than one colon
{ $PSItem -match '(:[^:]+:)|::+' } { Throw 'Path cannot contain more than one colon.'}
#Checks for any forbidden characters that don't get caught by Test-Path
{ $PSItem -match '[~/{}]' } { Throw 'The file contains one or more invalid characters (~/{}).' }
#Checks to see if file can be created given the parent path. If path does not exist, we cannot create a file there, now can we?
{(-not(Test-Path -Path (Split-Path -Path $PSItem -Parent))) } { Throw 'The file cannot be created in the path you provided because the folder does not exist.' }
#Default true if nothing has matched.
Default { $True; Write-Debug "Switch block 2 exited out of default value.`n" }
}
}
)]
[ValidateScript(
{
Write-Debug ("Testing path: " + $PSItem + "`n")
<##### DEBUG: Switch block 1 #####>
#Checking final character in path for a period or whitespace.
switch ($PSItem[-1]) {
'.' {Throw 'A valid filepath cannot end with a period.'}
{ $PSItem -match '\s' } { Throw 'A valid filepath cannot end with a blank character.' }
Default { $True; Write-Debug "Switch block 1 exited out of default value.`n" }
}
}
)]
#Syntax validation
[ValidateScript( {Test-Path -Path $PSItem -IsValid})]
[ValidateNotNullOrEmpty()]
[string]$FilePath
)
Write-Debug ("Is the path valid? " + $?)
}#Test-RestorePath
function Show-Backups {
<#
.Synopsis
Formats table of backups for better readability.
.Description
This function will format the $Result into a table to display to user and make it easier to choose which backup(s) to select for restore.
.Example
PS C:\> Show-Backups $Result
.Notes
NAME: Show-Backups
VERSION: 1.0
AUTHOR: Chris Evans
#>
[CmdletBinding()]
param (
[Parameter(
Position = 0,
Mandatory = $False
)]
[PSObject]
$Output = $Result
)
begin {
$Global:n = 0
}
process {
$CustomTable = @{ Expression={ $Global:n;$Global:n++ };Label="Index";Width=10;Align="left" }, `
@{ Expression={ $_.VmName };Label="VM Name";Width=20;Align="left" }, `
@{ Expression={ $_.CreationTime };Label="Creation Time";Width=25;Align="left" }, `
@{ Expression={ $_.Type };Label="Type";Width=15;Align="left" }
}
end {
Write-Host
Write-Host "Here is the list of backups according to your input:"
Write-Host
return $Output | Format-Table $CustomTable
}
}#Show-Backups
do { [string]$JobName = Read-Host '
Name of job which has backups you would like to restore from.
Enter job name' } until ($JobName -ne '')
Write-Debug ('$JobName: ' + $JobName)
do { [string]$ServerName = Read-Host '
From which server would you like to restore folder/file(s)?
Enter server name' } until ($ServerName -ne '')
Write-Debug ('$ServerName: ' + $ServerName)
do { [string]$RestoreFile = Read-Host '
Path to folder or file which you would like to restore.
Example: C:\temp\ or C:\temp\file.txt
Enter source folder/file path' } until ($RestoreFile -ne '')
Write-Debug ('$RestoreFile: ' + $RestoreFile)
do { [string]$CopyTo = Read-Host '
Drive path (or UNC path) where restored folder or file(s) will go.
Example: C:\temp\ or \\servername\c$\temp\
Enter target folder path'; Test-RestorePath $CopyTo } until ($? -eq $True)
<# Checks to make sure $CopyTo path ends with \. If not, adds it.#>
if ($CopyTo -NotMatch '\\$') { $CopyTo += '\' }
Write-Debug ('$CopyTo: ' + $CopyTo)
$Result = Get-VBRBackup | ? { $_.jobname -eq $JobName } | Get-VBRRestorePoint | ? { $_.name -eq $ServerName } | Sort-Object CreationTime
if ($Result.Count -eq 0) {
Write-Host 'Unable to locate any backups based on input. Please try again.'
Start-Sleep -s 3
Exit
} else {
Show-Backups $Result
}
$Title = 'Reverse List Order?'
$Question = 'Would you like to flip the sort order to list newest backups at the top?'
$Choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$Choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$Choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$Decision = $Host.UI.PromptForChoice($Title, $Question, $Choices, 1)
if ($Decision -eq 0) {
Clear-Host
$Result = Get-VBRBackup | ? { $_.jobname -eq $JobName } | Get-VBRRestorePoint | ? { $_.name -eq $ServerName } | Sort-Object CreationTime -Descending
Show-Backups $Result
}
do { [int]$Start = Read-Host 'Please enter starting index' } until (($Start -lt $Result.Count) -and ($Start -ge 0))
Write-Debug ('$Start index: ' + $Start)
Write-Host
do { [int]$Stop = Read-Host 'Please enter ending index' } until (($Stop -ge $Start) -and ($Stop -lt $Result.Count))
Write-Debug ('$Stop index: ' + $Stop)
for (($i = $Start), ($j = 1), ($k = (($Stop - $Start) + 1)); $i -le $Stop; ($i++), ($j++)) {
Write-Host
Write-Host 'Starting restore' $j 'of' $k
$Session = $Result | Select-Object -Index $i | Start-VBRWindowsFileRestore
$FLRMountPoint = ($Session.MountSession.MountedDevices | ? { $_.DriveLetter -eq (Split-Path -Qualifier $RestoreFile) })
$VeeamFLRDir = $FLRMountPoint.MountPoint + (Split-Path -NoQualifier $RestoreFile)
$VeeamFLRLeaf = $VeeamFLRDir | Split-Path -Leaf
Copy-Item -Path $VeeamFLRDir -Destination $CopyTo -Recurse -Force
Get-ChildItem ($CopyTo + $VeeamFLRLeaf) | Rename-Item -NewName { (Get-Date $Session.CreationTime -UFormat 'RESTORED_%d-%b-%Y_%H-%M-%S ') + $_.Name }
try {
Get-Item ($CopyTo + $VeeamFLRLeaf) -ErrorAction Stop | Rename-Item -NewName { (Get-Date $Session.CreationTime -UFormat 'RESTORED_%d-%b-%Y_%H-%M-%S ') + $_.Name }
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Debug "Exception caught! Item being restored was a file, not a folder. It had already been renamed."
}
Stop-VBRWindowsFileRestore $Session
Write-Host 'Restore' $j 'of' $k 'completed.'
}
Write-Host
Invoke-Item $CopyTo
$null = Read-Host 'Restores complete! Press ENTER to exit.'
Exit