add support for external, removable drive backup
- select backup source by drive label, device name, or serial number - fix forget policy to be safe for multiple drives (group by host,tags) - tag each backup source with drive/source name
This commit is contained in:
+75
-8
@@ -17,6 +17,51 @@ $Script:ResticStateRepositoryInitialized = $null
|
|||||||
$Script:ResticStateLastMaintenance = $null
|
$Script:ResticStateLastMaintenance = $null
|
||||||
$Script:ResticStateLastDeepMaintenance = $null
|
$Script:ResticStateLastDeepMaintenance = $null
|
||||||
$Script:ResticStateMaintenanceCounter = $null
|
$Script:ResticStateMaintenanceCounter = $null
|
||||||
|
|
||||||
|
# Returns all drive letters which exactly match the serial number, drive label, or drive name of
|
||||||
|
# the input parameter. Returns all drives if no input parameter is provided.
|
||||||
|
# inspiration: https://stackoverflow.com/questions/31088930/combine-get-disk-info-and-logicaldisk-info-in-powershell
|
||||||
|
function Get-Drives {
|
||||||
|
Param($ID)
|
||||||
|
|
||||||
|
foreach($disk in Get-CimInstance Win32_Diskdrive) {
|
||||||
|
$diskMetadata = Get-Disk | Where-Object { $_.Number -eq $disk.Index } | Select-Object -First 1
|
||||||
|
$partitions = Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -InputObject $disk
|
||||||
|
|
||||||
|
foreach($partition in $partitions) {
|
||||||
|
|
||||||
|
$drives = Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk -InputObject $partition
|
||||||
|
|
||||||
|
foreach($drive in $drives) {
|
||||||
|
|
||||||
|
$volume = Get-Volume |
|
||||||
|
Where-Object { $_.DriveLetter -eq $drive.DeviceID.Trim(":") } |
|
||||||
|
Select-Object -First 1
|
||||||
|
|
||||||
|
if(($diskMetadata.SerialNumber.trim() -eq $ID) -or
|
||||||
|
($disk.Caption -eq $ID) -or
|
||||||
|
($volume.FileSystemLabel -eq $ID) -or
|
||||||
|
($null -eq $ID)) {
|
||||||
|
|
||||||
|
[PSCustomObject] @{
|
||||||
|
DriveLetter = $drive.DeviceID
|
||||||
|
Number = $disk.Index
|
||||||
|
Label = $volume.FileSystemLabel
|
||||||
|
Manufacturer = $diskMetadata.Manufacturer
|
||||||
|
Model = $diskMetadata.Model
|
||||||
|
SerialNumber = $diskMetadata.SerialNumber.trim()
|
||||||
|
Name = $disk.Caption
|
||||||
|
FileSystem = $volume.FileSystem
|
||||||
|
PartitionKind = $diskMetadata.PartitionStyle
|
||||||
|
Drive = $drive
|
||||||
|
Partition = $partition
|
||||||
|
Disk = $disk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# restore backup state from disk
|
# restore backup state from disk
|
||||||
function Get-BackupState {
|
function Get-BackupState {
|
||||||
@@ -123,19 +168,39 @@ function Invoke-Backup {
|
|||||||
|
|
||||||
Write-Output "[[Backup]] Start $(Get-Date)" | Tee-Object -Append $SuccessLog
|
Write-Output "[[Backup]] Start $(Get-Date)" | Tee-Object -Append $SuccessLog
|
||||||
$return_value = $true
|
$return_value = $true
|
||||||
$drive_count = $BackupSources.Count
|
|
||||||
$starting_location = Get-Location
|
$starting_location = Get-Location
|
||||||
ForEach ($item in $BackupSources.GetEnumerator()) {
|
ForEach ($item in $BackupSources.GetEnumerator()) {
|
||||||
|
|
||||||
# Get the source drive letter and set as the root path
|
# Get the source drive letter or identifier and set as the root path
|
||||||
$root_path = $item.Key
|
$root_path = $item.Key
|
||||||
|
$tag = $item.Key
|
||||||
|
|
||||||
# Avoid storing the drive letter in the backup path if only backing up a single drive
|
$vss_option = "--use-fs-snapshot"
|
||||||
# FIXME: this doesn't really work. "C:\" still gets stored
|
|
||||||
if($drive_count -eq 1) {
|
# Test if root path is a valid path, if not assume it is an external drive identifier
|
||||||
Set-Location $root_path
|
if(-not (Test-Path $root_path)) {
|
||||||
$root_path = "."
|
# attempt to find a drive letter associated with the identifier provided
|
||||||
|
$drives = Get-Drives $root_path
|
||||||
|
if($drives.Count -gt 1) {
|
||||||
|
Write-Output "[[Backup]] Fatal error - external drives with more than one partition are not currently supported." | Tee-Object -Append $SuccessLog | Tee-Object -Append $ErrorLog
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
elseif ($drives.Count -eq 0) {
|
||||||
|
# TODO: Silently fails if an entire drive is missing. This is good for occasionally mounted external drives, but bad for
|
||||||
|
# drives that are always expected to be here. May want to make this an optional error.
|
||||||
|
Write-Output "[[Backup]] Warning - backup path $root_path not found." | Tee-Object -Append $SuccessLog #| Tee-Object -Append $ErrorLog
|
||||||
|
# $return_value = $false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$root_path = Join-Path $drives[0].DriveLetter ""
|
||||||
|
|
||||||
|
# disable VSS / file system snapshot for external drives
|
||||||
|
# TODO: would be best to just test for VSS compatibility on the drive, rather than assume it won't work
|
||||||
|
$vss_option = $null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Output "[[Backup]] Start $(Get-Date) [$tag]" | Tee-Object -Append $SuccessLog
|
||||||
|
|
||||||
# Build the new list of folders from settings (if there are any)
|
# Build the new list of folders from settings (if there are any)
|
||||||
$folder_list = New-Object System.Collections.Generic.List[System.Object]
|
$folder_list = New-Object System.Collections.Generic.List[System.Object]
|
||||||
@@ -150,11 +215,13 @@ function Invoke-Backup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Launch Restic
|
# Launch Restic
|
||||||
& $ResticExe backup $folder_list --use-fs-snapshot --exclude-file=$WindowsExcludeFile --exclude-file=$LocalExcludeFile 3>&1 2>> $ErrorLog | Tee-Object -Append $SuccessLog
|
& $ResticExe backup $folder_list $vss_option --tag "$tag" --exclude-file=$WindowsExcludeFile --exclude-file=$LocalExcludeFile 3>&1 2>> $ErrorLog | Tee-Object -Append $SuccessLog
|
||||||
if(-not $?) {
|
if(-not $?) {
|
||||||
Write-Output "[[Backup]] Completed with errors" | Tee-Object -Append $ErrorLog | Tee-Object -Append $SuccessLog
|
Write-Output "[[Backup]] Completed with errors" | Tee-Object -Append $ErrorLog | Tee-Object -Append $SuccessLog
|
||||||
$return_value = $false
|
$return_value = $false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Output "[[Backup]] End $(Get-Date) [$tag]" | Tee-Object -Append $SuccessLog
|
||||||
}
|
}
|
||||||
|
|
||||||
Set-Location $starting_location
|
Set-Location $starting_location
|
||||||
|
|||||||
+4
-1
@@ -12,7 +12,7 @@ $GlobalRetryAttempts = 4
|
|||||||
|
|
||||||
# maintenance configuration
|
# maintenance configuration
|
||||||
$SnapshotMaintenanceEnabled = $true
|
$SnapshotMaintenanceEnabled = $true
|
||||||
$SnapshotRetentionPolicy = @("--group-by", "host", "--keep-daily", "30", "--keep-weekly", "52", "--keep-monthly", "24", "--keep-yearly", "10")
|
$SnapshotRetentionPolicy = @("--group-by", "host,tags", "--keep-daily", "30", "--keep-weekly", "52", "--keep-monthly", "24", "--keep-yearly", "10")
|
||||||
$SnapshotPrunePolicy = @("--max-unused", "1%")
|
$SnapshotPrunePolicy = @("--max-unused", "1%")
|
||||||
$SnapshotMaintenanceInterval = 7
|
$SnapshotMaintenanceInterval = 7
|
||||||
$SnapshotMaintenanceDays = 30
|
$SnapshotMaintenanceDays = 30
|
||||||
@@ -30,3 +30,6 @@ $BackupSources["C:\"] = @(
|
|||||||
#$BackupSources["D:\"] = @(
|
#$BackupSources["D:\"] = @(
|
||||||
# 'Software'
|
# 'Software'
|
||||||
#)
|
#)
|
||||||
|
#$BackupSources["DRIVE_LABEL_NAME_OR_SERIAL_NUMBER"] = @(
|
||||||
|
# 'FolderName'
|
||||||
|
#)
|
||||||
|
|||||||
Reference in New Issue
Block a user