Skip to content

Instantly share code, notes, and snippets.

@trossr32
Forked from Fireforge/backup.ps1
Last active April 2, 2024 15:27
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 trossr32/a56289ed1d4e0384423bdadc5c00f58f to your computer and use it in GitHub Desktop.
Save trossr32/a56289ed1d4e0384423bdadc5c00f58f to your computer and use it in GitHub Desktop.
Powershell backup script using Robocopy
#requires -RunAsAdministrator
function Robocopy-Backup {
<#
.SYNOPSIS
Backup a directory or multiple directories using robocopy
.DESCRIPTION
This script will backup a directory or multiple directories using robocopy. It will create log files per job in the specified log directory.
.PARAMETER SourceDirectory
The source directory to backup, e.g. 'G:\'. Can not be used with SourceDirectories or SourceDestinationPairs parameters.
.PARAMETER SourceDirectories
Multiple source directories to backup, e.g. 'G:\' and 'H:\'. Can not be used with SourceDirectory or SourceDestinationPairs parameters.
.PARAMETER DestinationDirectory
The destination directory to backup to, e.g. 'H:\'.
.PARAMETER SourceDestinationPairs
The source and destination directories to backup, e.g. @{src='G:\';dst='H:\'}, @{src='H:\';dst='I:\'}. Can not be used with SourceDirectory or SourceDirectories parameters.
.PARAMETER LogDirectory
The directory to store the logs in, defaults to 'C:\Logs\'.
.EXAMPLE
Robocopy-Backup -SourceDirectory "G:\" -DestinationDirectory "H:\" -LogDirectory "C:\Logs\"
.EXAMPLE
Robocopy-Backup -SourceDirectories "G:\", "H:\" -DestinationDirectory "I:\" -LogDirectory "C:\Logs\"
.EXAMPLE
Robocopy-Backup -SourceDestinationPairs @{src='G:\';dst='H:\'}, @{src='H:\';dst='I:\'} -LogDirectory "C:\Logs\"
#>
[CmdletBinding(DefaultParameterSetName="SingleSource")]
Param(
[Parameter(Mandatory=$true, ParameterSetName="SingleSource", HelpMessage="The source directory to backup, e.g. 'G:\'")]
[string]$SourceDirectory,
[Parameter(Mandatory=$true, ParameterSetName="MultiSource", HelpMessage="Multiple source directories to backup, e.g. 'G:\' and 'H:\'")]
[string[]]$SourceDirectories,
[Parameter(Mandatory=$true, ParameterSetName="SingleSource", HelpMessage="The destination directory to backup to, e.g. 'H:\'")]
[Parameter(Mandatory=$true, ParameterSetName="MultiSource", HelpMessage="The destination directory to backup to, e.g. 'H:\'")]
[string]$DestinationDirectory,
[Parameter(Mandatory=$true, ParameterSetName="MultiSourceAndDestinations", HelpMessage="The source and destination directories to backup, e.g. @{src='G:\';dst='H:\'}, @{src='H:\';dst='I:\'}")]
[pscustomobject[]]$SourceDestinationPairs,
[Parameter(Mandatory=$false, HelpMessage="The directory to store the logs in, defaults to 'C:\Logs\'")]
[string]$LogDirectory = "C:\Logs\robocopy-backup\"
)
Begin {
# Start a stopwatch to measure the time taken
$sw = [System.Diagnostics.Stopwatch]::StartNew()
# Set the location to the script root
Set-Location -Path $PSScriptRoot
Write-Host "Starting backup at $(get-date -f "yyyy/MM/dd hh:mm:ss")"
$robocopy_block = {
<#
.SYNOPSIS
Backup a directory using robocopy
.DESCRIPTION
This script will backup a directory using robocopy. It will create a log file in the specified log directory.
.PARAMETER src
The source directory to backup, e.g. 'G:\'.
.PARAMETER dst
The destination directory to backup to, e.g. 'H:\'.
.PARAMETER log
The log file to write to, e.g. 'C:\Logs\backup_yyyy-MM-dd_hh-mm-ss.log'.
#>
param(
[Parameter(Mandatory=$true, HelpMessage="The source directory to backup, e.g. 'G:\'")]
[string]$src,
[Parameter(Mandatory=$true, HelpMessage="The destination directory to backup to, e.g. 'H:\'")]
[string]$dst,
[Parameter(Mandatory=$true, HelpMessage="The log file to write to, e.g. 'C:\Logs\backup_yyyy-MM-dd_hh-mm-ss.log'")]
[string]$log
)
# Create log file
New-Item -Force $log | Out-Null
# Execute robocopy command
robocopy $src $dst /MIR /ZB /MT /XJ /XD Temp /R:3 /LOG:$log /NP
# /MIR # mirroring the folders, removing non-existant files. We don't want lots of old files cluttering up the backup
# and the File Shares should keep a history on their own
# /ZB # copies files such that if they are interrupted part-way, they can be restarted
# /MT # copies using multiple cores, good for many small files
# /XJ # Ignoring junctions, they can cause infinite loops when recursing through folders
# /XD Temp # Temp directories shouldn't have important data files
# /R:3 # Retrying a file only 3 times, we don't want bad permissions or other dumb stuff to halt the entire backup
# /LOG:$log # Logging to a file internally, the best way to log with the /MT flag
# /NP # Removing percentages from the log, they don't format well
# Remove hidden and system attributes from the destination directory
attrib -h -s $($dst.TrimEnd("\"))
"Backup directory is complete. [$src -> $dst] ($log)"
}
function Get-FormattedDateTime {
return $(get-date -f yyyy-MM-dd_hh-mm-ss_fff)
}
function Get-JobName {
return "backup_$(Get-FormattedDateTime)"
}
function Get-LogFileName {
return Join-Path $LogDirectory "$(Get-JobName).log"
}
}
Process {
switch ($PSCmdlet.ParameterSetName) {
'SingleSource' {
Write-Host "Backing up $SourceDirectory -> $DestinationDirectory"
$log = Get-LogFileName
# Run the robocopy command
Invoke-Command $robocopy_block -ArgumentList $SourceDirectory,$DestinationDirectory,$log | Out-Null
}
'MultiSource' {
# Loop through each source directory
foreach ($dir in $SourceDirectories) {
$src = $dir
$dst = $DestinationDirectory
$log = Get-LogFileName
Write-Host "Backing up $dir -> $dst"
# pass the loop variable across the job-context barrier
Start-Job $robocopy_block -ArgumentList $src,$dst,$log -Name $(Get-JobName) | Out-Null
}
}
'MultiSourceAndDestinations' {
# Loop through each source/destination directory pair
foreach ($pair in $SourceDestinationPairs) {
$src = $pair.src
$dst = $pair.dst
$log = Get-LogFileName
Write-Host "Backing up $src -> $dst"
# pass the loop variable across the job-context barrier
Start-Job $robocopy_block -ArgumentList $src,$dst,$log -Name $(Get-JobName) | Out-Null
}
}
}
# Wait for all to complete
While (Get-Job -State "Running") {
Start-Sleep 2
}
}
End {
# Display output from all jobs
Get-Job | Receive-Job
# Cleanup
Remove-Job *
Write-Host "Backup complete at $(get-date -f "yyyy/MM/dd hh:mm:ss")"
Write-Host "Processing took: " -NoNewline
Write-Host -ForegroundColor Magenta "$("{0:hh}h:{0:mm}m:{0:ss}s" -f $sw.Elapsed)"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment