<# .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 }