Files
claudetools/clients/cascades-tucson/docs/migration/scripts/phase2-sync-synology.ps1
Howard Enos 8d975c1b44 import: ingested 160 files from C:\Users\howar\Clients
Howard's personal MSP client documentation folder imported into shared
ClaudeTools repo via /import command. Scope:

Clients (structured MSP docs under clients/<name>/docs/):
- anaise       (NEW)  - 13 files
- cascades-tucson     - 47 files merged (existing had only reports/)
- dataforth           - 18 files merged (alongside incident reports)
- instrumental-music-center - 14 files merged
- khalsa       (NEW)  - 22 files, multi-site (camden, river)
- kittle       (NEW)  - 16 files incl. fix-pdf-preview, gpo-intranet-zone
- lens-auto-brokerage (NEW) - 3 files (name matches SOPS vault)
- _client_template    - 13-file scaffold for new clients

MSP tooling (projects/msp-tools/):
- msp-audit-scripts/ - server_audit.ps1, workstation_audit.ps1, README
- utilities/         - clean_printer_ports, win11_upgrade,
                       screenconnect-toolbox-commands

Credential handling:
- Extracted 1 inline password (Anaise DESKTOP-O8GF4SD / david)
  to SOPS vault: clients/anaise/desktop-o8gf4sd.sops.yaml
- Redacted overview.md with vault reference pattern
- Scanned all 160 files for keys/tokens/connection strings -
  no other credentials found

Skipped:
- Cascades/.claude/settings.local.json (per-machine config)
- Source-root CLAUDE.md (personal, claudetools has its own)
- scripts/server_audit.ps1 and workstation_audit.ps1 at source root
  (identical duplicates of msp-audit-scripts versions)

Memory updates:
- reference_client_docs_structure.md (layout, conventions, active list)
- reference_msp_audit_scripts.md (locations, ScreenConnect 80-char rule)

Session log: session-logs/2026-04-16-howard-client-docs-import.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 19:43:58 -07:00

148 lines
5.7 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
Phase 2.3: Sync data from Synology NAS to CS-SERVER.
.DESCRIPTION
Uses robocopy to pull all shared folder data from the Synology NAS
(192.168.0.120) to D:\Shares on CS-SERVER. Creates missing folders
and SMB shares. Run on CS-SERVER via ScreenConnect.
.NOTES
- Synology must be accessible at \\192.168.0.120
- Run BEFORE phase2-file-shares.ps1 (permissions are set after sync)
- First run may take hours depending on data size
- Subsequent runs are incremental (robocopy only copies changes)
- /MIR is NOT used to avoid accidental deletion — uses /E instead
#>
$NAS = "192.168.0.120"
$DestRoot = "D:\Shares"
Write-Host "=== Phase 2.3: Synology Data Sync ===" -ForegroundColor Cyan
Write-Host ""
# --- Test NAS connectivity ---
Write-Host "--- Testing NAS connectivity ---" -ForegroundColor Yellow
$ping = Test-Connection -ComputerName $NAS -Count 2 -Quiet -ErrorAction SilentlyContinue
if (-not $ping) {
Write-Host "[FAIL] Cannot reach $NAS — aborting" -ForegroundColor Red
exit 1
}
Write-Host " [OK] $NAS is reachable" -ForegroundColor Green
# Test SMB access
try {
$testPath = Test-Path "\\$NAS\Public" -ErrorAction Stop
if ($testPath) {
Write-Host " [OK] SMB access to \\$NAS works" -ForegroundColor Green
} else {
Write-Host " [WARN] \\$NAS\Public not accessible — may need credentials" -ForegroundColor Yellow
Write-Host " Run: net use \\$NAS /user:admin <password>" -ForegroundColor Yellow
}
}
catch {
Write-Host " [WARN] SMB access test failed: $_" -ForegroundColor Yellow
Write-Host " You may need to map the NAS first:" -ForegroundColor Yellow
Write-Host " net use \\$NAS /user:admin <password>" -ForegroundColor Yellow
}
# --- Define sync jobs ---
# Source (Synology share name) -> Destination folder on CS-SERVER
$syncJobs = @(
@{ Name = "Management"; Source = "\\$NAS\Management"; Dest = "$DestRoot\Management" }
@{ Name = "SalesDept"; Source = "\\$NAS\SalesDept"; Dest = "$DestRoot\SalesDept" }
@{ Name = "Server"; Source = "\\$NAS\Server"; Dest = "$DestRoot\Server" }
@{ Name = "chat"; Source = "\\$NAS\chat"; Dest = "$DestRoot\chat" }
@{ Name = "Public"; Source = "\\$NAS\Public"; Dest = "$DestRoot\Public" }
@{ Name = "homes"; Source = "\\$NAS\homes"; Dest = "$DestRoot\homes" }
)
# --- Create destination folders ---
Write-Host "`n--- Creating destination folders ---" -ForegroundColor Yellow
foreach ($job in $syncJobs) {
if (-not (Test-Path $job.Dest)) {
New-Item -Path $job.Dest -ItemType Directory -Force | Out-Null
Write-Host " [OK] Created $($job.Dest)" -ForegroundColor Green
} else {
Write-Host " [SKIP] $($job.Dest) already exists" -ForegroundColor DarkGray
}
}
# --- Sync each share ---
Write-Host "`n--- Syncing data (this may take a while) ---" -ForegroundColor Yellow
Write-Host ""
$logDir = "$DestRoot\IT\Backups\SyncLogs"
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
foreach ($job in $syncJobs) {
Write-Host "=== Syncing: $($job.Name) ===" -ForegroundColor Cyan
Write-Host " From: $($job.Source)" -ForegroundColor DarkGray
Write-Host " To: $($job.Dest)" -ForegroundColor DarkGray
# Check if source is accessible
if (-not (Test-Path $job.Source)) {
Write-Host " [SKIP] Source not accessible: $($job.Source)" -ForegroundColor Yellow
continue
}
$logFile = "$logDir\sync-$($job.Name)-$(Get-Date -Format 'yyyy-MM-dd_HHmm').log"
# Robocopy options:
# /E = copy subdirectories including empty ones
# /Z = restartable mode (resume on network interruption)
# /COPY:DAT = copy Data, Attributes, Timestamps (no permissions — we set those separately)
# /R:3 = retry 3 times on failure
# /W:5 = wait 5 seconds between retries
# /MT:8 = 8 threads for parallel copy
# /XD = exclude directories (@ prefixed Synology system dirs)
# /LOG = log to file
# /NP = no progress percentage (cleaner log)
# /TEE = output to console AND log file
$roboArgs = @(
$job.Source,
$job.Dest,
"/E", "/Z",
"/COPY:DAT",
"/R:3", "/W:5",
"/MT:8",
"/XD", "@eaDir", "@tmp", "#recycle", "#snapshot",
"/XF", "Thumbs.db", ".DS_Store", "desktop.ini",
"/LOG:$logFile",
"/NP", "/TEE"
)
$startTime = Get-Date
& robocopy @roboArgs
$exitCode = $LASTEXITCODE
$elapsed = (Get-Date) - $startTime
# Robocopy exit codes: 0=no change, 1=files copied, 2=extras, 4=mismatches, 8+=errors
if ($exitCode -lt 8) {
Write-Host " [OK] $($job.Name) synced in $([math]::Round($elapsed.TotalMinutes, 1)) minutes (exit code: $exitCode)" -ForegroundColor Green
} else {
Write-Host " [ERROR] $($job.Name) had errors (exit code: $exitCode) — check $logFile" -ForegroundColor Red
}
Write-Host ""
}
# --- Summary ---
Write-Host "=== Sync Complete ===" -ForegroundColor Cyan
Write-Host ""
Write-Host "Folder sizes:" -ForegroundColor Yellow
foreach ($job in $syncJobs) {
if (Test-Path $job.Dest) {
$size = (Get-ChildItem $job.Dest -Recurse -File -ErrorAction SilentlyContinue | Measure-Object Length -Sum).Sum
$sizeGB = [math]::Round($size / 1GB, 2)
$fileCount = (Get-ChildItem $job.Dest -Recurse -File -ErrorAction SilentlyContinue).Count
Write-Host " $($job.Name): $sizeGB GB ($fileCount files)" -ForegroundColor Cyan
}
}
Write-Host ""
Write-Host "Sync logs saved to: $logDir" -ForegroundColor Green
Write-Host "Next: Run phase2-file-shares.ps1 to set permissions and create SMB shares" -ForegroundColor Green