<#
.SYNOPSIS
Automates USB drive synchronization using CSV-defined mappings with event-driven monitoring.
To install as a service, use the following commands:
nssm install "AutomatedFlashDriveSync" "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -File "C:\Users\J2897\Code\PowerShell\DriveSync\AutomatedFlashDriveSync.ps1"
nssm set "AutomatedFlashDriveSync" Description "Automates the synchronization of USB drives with specified directories using a CSV-defined mapping."
.DESCRIPTION
This script:
1. Runs as a service monitoring USB insertion events via WMI
2. Performs automatic/manual syncs using device mappings from DeviceMapping.csv
3. Uses Robocopy for file synchronization with detailed logging
4. Supports multiple partitions per device
.OPERATION MODES
- Automatic: Triggers on USB insertion (Win32_DiskDrive creation event)
- Manual: Press 'S' to force sync, 'Q' to quit
.SYNC FEATURES
- Automatic drive letter detection
- Path validation (source/target)
- Logging with timestamps
- Error handling with exit codes
- Safe retries (R:3/W:5)
.REQUIREMENTS
- DeviceMapping.csv from mapping script (USB_Drive_Mapper_CSV.ps1)
- Robocopy available in PATH
- Admin privileges for WMI events
.NOTES
CSV must contain: DeviceID,SourceDirectory,TargetDirectory
Safe exit code range: Robocopy codes < 8
#>
# Path to CSV file (global variable)
$global:csvPath = "C:\Users\J2897\Code\PowerShell\DriveSync\DeviceMapping.csv"
function global:Scan-Devices {
param (
[Parameter(Mandatory=$true)]
[string]$csvFile
)
try {
Write-Host "`n[SCAN STARTED] Loading CSV from: $csvFile"
$devices = Import-Csv -Path $csvFile
Write-Host "Loaded $($devices.Count) device mappings"
# Wait for drive initialization
Write-Host "Waiting 5 seconds for drive preparation..."
1..5 | ForEach-Object {
Write-Host " - Second $_/5"
Start-Sleep -Seconds 1
}
# Get fresh device lists
$diskDrives = Get-WmiObject -Class Win32_DiskDrive | Where-Object { $_.PNPDeviceID -like "USBSTOR*" }
Write-Host "Detected USB disk drives: $($diskDrives.Count)"
foreach ($device in $devices) {
$trimmedFriendlyName = $device.FriendlyName.Trim()
Write-Host "`nProcessing device: $trimmedFriendlyName ($($device.DeviceID))"
# Find matching disk drive
$matchingDisk = $diskDrives | Where-Object { $_.PNPDeviceID -eq $device.DeviceID }
if (-not $matchingDisk) {
Write-Host " No matching disk found!"
continue
}
Write-Host " Found matching disk: $($matchingDisk.Model)"
# Get drive letters
$driveLetters = $matchingDisk.GetRelated("Win32_DiskPartition") |
ForEach-Object { $_.GetRelated("Win32_LogicalDisk") } |
Select-Object -ExpandProperty DeviceID
if (-not $driveLetters) {
Write-Host " No drive letters found!"
continue
}
Write-Host " Drive letters: $($driveLetters -join ', ')"
foreach ($letter in $driveLetters) {
# Ensure correct source path handling
$sourcePath = if ($device.SourceDirectory -and $device.SourceDirectory.Trim() -ne "") {
"$letter\$($device.SourceDirectory)"
} else {
"$letter\"
}
Write-Host "`n Attempting sync from: $sourcePath"
if (Test-Path $sourcePath) {
Write-Host " Source path verified - starting sync..."
# Ensure the target directory exists
if (-not (Test-Path $device.TargetDirectory)) {
Write-Host " Target directory does not exist. Creating: $($device.TargetDirectory)"
New-Item -ItemType Directory -Path $device.TargetDirectory -Force | Out-Null
}
$logPath = Join-Path $device.TargetDirectory "SyncLog_$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
# Ensure correct formatting for source and target paths
$robocopyParams = @(
$sourcePath,
$device.TargetDirectory,
"/E", "/COPY:DAT", "/R:3", "/W:5", "/LOG:$logPath",
"/NP", "/V"
)
Write-Host " Starting sync with Robocopy..."
try {
& robocopy @robocopyParams | ForEach-Object { Write-Host $_ }
if ($LASTEXITCODE -lt 8) {
Write-Host " Sync completed successfully (Exit Code: $LASTEXITCODE)"
} else {
Write-Host " Sync completed with warnings (Exit Code: $LASTEXITCODE)"
}
} catch {
Write-Host " Sync failed: $_"
}
}
else {
Write-Host " Source path not found!"
}
}
}
}
catch {
Write-Host "[CRITICAL ERROR] $_"
}
Write-Host "`n[SCAN COMPLETED]"
}
# Event registration
$null = Register-WmiEvent -Query @"
SELECT * FROM __InstanceCreationEvent WITHIN 2
WHERE TargetInstance ISA 'Win32_DiskDrive'
AND TargetInstance.InterfaceType = 'USB'
"@ -SourceIdentifier "USBInserted" -Action {
Write-Host "`n[EVENT TRIGGERED] Starting scan sequence..."
Scan-Devices -csvFile $global:csvPath
}
# Monitoring loop
Write-Host "Monitoring USB devices. Press 'S' to scan manually, 'Q' to quit."
try {
while ($true) {
if ([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true).Key
if ($key -eq [ConsoleKey]::S) {
Write-Host "`n[MANUAL SCAN]"
Scan-Devices -csvFile $global:csvPath
}
elseif ($key -eq [ConsoleKey]::Q) {
break
}
}
Start-Sleep -Milliseconds 100
}
}
finally {
Write-Host "Exiting..."
Get-EventSubscriber -SourceIdentifier "USBInserted" | Unregister-Event -ErrorAction SilentlyContinue
}
Comments
0 B
|👍
/👎