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>
148 lines
5.7 KiB
PowerShell
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
|