#!/usr/bin/env python3 """ Reset Quality Systems Department to EXACTLY match Datto. Step 1: Delete everything from SharePoint Step 2: Use robocopy to mirror Datto to OneDrive sync folder (will create new site) """ import requests import subprocess import time import json RMM_API = "http://172.16.3.30:3001" ADMIN_EMAIL = "claude-api@azcomputerguru.com" ADMIN_PASS = "ClaudeAPI2026!@#" AGENT_ID = "a4524e85-8a07-45d0-91b1-51ce7e2ca74a" TENANT_ID = "19a568e8-9e88-413b-9341-cbc224b39145" CLIENT_ID = "709e6eed-0711-4875-9c44-2d3518c47063" def vault(path, field): r = subprocess.run(["bash", ".claude/scripts/vault.sh", "get-field", path, field], capture_output=True, text=True, cwd="/Users/azcomputerguru/ClaudeTools") return r.stdout.strip() def rmm_cmd(script, timeout=300): """Run PS command via RMM, return stdout""" token = requests.post(f"{RMM_API}/api/auth/login", json={"email": ADMIN_EMAIL, "password": ADMIN_PASS}).json()["token"] r = requests.post(f"{RMM_API}/api/agents/{AGENT_ID}/command", headers={"Authorization": f"Bearer {token}"}, json={"command_type": "powershell", "command": script, "timeout_seconds": timeout}) cmd_id = r.json()["command_id"] print(f" Command {cmd_id} submitted...") for i in range(int(timeout/5) + 10): time.sleep(5) r = requests.get(f"{RMM_API}/api/commands/{cmd_id}", headers={"Authorization": f"Bearer {token}"}) cmd = r.json() if cmd["status"] == "completed": return cmd.get("stdout", "") elif cmd["status"] == "failed": raise Exception(f"RMM failed: {cmd.get('stderr', '')}") if i % 6 == 0 and i > 0: print(f" Still running ({i*5}s)...") raise Exception("RMM timeout") def get_graph_token(): secret = vault("msp-tools/computerguru-tenant-admin", "credentials.client_secret") r = requests.post(f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data={"client_id": CLIENT_ID, "client_secret": secret, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials"}) return r.json()["access_token"] print("="*60) print("RESET Quality Systems Department to EXACT Datto match") print("="*60) print() print("[STEP 1/3] Delete ALL files from SharePoint Quality Systems Department...") print() token = get_graph_token() # Get site/drive IDs r = requests.get("https://graph.microsoft.com/v1.0/sites/birthbiologic.sharepoint.com:/sites/QualitySystemsDepartment", headers={"Authorization": f"Bearer {token}"}) site_id = r.json()["id"] r = requests.get(f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives", headers={"Authorization": f"Bearer {token}"}) drive = next(d for d in r.json()["value"] if d["name"] == "Documents") drive_id = drive["id"] # Get all items (files and folders) at root r = requests.get(f"https://graph.microsoft.com/v1.0/drives/{drive_id}/root/children", headers={"Authorization": f"Bearer {token}"}) items = r.json()["value"] print(f" Found {len(items)} root items to delete...") deleted = 0 for item in items: try: requests.delete(f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item['id']}", headers={"Authorization": f"Bearer {token}"}).raise_for_status() deleted += 1 print(f" Deleted: {item['name']}") except Exception as e: print(f" ERROR deleting {item['name']}: {e}") print(f"\n Deleted {deleted}/{len(items)} root items") print() print("[STEP 2/3] Copy ALL files from Datto to SharePoint via robocopy + OneDrive...") print() # Create a new OneDrive folder for Quality Systems Department and robocopy everything script = r''' $ErrorActionPreference = 'Stop' $source = "C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department" $oneDriveRoot = "C:\Users\Administrator\OneDrive - Birth Biologic, LLC" $dest = Join-Path $oneDriveRoot "Quality Systems Department - Documents" Write-Host "Source: $source" Write-Host "Destination: $dest" Write-Host "" # Create destination if it doesn't exist if (-not (Test-Path $dest)) { New-Item -Path $dest -ItemType Directory -Force | Out-Null Write-Host "Created destination folder" } # Robocopy - mirror source to destination Write-Host "Running robocopy to mirror Datto to OneDrive..." Write-Host "" $logFile = "C:\temp\robocopy-quality-reset.log" New-Item -Path "C:\temp" -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null # /MIR = mirror (copy all + purge extra) # /MT:8 = 8 threads # /R:3 = 3 retries # /W:5 = wait 5 seconds between retries # /NP = no progress (cleaner output) robocopy $source $dest /MIR /MT:8 /R:3 /W:5 /LOG:$logFile /TEE $exitCode = $LASTEXITCODE # Robocopy exit codes: 0-7 are success, 8+ are errors if ($exitCode -ge 8) { Write-Error "Robocopy failed with exit code $exitCode" exit $exitCode } Write-Host "" Write-Host "Robocopy complete (exit code $exitCode)" # Count files copied $fileCount = (Get-ChildItem $dest -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count Write-Host "Files in destination: $fileCount" Write-Host "" Write-Host "OneDrive will now sync to SharePoint..." ''' print(" Running robocopy (this may take several minutes for 3768 files)...") result = rmm_cmd(script, timeout=1800) # 30 minute timeout print(result) print() print("[STEP 3/3] Verifying sync...") print() # Wait for OneDrive to sync (give it 2 minutes) print(" Waiting 2 minutes for OneDrive to sync to SharePoint...") for i in range(24): time.sleep(5) if i % 6 == 0: print(f" {i*5}s elapsed...") # Refresh token and check SharePoint token = get_graph_token() # Count files in SharePoint sp_files = [] def get_items(item_id="root", prefix=""): r = requests.get(f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{item_id}/children?$top=5000", headers={"Authorization": f"Bearer {token}"}) for item in r.json()["value"]: path = f"{prefix}/{item['name']}".lstrip("/") if "folder" in item: get_items(item["id"], path) else: sp_files.append({"Path": path}) get_items() print(f"\n SharePoint now has {len(sp_files)} files") print() # Get Datto count for comparison script2 = r''' $root = "C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department" $count = (Get-ChildItem $root -Recurse -File -ErrorAction SilentlyContinue | Measure-Object).Count Write-Host $count ''' datto_count = int(rmm_cmd(script2, 60).strip()) print(f" Datto source has {datto_count} files") print() if len(sp_files) == datto_count: print("[SUCCESS] SharePoint matches Datto EXACTLY!") else: print(f"[WARNING] File count mismatch: SharePoint={len(sp_files)}, Datto={datto_count}") print(" OneDrive may still be syncing. Check again in a few minutes.") print() print("="*60) print("RESET COMPLETE") print("="*60)