mirror of
https://github.com/MAKS-IT-COM/uscheduler.git
synced 2026-02-14 06:37:18 +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
|
## Scripts Examples
|
||||||
|
|
||||||
- [Hyper-V Backup](./examples/HyperV-Backup/README.md) - Production-ready Hyper-V VM backup solution with scheduling and retention management
|
- [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