(License: This article, along with any associated source code, is licensed under The LGPL 2.1)
In Part 1 of this series, we laid the foundation by building GoogleDriveBackup.Core, a robust class library for handling Google Drive backup, restore, and repair tasks. Part 2 demonstrated how the GoogleDriveZipBackupTool console application leverages this core library, providing both an interactive menu and a powerful command-line interface (CLI).
While the CLI enables scripting, running efficient incremental backups still requires manually identifying the most recent existing backup archive to pass to the previous= argument. This manual step can be tedious and error-prone, undermining the benefits of automation.
This final part addresses that challenge. We'll explore a PowerShell script designed specifically to automate the incremental backup process. This script intelligently finds the latest backup archive, constructs the correct command-line arguments for GoogleDriveZipBackupTool.exe, executes the backup, and reports the outcome, making regular, efficient backups significantly easier.
The incremental backup feature in GoogleDriveBackup.Core (exposed via the previous= CLI argument) is powerful. It compares the current state of your Google Drive folder against the manifest (_manifest.json) inside a previous backup ZIP. This allows the tool to download only new or modified files, saving significant time and bandwidth compared to a full backup.
However, when automating backups (e.g., using a scheduled task), the automation script needs to know which backup file is the most recent one to use as the baseline for comparison. Manually updating a script with the latest filename after each run is impractical. We need a way for the script to determine this dynamically.
PowerShell is an excellent tool for this kind of automation on Windows. The script below provides a reusable solution. Its core tasks are:
Configuration: Define paths to the backup tool executable and the backup storage directory.
Discovery: Search the backup directory for existing backup files matching a specific naming pattern.
Selection: Identify the most recently modified backup file from the search results.
Command Construction: Build the argument list for GoogleDriveZipBackupTool.exe, including action=backup and, if a recent backup was found, the previous="path/to/latest.zip" argument. It also incorporates any additional user-defined arguments.
Execution: Run GoogleDriveZipBackupTool.exe with the constructed arguments.
Reporting: Check the exit code returned by the executable and report success, failure, or cancellation to the console.
Here is the complete PowerShell script:
<#
.SYNOPSIS
Runs GoogleDriveZipBackupTool.exe for a backup action, automatically finding the
newest existing backup file in a specified directory and using it for the 'previous'
argument for incremental backups.
.DESCRIPTION
This script configures paths for the backup tool and the backup storage location.
It searches the backup directory for files matching a specific pattern (e.g., GDriveBackup_*.zip),
sorts them by modification date (newest first), and selects the newest file.
If a newest file is found, it's added as the 'previous=' argument when calling
GoogleDriveZipBackupTool.exe with 'action=backup'. Otherwise, a full backup is initiated.
Allows specifying additional command-line arguments for the tool.
Reports the success or failure based on the application's exit code.
.NOTES
Author: [Torsten Kelm]
Date: 2024-05-16
Version: 1.0
Requires PowerShell 3.0 or later (for $PSScriptRoot).
Ensure the PowerShell Execution Policy allows running local scripts.
(e.g., Set-ExecutionPolicy RemoteSigned -Scope CurrentUser)
#>
# ============================================================================
# PowerShell Script to run GoogleDriveZipBackupTool with the newest backup as previous
# ============================================================================
# --- Configuration - PLEASE EDIT THESE VALUES ---
# Set the directory where the application executable is located.
# $PSScriptRoot is the directory where the script itself is located. Assume script is in the same dir as exe.
$appDir = $PSScriptRoot
$appExe = "GoogleDriveZipBackupTool.exe"
$appPath = Join-Path -Path $appDir -ChildPath $appExe
# !!! IMPORTANT: Set this to the EXACT path where your backups are saved !!!
# This MUST match the 'LocalBackupArchivePath' in your app settings or profile.
$backupDir = "D:\bck_path" # <-- EDIT THIS
# The naming pattern of your backup files. Adjust if necessary.
# This should match the pattern used by GoogleDriveZipBackupTool when creating files.
$backupPattern = "GDriveBackup_*.zip" # <-- EDIT THIS if your naming convention is different
# Optional: Add any other command-line arguments here as a single string.
# They will be split and passed to the executable. Use quotes within the string if a value has spaces.
# Example: $additionalArgs = 'verbose=true settings="C:\My Configs\backup_profile.settings.json"'
$additionalArgs = 'settings="D:\bck_settings\[your_settings_file]" runIfDue=true' # <-- EDIT THIS with your desired settings profile, runIfDue, etc.
# --- End Configuration ---
Write-Host "Starting Backup Script..." -ForegroundColor Cyan
Write-Host "Application Path: `"$appPath`""
Write-Host "Backup Directory: `"$backupDir`""
Write-Host "Backup Pattern: `"$backupPattern`""
if ($additionalArgs) { Write-Host "Additional Args: $additionalArgs" }
Write-Host ""
# --- Check if Application Exists ---
if (-not (Test-Path -Path $appPath -PathType Leaf)) {
Write-Error "Application executable not found at specified path: `"$appPath`""
Write-Error "Please check the \$appDir and \$appExe variables in this script."
exit 1 # Exit with error code 1
}
# --- Check if Backup Directory Exists ---
if (-not (Test-Path -Path $backupDir -PathType Container)) {
Write-Error "Backup directory not found!"
Write-Error "Please check the \$backupDir variable in this script."
Write-Error "Path checked: `"$backupDir`""
exit 1 # Exit with error code 1
}
# --- Find the Newest Backup File ---
Write-Host "Searching for the newest backup file matching `"$backupPattern`"..." -ForegroundColor Yellow
$previousArgument = ""
$newestBackup = Get-ChildItem -Path $backupDir -Filter $backupPattern -File |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First 1
if ($null -ne $newestBackup) {
Write-Host "Found newest backup file: `"$($newestBackup.Name)`"" -ForegroundColor Green
# Ensure the full path is quoted correctly for the argument
$previousArgument = "previous=`"$($newestBackup.FullName)`""
Write-Host "Using this file for the 'previous' argument."
} else {
Write-Host "No previous backup files found matching `"$backupPattern`"." -ForegroundColor Yellow
Write-Host "Performing a FULL backup (no 'previous' argument will be used)."
}
Write-Host ""
# --- Construct and Execute the Command ---
Write-Host "Preparing command..." -ForegroundColor Cyan
# Start building the argument list for the executable
$arguments = @("action=backup") # Start with mandatory action
# Add 'previous' argument if found
if ($previousArgument) {
$arguments += $previousArgument
}
# Add any additional user-defined arguments
# Simple split by space - might need more robust parsing if args values contain spaces *without* internal quotes
if ($additionalArgs) {
$arguments += ($additionalArgs -split ' ') # Add each part as a separate argument
}
$commandStringForDisplay = "`"$appPath`" $($arguments -join ' ')"
Write-Host "Executing: $commandStringForDisplay"
Write-Host ""
Write-Host "--- Application Output Starts ---" -ForegroundColor Gray
# Execute the application
try {
& $appPath $arguments # The '&' call operator executes the command
$scriptExitCode = $LASTEXITCODE # Capture the exit code from the application
} catch {
Write-Error "An error occurred while trying to execute the application:"
Write-Error $_.Exception.Message
$scriptExitCode = -1 # Indicate script-level execution error
}
Write-Host "--- Application Output Ends ---" -ForegroundColor Gray
Write-Host ""
# --- Report Script Result based on Exit Code ---
Write-Host "--- Script Finished ---" -ForegroundColor Cyan
if ($scriptExitCode -eq 0) {
Write-Host "Backup command completed successfully (Application Exit Code: 0)." -ForegroundColor Green
} elseif ($scriptExitCode -eq 2) {
Write-Host "Backup command was cancelled by user (Application Exit Code: 2)." -ForegroundColor Yellow
} elseif ($scriptExitCode -eq -1) {
Write-Host "Script failed to execute the application." -ForegroundColor Red
# Error already written in catch block
} else {
Write-Host "Backup command failed or completed with errors (Application Exit Code: $scriptExitCode)." -ForegroundColor Red
Write-Host "Check console output above and application logs for details."
}
# Wait for Enter to be pressed before exiting (useful if run by double-clicking)
Read-Host "Press ENTER to continue…"
# Exit the script with the application's exit code (or script error code)
exit $scriptExitCode
Breakdown:
Configuration Section: This is the most important part for the user. You must edit the values for $appDir (if the script isn't in the same directory as the .exe), $appExe (if named differently), $backupDir (where your ZIPs are stored), $backupPattern (if your backup files use a different naming convention), and $additionalArgs (to specify settings profiles, runIfDue=true, verbosity, etc.).
Path Checks: Simple Test-Path cmdlets verify that the specified executable and backup directory actually exist before proceeding. This prevents cryptic errors later on.
Finding the Newest Backup:
Get-ChildItem -Path $backupDir -Filter $backupPattern -File: Gets all files (not directories) in the backup directory matching the specified pattern (e.g., GDriveBackup_*.zip).
Sort-Object -Property LastWriteTime -Descending: Sorts the found files based on their modification timestamp, with the newest file appearing first.
Select-Object -First 1: Selects only the first object from the sorted list, which is the newest file.
The result is stored in $newestBackup. If no files match, $newestBackup will be $null.
Constructing the previous Argument: If $newestBackup is not null, a string like previous="D:\bck_path\GDriveBackup_20240516_103000.zip" is created and stored in $previousArgument. The path is enclosed in double quotes to handle potential spaces.
Building the Full Argument List:
An array $arguments is initialized with the mandatory action=backup.
The $previousArgument string is added if it was created (i.e., if a previous backup was found).
The $additionalArgs string is split by spaces, and each part is added as a separate element to the $arguments array. Note: This simple split assumes arguments in $additionalArgs don't contain spaces unless they are properly quoted within that string.
Executing the Command:
The & (call operator) is used to execute the command stored in $appPath with the arguments stored in the $arguments array. PowerShell handles passing the array elements correctly as distinct arguments to the executable.
$LASTEXITCODE is an automatic PowerShell variable that holds the exit code of the last native executable that ran. This is captured immediately after the call.
A try-catch block handles potential errors during the execution of the command itself (e.g., file not found, permissions issues).
Reporting Results: The script checks the captured $scriptExitCode (which is the application's exit code or -1 if the script failed to run it) and prints a clear status message using different colors for success (Green), cancellation (Yellow), or failure (Red).
Read-Host: The final Read-Host prevents the console window from closing immediately if the script is run by double-clicking, allowing the user to see the output.
Save the Script: Save the code above into a file named, for example, Run-IncrementalBackup.ps1. It's often convenient to save it in the same directory as GoogleDriveZipBackupTool.exe.
Configure: Edit the configuration section at the top of the script. Pay close attention to $backupDir and $additionalArgs. Ensure $backupDir exactly matches where GoogleDriveZipBackupTool.exe saves its backups (controlled by its settings).
PowerShell Execution Policy: By default, PowerShell often restricts running scripts downloaded from the internet or unsigned local scripts. If you haven't run PowerShell scripts before, you might need to adjust the execution policy. A common approach for running local scripts is:
Open PowerShell as Administrator.
Run the command: Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Confirm the change if prompted.
You can then close the administrative PowerShell window. This allows running local scripts (.ps1 files) and signed remote scripts.
Run the Script:
Open a regular PowerShell window (no administrator required after setting the policy).
Navigate to the directory where you saved the script: cd C:\Path\To\Your\Tool
Execute the script: .\Run-IncrementalBackup.ps1
Observe the output in the console.
The true power of this script comes when it's run automatically on a schedule. You can use the Windows Task Scheduler for this:
Open Task Scheduler (search for it in the Start Menu).
Create a new Basic Task or a standard Task.
Trigger: Define when you want the backup to run (e.g., Daily at 2:00 AM).
Action: Select "Start a program".
Program/script: powershell.exe
Add arguments (optional): -ExecutionPolicy Bypass -File "C:\Path\To\Your\Tool\Run-IncrementalBackup.ps1"
Using -ExecutionPolicy Bypass here specifically for the task can sometimes avoid global policy issues, but be mindful of the security implications.
Ensure the path to your .ps1 file is correct.
Conditions/Settings: Configure other options as needed (e.g., run only if the network is available, wake the computer, run with highest privileges if necessary - though usually not required if the execution policy is set for the user).
Now, Task Scheduler will automatically run your PowerShell script, which will find the latest backup and execute GoogleDriveZipBackupTool.exe to perform an efficient incremental backup without any manual intervention.
Automating the incremental backup process is crucial for consistency and reliability. This PowerShell script bridges the gap between the powerful CLI capabilities of the GoogleDriveZipBackupTool (developed in Parts 1 and 2) and practical, hands-off automation. By dynamically finding the most recent backup archive and constructing the appropriate command-line arguments, it ensures that incremental backups are performed correctly every time. Combined with Windows Task Scheduler, this provides a complete, automated, and efficient solution for keeping your Google Drive data safely backed up locally.