# WORKING UPLOAD SCRIPT - Birth Biologic Quality Department # Upload all files from Datto to SharePoint via Graph API # CRITICAL: Drive ID uses concatenation to avoid PowerShell escaping param( [string]$ClientSecret = "" # Pass via command line ) $source = "C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department" # CRITICAL: Concatenate drive ID to avoid ! escaping $driveId = "b" + "!" + "F8BzMb1YakCIWCyWlmczb09LHqtxDxVMpLT6kAwYmsM7NUY4oPLSRq7ng3tJq-E9" $tenantId = "19a568e8-9e88-413b-9341-cbc224b39145" $clientId = "709e6eed-0711-4875-9c44-2d3518c47063" Write-Host "========================================================================" Write-Host " Upload ALL Files from Datto to SharePoint via Graph API" Write-Host "========================================================================" Write-Host "" Write-Host "Source: $source" Write-Host "Drive ID: $driveId" Write-Host "" # Get access token Write-Host "Getting Graph API token..." $tokenBody = @{ client_id = $clientId client_secret = $ClientSecret scope = "https://graph.microsoft.com/.default" grant_type = "client_credentials" } try { $tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Body $tokenBody $token = $tokenResponse.access_token Write-Host "[OK] Token acquired" } catch { Write-Host "[ERROR] Failed to get token: $($_.Exception.Message)" exit 1 } Write-Host "" # Get all files from source Write-Host "Scanning Datto files..." $files = Get-ChildItem $source -Recurse -File -ErrorAction SilentlyContinue Write-Host "[OK] Found $($files.Count) files to upload" Write-Host "" # Upload each file $uploaded = 0 $skipped = 0 $errors = 0 $errorDetails = @() Write-Host "Uploading files..." foreach ($file in $files) { $relativePath = $file.FullName.Substring($source.Length + 1) $uploadPath = $relativePath.Replace("\", "/") try { # For files < 4MB, use simple upload if ($file.Length -lt 4MB) { # CRITICAL: Concatenate :/content to avoid escape issues $uploadUrl = "https://graph.microsoft.com/v1.0/drives/$driveId/root:/$uploadPath" + ":/content" $headers = @{ "Authorization" = "Bearer $token" "Content-Type" = "application/octet-stream" } $fileBytes = [System.IO.File]::ReadAllBytes($file.FullName) Invoke-RestMethod -Method Put -Uri $uploadUrl -Headers $headers -Body $fileBytes -UseBasicParsing | Out-Null $uploaded++ if ($uploaded % 100 -eq 0) { Write-Host " Uploaded $uploaded files..." } } else { # For large files, skip for now (need upload session) $skipped++ } } catch { $errors++ $errorDetails += [PSCustomObject]@{ File = $relativePath Error = $_.Exception.Message } # Log first 5 errors if ($errors -le 5) { Write-Host " ERROR: $relativePath - $($_.Exception.Message)" } } } Write-Host "" Write-Host "========================================================================" Write-Host " Upload Complete" Write-Host "========================================================================" Write-Host "" Write-Host "Uploaded: $uploaded" Write-Host "Skipped (>4MB): $skipped" Write-Host "Errors: $errors" Write-Host "Total files: $($files.Count)" Write-Host "" if ($errors -gt 0 -and $errors -le 10) { Write-Host "Error details:" $errorDetails | ForEach-Object { Write-Host " $($_.File): $($_.Error)" } } elseif ($errors -gt 10) { Write-Host "First 10 errors:" $errorDetails | Select-Object -First 10 | ForEach-Object { Write-Host " $($_.File): $($_.Error)" } } if ($uploaded -eq ($files.Count - $skipped)) { Write-Host "[OK] ALL FILES UPLOADED SUCCESSFULLY" exit 0 } else { Write-Host "[WARNING] Some files failed to upload" exit 1 }