J2897 icon

AutomatedFlashDriveSync

J2897 | PRO | 04/04/25 01:34:29 PM UTC (Edited) | 0 ⭐ | 1266 👁️ | Never ⏰ | []
PowerShell |

5.9 KB

|

None

|

0 👍

/

0 👎

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

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎