Skip to content

Instantly share code, notes, and snippets.

@earthnuker
Created May 6, 2020 18:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save earthnuker/94a91faccc5b565a6661bc5245bd82b9 to your computer and use it in GitHub Desktop.
Save earthnuker/94a91faccc5b565a6661bc5245bd82b9 to your computer and use it in GitHub Desktop.
# Windows PowerShell Script to use restic to backup files using the Volume Shadow Copy Service, allowing
# that are in use to be backed up. The script must be run with elevated privileges.
# The Volume Shadow Copy Service must be enabled for the disk volume that contains the files to be backed up.
#
# credit: https://github.com/turnkey-commerce
#
# Parameters
# Config
function call_cmd() {
$cwd = $PWD
Set-Location $env:TEMP
& cmd $MyInvocation.UnboundArguments
Set-Location $cwd
}
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
Set-Location $scriptPath
$ProgressPreference = "SilentlyContinue"
$IsAdmin = (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
$env:RESTIC_PASSWORD_COMMAND = "gopass show -o misc/Restic_Backup"
if ($IsAdmin -eq $false) {
Write-Host "Running as admin!"
Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
exit
}
# =========================
$env:RESTIC_REPOSITORY = '\\Server\RAID\Backups\restic\' + $env:COMPUTERNAME
$exclude_file = $env:RESTIC_REPOSITORY + '\exclude.txt'
$list_file = $env:TEMP + '\files_' + $env:COMPUTERNAME + '.txt'
$Volumes = @()
foreach ($part in (Get-Partition)) {
$disk = (Get-Disk -Partition $part)
If (($disk.Bustype -in ("SATA", "NVMe")) -and $part.DriveLetter) {
$Volumes += $part.DriveLetter
}
}
foreach ($sc in (Get-WmiObject Win32_ShadowCopy)) {
$sc.Delete()
}
if ((Test-Path ($env:RESTIC_REPOSITORY + '\config')) -eq $false) {
restic init
}
if (Test-Path "$env:TEMP\__shadowcopy__\") {
call_cmd /c rmdir /s /q "$env:TEMP\__shadowcopy__\"
}
New-Item -ItemType Directory -Path "$env:TEMP\__shadowcopy__\" | Out-Null
$Files = @()
$ShadowVolumes = @()
$Mountpoints = @()
foreach ($rootVolume in ($Volumes | Sort-Object)) {
Write-Host "Creating Snapshot for $rootVolume"
$ShadowPath = $env:TEMP + '\__shadowcopy__\' + $rootVolume
$s1 = (Get-WmiObject -List Win32_ShadowCopy).Create($rootVolume + ':\', "ClientAccessible")
$s2 = Get-WmiObject Win32_ShadowCopy | Where-Object { $_.ID -eq $s1.ShadowID }
$device = $s2.DeviceObject + "\"
if (Test-Path $ShadowPath) {
call_cmd /c rmdir $ShadowPath
}
call_cmd /c mklink /d $ShadowPath "$device"
$ShadowVolumes += $s2
$Mountpoints += $ShadowPath
}
$L = (Get-Location)
$Files = @()
Set-Location ($env:TEMP + '\__shadowcopy__\')
(Get-ChildItem -Force .).Name | ForEach-Object {
$shadowPath = (Get-Item -LiteralPath ($env:TEMP + '\__shadowcopy__\')).FullName
$Files += (Get-ChildItem -Force $_).FullName.Replace("[","?").Replace("]","?").Replace($shadowPath,"") # Fix paths so restic doesn't intepret them as Regex patterns
}
$Files | Out-File $list_file
"" | Out-File $env:TEMP\restic_exclude.txt
if (Test-Path $exclude_file) {
(Get-Content $exclude_file) | ForEach-Object {
Write-Output ([Regex]::replace($_, "^([A-Z]):\\(.*)$", '$1\$2')) # make path relative to shadowcopy
} | Out-File $env:TEMP\restic_exclude.txt
}
restic cache --cleanup
restic backup --exclude-caches --exclude-file $env:TEMP\restic_exclude.txt --files-from $list_file
Set-Location $L
foreach ($vol in $ShadowVolumes) {
$vol.Delete()
}
foreach ($mount in $Mountpoints) {
call_cmd /c rmdir $mount
}
Remove-Item -Force $env:TEMP\restic_exclude.txt
Remove-Item -Force $list_file
Remove-Item -Force -Recurse $env:TEMP\__shadowcopy__
Write-Host "Done!"
Write-Host "Press enter to exit"
Read-Host
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment