# Mirror Datto Quality Department to SharePoint via OneDrive sync # Uses robocopy /MIR to make destination EXACTLY match source # Deletes files in SharePoint that don't exist in Datto param( [switch]$Confirm, [switch]$DryRun ) $ErrorActionPreference = 'Stop' $DattoSource = "C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department" $SharePointDest = "C:\Users\Administrator\OneDrive - Birth Biologic, LLC\Quality Systems Department\Documents" Write-Host "[INFO] Starting mirror from Datto to SharePoint via OneDrive sync..." Write-Host "[INFO] Source (Datto): $DattoSource" Write-Host "[INFO] Destination (SharePoint/OneDrive): $SharePointDest" Write-Host "" # Verify paths exist if (-not (Test-Path $DattoSource)) { throw "Source path does not exist: $DattoSource" } if (-not (Test-Path $SharePointDest)) { throw "Destination path does not exist: $SharePointDest" } # Get file counts before $beforeCount = (Get-ChildItem $SharePointDest -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count Write-Host "[INFO] Files in SharePoint before: $beforeCount" Write-Host "" # Show what robocopy will do (dry run) Write-Host "[DRY RUN] Simulating robocopy /MIR (mirror)..." Write-Host "" $dryRunLog = "C:\temp\robocopy-quality-dryrun.log" New-Item -Path "C:\temp" -ItemType Directory -Force | Out-Null robocopy $DattoSource $SharePointDest /MIR /L /NP /NDL /NFL /LOG:$dryRunLog /R:0 /W:0 # Parse dry run log to show what will be deleted $logContent = Get-Content $dryRunLog -Raw if ($logContent -match '\*EXTRA File\s+(.+)') { Write-Host "[WARNING] Files that will be DELETED from SharePoint (not in Datto):" Write-Host "" Get-Content $dryRunLog | Where-Object { $_ -match '\*EXTRA File' } | ForEach-Object { if ($_ -match '\*EXTRA File\s+\d+\s+(.+)') { Write-Host " - $($Matches[1])" } } Write-Host "" } # Count files to be deleted $extraFiles = (Get-Content $dryRunLog | Where-Object { $_ -match '\*EXTRA File' }).Count Write-Host "[INFO] $extraFiles file(s) will be deleted" Write-Host "" # Exit after dry run if -DryRun specified if ($DryRun) { Write-Host "[DRY RUN ONLY] Use -Confirm to execute actual mirror" exit 0 } # Confirm before proceeding if (-not $Confirm) { $response = Read-Host "Proceed with ACTUAL mirror? This will DELETE $extraFiles files from SharePoint. Type 'yes' to confirm" if ($response -ne 'yes') { Write-Host "[CANCELLED] No changes made" exit 1 } } else { Write-Host "[INFO] Running with -Confirm switch, proceeding automatically..." } # Execute actual mirror Write-Host "" Write-Host "[INFO] Executing robocopy /MIR..." Write-Host "" $actualLog = "C:\temp\robocopy-quality-actual.log" # /MIR = mirror (copy + purge extra files) # /R:0 = no retries on failed copies # /W:0 = no wait between retries # /MT:8 = use 8 threads # /LOG = write log file robocopy $DattoSource $SharePointDest /MIR /R:0 /W:0 /MT:8 /LOG:$actualLog /NP $exitCode = $LASTEXITCODE # Robocopy exit codes: 0-7 are success/warnings, 8+ are errors if ($exitCode -ge 8) { throw "Robocopy failed with exit code $exitCode - check $actualLog" } # Get file counts after $afterCount = (Get-ChildItem $SharePointDest -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count Write-Host "" Write-Host "[OK] Mirror complete" Write-Host "[INFO] Files in SharePoint after: $afterCount (was $beforeCount)" Write-Host "[INFO] Files deleted: $($beforeCount - $afterCount)" Write-Host "[INFO] Full log: $actualLog" Write-Host "" Write-Host "[INFO] OneDrive will now sync these changes to SharePoint..." Write-Host ""