diff --git a/README.md b/README.md index 6f210a3..686109b 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Designed for system administrators — and also for those who *feel like* system ## Scripts Examples - [Hyper-V Backup](./examples/HyperV-Backup/README.md) - Production-ready Hyper-V VM backup solution with scheduling and retention management +- [File-Sync](./examples//File-Sync/README.md) - [FreeFileSync](https://freefilesync.org/) batch job execution --- diff --git a/examples/File-Sync/README.md b/examples/File-Sync/README.md new file mode 100644 index 0000000..e88f779 --- /dev/null +++ b/examples/File-Sync/README.md @@ -0,0 +1,388 @@ +# File Sync Script + +**Version:** 1.0.0 +**Last Updated:** 2026-01-24 + +## Overview + +Production-ready automated file synchronization solution using FreeFileSync with scheduling, secure credential management, and remote storage support. + +## Features + +- ✅ **Automated File Sync** - Executes FreeFileSync batch jobs on schedule +- ✅ **Flexible Scheduling** - Schedule sync by month, weekday, and time with interval control +- ✅ **Remote Storage Support** - Sync to UNC shares with secure credential management +- ✅ **Prerequisite Validation** - Checks FreeFileSync installation before execution +- ✅ **Process Management** - Proper cleanup of FreeFileSync process on exit +- ✅ **Detailed Logging** - Comprehensive logging with timestamps and severity levels +- ✅ **Lock Files** - Prevents concurrent execution +- ✅ **Error Handling** - Proper exit codes and error reporting + +## Requirements + +### System Requirements +- Windows with PowerShell 5.1 or later +- FreeFileSync installed (free download from https://freefilesync.org/) +- Network access to target share (if using UNC paths) + +### Dependencies +- `SchedulerTemplate.psm1` module (located in parent directory) +- `scriptsettings.json` configuration file +- `sync.ffs_batch` FreeFileSync batch configuration file + +## File Structure + +``` +File-Sync/ +├── file-sync.bat # Batch launcher with admin check +├── file-sync.ps1 # Main PowerShell script +├── scriptsettings.json # Configuration file +├── sync.ffs_batch # FreeFileSync batch configuration +└── README.md # This file +``` + +## Installation + +1. **Install FreeFileSync** + + Download and install from https://freefilesync.org/ + + Default installation path: `C:\Program Files\FreeFileSync\FreeFileSync.exe` + +2. **Copy Files** + ```powershell + # Copy the entire File-Sync folder to your desired location + # Ensure SchedulerTemplate.psm1 is in the parent directory + ``` + +3. **Create FreeFileSync Batch Configuration** + + Use FreeFileSync GUI to: + - Configure source and destination folders + - Set synchronization settings (Mirror, Two-way, Update, etc.) + - Save as batch job: `sync.ffs_batch` in the File-Sync directory + +4. **Configure Settings** + + Edit `scriptsettings.json` with your environment settings: + ```json + { + "schedule": { + "runMonth": [], + "runWeekday": ["Monday"], + "runTime": ["00:00"], + "minIntervalMinutes": 10 + }, + "freeFileSyncExe": "C:\\Program Files\\FreeFileSync\\FreeFileSync.exe", + "batchFile": "sync.ffs_batch", + "nasRootShare": "\\\\your-nas\\share", + "credentialEnvVar": "YOUR_ENV_VAR_NAME" + } + ``` + +5. **Setup Credentials (for UNC paths)** + + If syncing to a network share, create a Machine-level environment variable: + ```powershell + # Create Base64-encoded credentials + $username = "DOMAIN\user" + $password = "your-password" + $creds = "$username:$password" + $encoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($creds)) + + # Set Machine-level environment variable + [System.Environment]::SetEnvironmentVariable("YOUR_ENV_VAR_NAME", $encoded, "Machine") + ``` + +6. **Test Manual Execution** + ```powershell + # Run as Administrator + .\file-sync.bat + # or + .\file-sync.ps1 + ``` + +## Configuration Reference + +### Schedule Settings + +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| `runMonth` | array | Month names to run. Empty = every month | `["January", "June", "December"]` or `[]` | +| `runWeekday` | array | Weekday names to run. Empty = every day | `["Monday", "Friday"]` | +| `runTime` | array | UTC times to run (HH:mm format) | `["00:00", "12:00"]` | +| `minIntervalMinutes` | number | Minimum minutes between runs | `10` | + +### Sync Settings + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `freeFileSyncExe` | string | Yes | Full path to FreeFileSync.exe executable | +| `batchFile` | string | Yes | FreeFileSync batch file name (must be in same directory). Configure all sync paths directly in this file. | +| `nasRootShare` | string | Yes | UNC path to NAS root share for authentication only | +| `credentialEnvVar` | string | No* | Name of Machine-level environment variable with credentials (*Required for UNC paths) | + +### Version Tracking + +| Property | Type | Description | +|----------|------|-------------| +| `version` | string | Configuration schema version | +| `lastModified` | string | Last modification date (YYYY-MM-DD) | + +## Usage + +### Manual Execution + +**Using Batch File (Recommended):** +```batch +REM Right-click and select "Run as administrator" +file-sync.bat +``` + +**Using PowerShell:** +```powershell +# Run as Administrator +.\file-sync.ps1 + +# With verbose output +.\file-sync.ps1 -Verbose +``` + +### Automated Execution + +The script supports automated execution through the UScheduler service: + +```powershell +# Called by scheduler with -Automated flag +.\file-sync.ps1 -Automated -CurrentDateTimeUtc "2026-01-24 00:00:00" +``` + +When `-Automated` is specified: +- Schedule is enforced (month, weekday, time) +- Lock files prevent concurrent execution +- Interval checking prevents duplicate runs +- Logs are formatted for service logger (no timestamps) + +## How It Works + +### Sync Process Flow + +1. **Initialization** + - Load SchedulerTemplate.psm1 module + - Load and validate scriptsettings.json + - Validate required settings + - Construct batch file path + +2. **Pre-flight Checks** + - Verify FreeFileSync executable exists + - Check FreeFileSync version + - Verify batch configuration file exists + - Authenticate to NAS share (if UNC) + - Verify NAS reachability + +3. **Sync Execution** + - Launch FreeFileSync in silent mode + - Capture stdout and stderr + - Monitor process completion + - Interpret exit code + +4. **Cleanup** + - Terminate FreeFileSync if script exits + - Display sync summary + +### FreeFileSync Exit Codes + +| Code | Meaning | Script Action | +|------|---------|---------------| +| 0 | Success | Mark as successful, exit 0 | +| 1 | Warning (sync completed with warnings) | Mark as successful, exit 0 | +| 2 | Error | Mark as failed, exit 1 | +| 3 | Cancelled | Mark as failed, exit 1 | + +### Lock and State Files + +When running in automated mode, the script creates: +- `file-sync.lock` - Prevents concurrent execution +- `file-sync.lastRun` - Tracks last execution time for interval control + +## Logging + +### Log Levels + +| Level | Description | Color (Manual) | +|-------|-------------|----------------| +| `Info` | Informational messages | White | +| `Success` | Successful operations | Green | +| `Warning` | Non-critical issues | Yellow | +| `Error` | Critical errors | Red | + +### Log Format + +**Manual Execution:** +``` +[2026-01-24 00:00:00] [Info] FreeFileSync Process Started +[2026-01-24 00:00:01] [Success] All prerequisites passed +[2026-01-24 00:05:30] [Success] FreeFileSync completed successfully +``` + +**Automated Execution:** +``` +[Info] FreeFileSync Process Started +[Success] All prerequisites passed +[Success] FreeFileSync completed successfully +``` + +## Exit Codes + +| Code | Description | +|------|-------------| +| `0` | Success (including warnings) | +| `1` | Error occurred (module load, config, prerequisites, sync failure) | + +## Troubleshooting + +### Common Issues + +**1. Module Not Found** +``` +Error: Failed to load SchedulerTemplate.psm1 +``` +**Solution:** Ensure SchedulerTemplate.psm1 is in the parent directory (`../SchedulerTemplate.psm1`) + +**2. FreeFileSync Not Found** +``` +Error: FreeFileSync not found: C:\Program Files\FreeFileSync\FreeFileSync.exe +``` +**Solution:** +- Install FreeFileSync from https://freefilesync.org/ +- Update `freeFileSyncExe` path in scriptsettings.json if installed to custom location + +**3. Batch File Not Found** +``` +Error: Batch config not found +``` +**Solution:** +- Create FreeFileSync batch configuration using FreeFileSync GUI +- Save as `sync.ffs_batch` in the File-Sync directory +- Update `batchFile` setting if using different name + +**4. UNC Path Authentication Failed** +``` +Error: Failed to connect to \\server\share +``` +**Solution:** +- Verify `credentialEnvVar` is set in scriptsettings.json +- Verify environment variable exists at Machine level +- Verify credentials are Base64-encoded in format: `username:password` +- Test with: `net use \\server\share` manually + +**5. Lock File Exists** +``` +Guard: Lock file exists. Skipping. +``` +**Solution:** +- Another instance is running, or previous run didn't complete +- Manually delete `.lock` file if stuck +- Check for hung PowerShell or FreeFileSync processes + +**6. FreeFileSync Errors** +``` +Error: FreeFileSync completed with errors (exit code 2) +``` +**Solution:** +- Check FreeFileSync output in logs +- Verify source and destination paths are accessible +- Review FreeFileSync batch configuration +- Check file/folder permissions + +### Debug Mode + +Run with verbose output: +```powershell +.\file-sync.ps1 -Verbose +``` + +## Best Practices + +1. **Test First** - Always test sync manually before scheduling +2. **Backup First** - Ensure you have backups before first sync +3. **Verify Paths** - Double-check source and destination paths in batch file +4. **Monitor Logs** - Check sync summaries regularly +5. **Secure Credentials** - Use Machine-level environment variables, never store passwords in scripts +6. **Schedule Wisely** - Run syncs during low-usage periods +7. **Review Settings** - Understand FreeFileSync sync mode (Mirror, Two-way, Update) +8. **Test Restores** - Periodically verify you can restore from synced data + +## FreeFileSync Configuration + +### Creating Batch Configuration + +1. Launch FreeFileSync GUI +2. Configure left (source) and right (target) folders +3. Set comparison settings (File time and size, Content, File size) +4. Set filter rules (include/exclude patterns) +5. Choose sync variant: + - **Mirror**: Make right folder identical to left + - **Update**: Copy new/updated files to right + - **Two-way**: Propagate changes both ways + - **Custom**: Define custom rules +6. Click "Save as batch job" +7. Save as `sync.ffs_batch` in File-Sync directory + +**Note:** All source and destination paths are configured directly in the FreeFileSync batch file. The script does not modify these paths - it only handles authentication to network shares before FreeFileSync executes. + +## Security Considerations + +- **Credentials** are stored Base64-encoded in Machine-level environment variables (not encryption, just encoding) +- Script requires **Administrator privileges** for network authentication +- **Network credentials** are passed to `net use` command +- Consider using **dedicated sync account** with minimal required permissions +- **Sync data** should be stored on secured network shares with appropriate ACLs +- **FreeFileSync** runs in silent mode without user interaction + +## Performance Considerations + +- **Sync time** depends on file count, size, and network speed +- **Network speed** is typically the bottleneck for UNC shares +- **FreeFileSync** is optimized for incremental syncs +- Use **filter rules** to exclude temporary/unnecessary files +- Consider **bandwidth throttling** in FreeFileSync settings for large syncs +- Run during **off-peak hours** to minimize network impact + +## Version History + +### 1.0.0 (2026-01-24) +- Initial production release +- Automated sync with scheduling +- FreeFileSync batch execution +- UNC share support with credential management +- Lock file and interval control +- Comprehensive error handling and logging +- Dynamic batch file path updates +- Prerequisite validation + +## Support + +For issues or questions: +1. Check the [Troubleshooting](#troubleshooting) section +2. Review script logs for error details +3. Verify all [Requirements](#requirements) are met +4. Check FreeFileSync documentation at https://freefilesync.org/manual.php + +## License + +See [LICENSE](../../LICENSE.md) in the root directory. + +## Related Files + +- `../SchedulerTemplate.psm1` - Shared scheduling and logging module +- `scriptsettings.json` - Configuration file +- `file-sync.bat` - Batch launcher +- `file-sync.ps1` - Main script +- `sync.ffs_batch` - FreeFileSync batch configuration + +## Additional Resources + +- **FreeFileSync Official Site:** https://freefilesync.org/ +- **FreeFileSync Manual:** https://freefilesync.org/manual.php +- **FreeFileSync Forum:** https://freefilesync.org/forum/ diff --git a/examples/File-Sync/file-sync.bat b/examples/File-Sync/file-sync.bat new file mode 100644 index 0000000..4890fb4 --- /dev/null +++ b/examples/File-Sync/file-sync.bat @@ -0,0 +1,74 @@ +@echo off +setlocal EnableDelayedExpansion + +REM ============================================================================ +REM File Sync Launcher +REM VERSION: 1.0.0 +REM DATE: 2026-01-24 +REM DESCRIPTION: Batch file launcher for file-sync.ps1 with admin check +REM ============================================================================ + +echo. +echo ============================================ +echo File Sync Automated Launcher +echo ============================================ +echo. + +REM Check for Administrator privileges +net session >nul 2>&1 +if %errorLevel% NEQ 0 ( + echo [ERROR] This script must be run as Administrator! + echo. + echo Please right-click and select "Run as administrator" + echo. + pause + exit /b 1 +) + +echo [OK] Running with Administrator privileges +echo. + +REM Get script directory +set "SCRIPT_DIR=%~dp0" +set "PS_SCRIPT=%SCRIPT_DIR%file-sync.ps1" + +REM Check if PowerShell script exists +if not exist "%PS_SCRIPT%" ( + echo [ERROR] PowerShell script not found: %PS_SCRIPT% + echo. + pause + exit /b 1 +) + +echo [OK] Found PowerShell script: %PS_SCRIPT% +echo. +echo ============================================ +echo Starting sync process... +echo ============================================ +echo. + +REM Execute PowerShell script +REM Note: Logging is handled by UScheduler service +powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%PS_SCRIPT%" + +REM Capture exit code +set "EXIT_CODE=%ERRORLEVEL%" + +echo. +echo ============================================ +echo Sync process completed +echo Exit Code: %EXIT_CODE% +echo ============================================ +echo. + +if %EXIT_CODE% EQU 0 ( + echo [SUCCESS] Sync completed successfully +) else ( + echo [ERROR] Sync completed with errors +) + +echo. +pause + +endlocal +exit /b %EXIT_CODE% \ No newline at end of file diff --git a/examples/File-Sync/file-sync.ps1 b/examples/File-Sync/file-sync.ps1 new file mode 100644 index 0000000..a117c55 --- /dev/null +++ b/examples/File-Sync/file-sync.ps1 @@ -0,0 +1,373 @@ +[CmdletBinding()] +param ( + [switch]$Automated, + [string]$CurrentDateTimeUtc +) + +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + FreeFileSync Automated Backup Script +.DESCRIPTION + Production-ready file synchronization solution with scheduling and secure credential management. +.VERSION + 1.0.0 +.DATE + 2026-01-24 +.NOTES + - Requires FreeFileSync installed + - Requires SchedulerTemplate.psm1 module + - Requires sync.ffs_batch configuration file +#> + +# Script Version +$ScriptVersion = "1.0.0" +$ScriptDate = "2026-01-24" + +try { + Import-Module "$PSScriptRoot\..\SchedulerTemplate.psm1" -Force -ErrorAction Stop +} +catch { + Write-Error "Failed to load SchedulerTemplate.psm1: $_" + exit 1 +} + +# Load Settings ============================================================ + +$settingsFile = Join-Path $PSScriptRoot "scriptsettings.json" + +if (-not (Test-Path $settingsFile)) { + Write-Error "Settings file not found: $settingsFile" + exit 1 +} + +try { + $settings = Get-Content $settingsFile -Raw | ConvertFrom-Json + Write-Verbose "Loaded settings from $settingsFile" +} +catch { + Write-Error "Failed to load settings from $settingsFile : $_" + exit 1 +} + +# Process Settings ========================================================= + +# Validate required settings +$requiredSettings = @('freeFileSyncExe', 'batchFile', 'nasRootShare') +foreach ($setting in $requiredSettings) { + if (-not $settings.$setting) { + Write-Error "Required setting '$setting' is missing or empty in $settingsFile" + exit 1 + } +} + +$FreeFileSyncExe = $settings.freeFileSyncExe +$FfsBatchFile = Join-Path $PSScriptRoot $settings.batchFile +$NasRootShare = $settings.nasRootShare +$CredentialEnvVar = $settings.credentialEnvVar + +# Schedule Configuration +$Config = @{ + RunMonth = $settings.schedule.runMonth + RunWeekday = $settings.schedule.runWeekday + RunTime = $settings.schedule.runTime + MinIntervalMinutes = $settings.schedule.minIntervalMinutes +} + +# End Settings ============================================================= + +# Global variables +$script:SyncStats = @{ + StartTime = Get-Date + EndTime = $null + Success = $false + ExitCode = $null + ErrorMessage = $null +} + +# Global reference to the process so that the exit handler can see it +$global:FFS_Process = $null + +# Helper Functions ========================================================= + +function Test-Prerequisites { + param([switch]$Automated) + + Write-Log "Checking prerequisites..." -Level Info -Automated:$Automated + + # Check if FreeFileSync executable exists + if (-not (Test-Path -LiteralPath $FreeFileSyncExe)) { + Write-Log "FreeFileSync not found: $FreeFileSyncExe" -Level Error -Automated:$Automated + Write-Log "Please install FreeFileSync or update the path in scriptsettings.json" -Level Error -Automated:$Automated + return $false + } + + # Verify FreeFileSync version + try { + $ffsVersion = (Get-Item $FreeFileSyncExe).VersionInfo.FileVersion + Write-Log "FreeFileSync version: $ffsVersion" -Level Info -Automated:$Automated + } + catch { + Write-Log "Warning: Could not determine FreeFileSync version" -Level Warning -Automated:$Automated + } + + # Check if batch file exists + if (-not (Test-Path -LiteralPath $FfsBatchFile)) { + Write-Log "Batch config not found: $FfsBatchFile" -Level Error -Automated:$Automated + Write-Log "Please create sync.ffs_batch configuration file" -Level Error -Automated:$Automated + return $false + } + + Write-Log "All prerequisites passed" -Level Success -Automated:$Automated + return $true +} + +function Connect-NasShare { + param( + [string]$SharePath, + [switch]$Automated + ) + + # Check if path is UNC + if (-not $SharePath.StartsWith("\\")) { + Write-Log "NAS path is local, no authentication needed" -Level Info -Automated:$Automated + return $true + } + + Write-Log "Authenticating to NAS share: $SharePath" -Level Info -Automated:$Automated + + # Validate credential environment variable name is configured + if (-not $CredentialEnvVar) { + Write-Log "credentialEnvVar is not configured in settings for UNC path authentication" -Level Error -Automated:$Automated + return $false + } + + try { + # Retrieve credentials from environment variable + $creds = Get-CredentialFromEnvVar -EnvVarName $CredentialEnvVar -Automated:$Automated + + if (-not $creds) { + return $false + } + + # Check if already connected + $existingConnection = net use | Select-String $SharePath + if ($existingConnection) { + Write-Log "Already connected to $SharePath" -Level Info -Automated:$Automated + return $true + } + + # Connect to share + $netUseResult = cmd /c "net use $SharePath $($creds.Password) /user:$($creds.Username) 2>&1" + + if ($LASTEXITCODE -ne 0) { + Write-Log "Failed to connect to $SharePath : $netUseResult" -Level Error -Automated:$Automated + return $false + } + + Write-Log "Successfully authenticated to $SharePath" -Level Success -Automated:$Automated + return $true + } + catch { + Write-Log "Error connecting to share: $_" -Level Error -Automated:$Automated + return $false + } +} + +function Test-NasReachability { + param( + [string]$SharePath, + [switch]$Automated + ) + + if (-not (Test-Path $SharePath)) { + Write-Log "NAS share $SharePath is not reachable after authentication" -Level Warning -Automated:$Automated + return $false + } + + Write-Log "NAS share is reachable: $SharePath" -Level Success -Automated:$Automated + return $true +} + +function Start-FreeFileSyncProcess { + param([switch]$Automated) + + Write-Log "Starting FreeFileSync batch synchronization..." -Level Info -Automated:$Automated + Write-Log " EXE: $FreeFileSyncExe" -Level Info -Automated:$Automated + Write-Log " BATCH: $FfsBatchFile" -Level Info -Automated:$Automated + + # Ensure FreeFileSync is killed if PowerShell exits gracefully + Register-EngineEvent PowerShell.Exiting -Action { + if ($global:FFS_Process -and -not $global:FFS_Process.HasExited) { + try { + $global:FFS_Process.Kill() + } + catch { + } + } + } | Out-Null + + try { + # Use ProcessStartInfo to ensure that no window is shown + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = $FreeFileSyncExe + $psi.Arguments = "`"$FfsBatchFile`"" + $psi.WorkingDirectory = [System.IO.Path]::GetDirectoryName($FreeFileSyncExe) + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $true + + $proc = [System.Diagnostics.Process]::Start($psi) + $global:FFS_Process = $proc + + # Capture FFS console output + $stdOut = $proc.StandardOutput.ReadToEnd() + $stdErr = $proc.StandardError.ReadToEnd() + + $proc.WaitForExit() + $exitCode = $proc.ExitCode + + if ($stdOut) { + Write-Log "FreeFileSync STDOUT:" -Level Info -Automated:$Automated + Write-Log $stdOut.TrimEnd() -Level Info -Automated:$Automated + } + + if ($stdErr) { + Write-Log "FreeFileSync STDERR:" -Level Warning -Automated:$Automated + Write-Log $stdErr.TrimEnd() -Level Warning -Automated:$Automated + } + + # Store exit code + $script:SyncStats.ExitCode = $exitCode + + # Interpret exit code + switch ($exitCode) { + 0 { + Write-Log "FreeFileSync completed successfully (exit code 0)" -Level Success -Automated:$Automated + $script:SyncStats.Success = $true + return $true + } + 1 { + Write-Log "FreeFileSync completed with warnings (exit code 1)" -Level Warning -Automated:$Automated + $script:SyncStats.Success = $true + return $true + } + 2 { + Write-Log "FreeFileSync completed with errors (exit code 2)" -Level Error -Automated:$Automated + $script:SyncStats.ErrorMessage = "FreeFileSync reported errors" + return $false + } + 3 { + Write-Log "FreeFileSync was cancelled (exit code 3)" -Level Warning -Automated:$Automated + $script:SyncStats.ErrorMessage = "FreeFileSync was cancelled" + return $false + } + default { + Write-Log "FreeFileSync exited with unexpected code $exitCode" -Level Error -Automated:$Automated + $script:SyncStats.ErrorMessage = "Unexpected exit code: $exitCode" + return $false + } + } + } + catch { + Write-Log "Failed to execute FreeFileSync: $_" -Level Error -Automated:$Automated + $script:SyncStats.ErrorMessage = "Execution failed: $_" + return $false + } + finally { + if ($global:FFS_Process -and -not $global:FFS_Process.HasExited) { + Write-Log "PowerShell is stopping. Killing FreeFileSync process (PID $($global:FFS_Process.Id))..." -Level Warning -Automated:$Automated + try { + $global:FFS_Process.Kill() + } + catch { + Write-Log "Failed to kill FreeFileSync process: $_" -Level Error -Automated:$Automated + } + } + } +} + +function Write-SyncSummary { + param([switch]$Automated) + + $script:SyncStats.EndTime = Get-Date + $duration = $script:SyncStats.EndTime - $script:SyncStats.StartTime + + Write-Log "" -Level Info -Automated:$Automated + Write-Log "========================================" -Level Info -Automated:$Automated + Write-Log "SYNC SUMMARY" -Level Info -Automated:$Automated + Write-Log "========================================" -Level Info -Automated:$Automated + Write-Log "Start Time : $($script:SyncStats.StartTime.ToString('yyyy-MM-dd HH:mm:ss'))" -Level Info -Automated:$Automated + Write-Log "End Time : $($script:SyncStats.EndTime.ToString('yyyy-MM-dd HH:mm:ss'))" -Level Info -Automated:$Automated + Write-Log "Duration : $($duration.Hours)h $($duration.Minutes)m $($duration.Seconds)s" -Level Info -Automated:$Automated + Write-Log "Status : $(if ($script:SyncStats.Success) { 'SUCCESS' } else { 'FAILED' })" -Level $(if ($script:SyncStats.Success) { 'Success' } else { 'Error' }) -Automated:$Automated + Write-Log "Exit Code : $($script:SyncStats.ExitCode)" -Level Info -Automated:$Automated + + if ($script:SyncStats.ErrorMessage) { + Write-Log "Error : $($script:SyncStats.ErrorMessage)" -Level Error -Automated:$Automated + } + + Write-Log "========================================" -Level Info -Automated:$Automated +} + +# Main Business Logic ====================================================== + +function Start-BusinessLogic { + param([switch]$Automated) + + Write-Log "========================================" -Level Info -Automated:$Automated + Write-Log "FreeFileSync Process Started" -Level Info -Automated:$Automated + Write-Log "Script Version: $ScriptVersion ($ScriptDate)" -Level Info -Automated:$Automated + Write-Log "========================================" -Level Info -Automated:$Automated + + # Check prerequisites + if (-not (Test-Prerequisites -Automated:$Automated)) { + Write-Log "Prerequisites check failed. Aborting sync." -Level Error -Automated:$Automated + exit 1 + } + + # Connect to NAS share + if (-not (Connect-NasShare -SharePath $NasRootShare -Automated:$Automated)) { + Write-Log "Failed to connect to NAS share. Aborting sync." -Level Error -Automated:$Automated + exit 1 + } + + # Verify NAS reachability + if (-not (Test-NasReachability -SharePath $NasRootShare -Automated:$Automated)) { + Write-Log "NAS share is not reachable. Continuing anyway..." -Level Warning -Automated:$Automated + } + + # Execute FreeFileSync + $syncResult = Start-FreeFileSyncProcess -Automated:$Automated + + # Print summary + Write-SyncSummary -Automated:$Automated + + # Exit with appropriate code + if (-not $syncResult) { + exit 1 + } +} + +# Entry Point ============================================================== + +if ($Automated) { + if (Get-Command Invoke-ScheduledExecution -ErrorAction SilentlyContinue) { + Invoke-ScheduledExecution ` + -Config $Config ` + -Automated:$Automated ` + -CurrentDateTimeUtc $CurrentDateTimeUtc ` + -ScriptBlock { Start-BusinessLogic -Automated:$Automated } + } + else { + Write-Log "Invoke-ScheduledExecution not available. Execution aborted." -Level Error -Automated:$Automated + exit 1 + } +} +else { + Write-Log "Manual execution started" -Level Info -Automated:$Automated + Start-BusinessLogic -Automated:$Automated +} diff --git a/examples/File-Sync/scriptsettings.json b/examples/File-Sync/scriptsettings.json new file mode 100644 index 0000000..07d1cd5 --- /dev/null +++ b/examples/File-Sync/scriptsettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema", + "title": "File Sync Script Settings", + "description": "Configuration file for file-sync.ps1 script using FreeFileSync", + "version": "1.0.0", + "lastModified": "2026-01-24", + "schedule": { + "runMonth": [], + "runWeekday": ["Monday"], + "runTime": ["00:00"], + "minIntervalMinutes": 10 + }, + "freeFileSyncExe": "C:\\Program Files\\FreeFileSync\\FreeFileSync.exe", + "batchFile": "sync.ffs_batch", + "nasRootShare": "\\\\nassrv0001.corp.maks-it.com\\data-1", + "credentialEnvVar": "nassrv0001", + "_comments": { + "version": "Configuration schema version", + "lastModified": "Last modification date (YYYY-MM-DD)", + "schedule": { + "runMonth": "Array of month names (e.g. 'January', 'June', 'December') to run sync. Empty array = every month.", + "runWeekday": "Array of weekday names (e.g. 'Monday', 'Friday') to run sync. Empty array = every day.", + "runTime": "Array of UTC times in HH:mm format when sync should run.", + "minIntervalMinutes": "Minimum minutes between sync runs to prevent duplicate executions." + }, + "freeFileSyncExe": "Full path to FreeFileSync.exe executable. Typically 'C:\\Program Files\\FreeFileSync\\FreeFileSync.exe'", + "batchFile": "FreeFileSync batch configuration file name (must be in same directory as script). Configure sync paths directly in the .ffs_batch file using FreeFileSync GUI.", + "nasRootShare": "UNC path to NAS root share for authentication. This is only used for connecting to the share, not for modifying batch file paths.", + "credentialEnvVar": "Name of Machine-level environment variable containing Base64-encoded 'username:password' for NAS authentication" + } +} diff --git a/examples/File-Sync/sync.ffs_batch b/examples/File-Sync/sync.ffs_batch new file mode 100644 index 0000000..2a8c651 --- /dev/null +++ b/examples/File-Sync/sync.ffs_batch @@ -0,0 +1,49 @@ + + + + + TimeAndSize + Exclude + + + + + RecycleBin + + + + + * + + + \System Volume Information\ + \$Recycle.Bin\ + \RECYCLE?\ + \Recovery\ + *\thumbs.db + + 0 + 0 + 0 + + + + E:\Users\maksym\source + \\nassrv0001.corp.maks-it.com\data-1\Users\maksym\source + + + + + + + + + + + Action + + + Show + None + +