mirror of
https://github.com/MAKS-IT-COM/uscheduler.git
synced 2026-02-13 22:27:17 +01:00
(feature): freefilesync batch automation example
This commit is contained in:
parent
f337212eee
commit
20c7419b75
@ -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
|
||||
|
||||
---
|
||||
|
||||
|
||||
388
examples/File-Sync/README.md
Normal file
388
examples/File-Sync/README.md
Normal file
@ -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/
|
||||
74
examples/File-Sync/file-sync.bat
Normal file
74
examples/File-Sync/file-sync.bat
Normal file
@ -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%
|
||||
373
examples/File-Sync/file-sync.ps1
Normal file
373
examples/File-Sync/file-sync.ps1
Normal file
@ -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
|
||||
}
|
||||
31
examples/File-Sync/scriptsettings.json
Normal file
31
examples/File-Sync/scriptsettings.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
49
examples/File-Sync/sync.ffs_batch
Normal file
49
examples/File-Sync/sync.ffs_batch
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FreeFileSync XmlType="BATCH" XmlFormat="23">
|
||||
<Notes/>
|
||||
<Compare>
|
||||
<Variant>TimeAndSize</Variant>
|
||||
<Symlinks>Exclude</Symlinks>
|
||||
<IgnoreTimeShift/>
|
||||
</Compare>
|
||||
<Synchronize>
|
||||
<Differences LeftOnly="right" LeftNewer="right" RightNewer="right" RightOnly="right"/>
|
||||
<DeletionPolicy>RecycleBin</DeletionPolicy>
|
||||
<VersioningFolder Style="Replace"/>
|
||||
</Synchronize>
|
||||
<Filter>
|
||||
<Include>
|
||||
<Item>*</Item>
|
||||
</Include>
|
||||
<Exclude>
|
||||
<Item>\System Volume Information\</Item>
|
||||
<Item>\$Recycle.Bin\</Item>
|
||||
<Item>\RECYCLE?\</Item>
|
||||
<Item>\Recovery\</Item>
|
||||
<Item>*\thumbs.db</Item>
|
||||
</Exclude>
|
||||
<SizeMin Unit="None">0</SizeMin>
|
||||
<SizeMax Unit="None">0</SizeMax>
|
||||
<TimeSpan Type="None">0</TimeSpan>
|
||||
</Filter>
|
||||
<FolderPairs>
|
||||
<Pair>
|
||||
<Left>E:\Users\maksym\source</Left>
|
||||
<Right>\\nassrv0001.corp.maks-it.com\data-1\Users\maksym\source</Right>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<Left/>
|
||||
<Right/>
|
||||
</Pair>
|
||||
</FolderPairs>
|
||||
<Errors Ignore="true" Retry="0" Delay="5"/>
|
||||
<PostSyncCommand Condition="Completion"/>
|
||||
<LogFolder/>
|
||||
<EmailNotification Condition="Always"/>
|
||||
<GridViewType>Action</GridViewType>
|
||||
<Batch>
|
||||
<ProgressDialog Minimized="true" AutoClose="true"/>
|
||||
<ErrorDialog>Show</ErrorDialog>
|
||||
<PostSyncAction>None</PostSyncAction>
|
||||
</Batch>
|
||||
</FreeFileSync>
|
||||
Loading…
Reference in New Issue
Block a user