cascades: CS-SERVER preflight verified + Synology discovery complete

CS-SERVER post-reboot verification: time sync, TLS 1.2 enforcement, and
Windows Server Backup feature all persisted cleanly. dcdiag clean. Ready
for Entra Connect install.

Synology cascadesDS permission inventory captured via DSM API (SSH
disabled by default on Synology). 35 users, 4 groups, 10 shares.
Analysis identifies 7 shared-account role logins (HIPAA violation),
8 departed-employee accounts to clean up, and 4 shares needing
Meredith-side confirmation before migration (pacs most sensitive).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 18:59:38 -07:00
parent 0019f9cd92
commit 717b10d1ae
8 changed files with 5434 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
# ============================================================================
# Synology Permission Discovery via DSM API - cascadesDS (192.168.0.120)
# ----------------------------------------------------------------------------
# Runs on CS-SERVER via GuruRMM. Uses DSM's HTTP API (port 5000) instead of
# SSH since SSH is not enabled on this Synology.
#
# Strictly read-only. Login -> list users/groups/shares -> per-share
# permissions -> logout.
#
# Prepared: 2026-04-22 (Cascades Phase 4 Synology retirement prep)
# ============================================================================
$ErrorActionPreference = 'Continue'
$SynoBase = 'http://192.168.0.120:5000'
$SynoUser = 'admin'
$SynoPass = '__SYNO_PASSWORD__'
function Section($n) {
Write-Output ''
Write-Output ('=' * 72)
Write-Output "== $n"
Write-Output ('=' * 72)
}
function Dump($label, $obj) {
Write-Output ''
Write-Output "--- $label ---"
try {
$obj | ConvertTo-Json -Depth 8 | Write-Output
} catch {
$obj | Format-List * | Out-String | Write-Output
}
}
# TLS changes queued for tonight but not yet effective (reboot @ 18:00). This
# talks HTTP port 5000 so TLS state doesn't matter here.
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
# ----------------------------------------------------------------------------
Section 'Step 0: API version discovery'
# ----------------------------------------------------------------------------
try {
$info = Invoke-RestMethod -Uri "$SynoBase/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth,SYNO.Core.User,SYNO.Core.Group,SYNO.Core.Share,SYNO.Core.Share.Permission,SYNO.Core.Group.Member"
Dump 'SYNO.API.Info' $info
} catch {
Write-Output "API info probe failed: $_"
exit 1
}
# Derive authenticate path + max version
$authPath = $info.data.'SYNO.API.Auth'.path
$authMax = $info.data.'SYNO.API.Auth'.maxVersion
Write-Output "auth path: $authPath, maxVersion: $authMax"
# ----------------------------------------------------------------------------
Section 'Step 1: Login'
# ----------------------------------------------------------------------------
$loginUri = "$SynoBase/webapi/$authPath" +
"?api=SYNO.API.Auth&version=$authMax&method=login" +
"&account=$([uri]::EscapeDataString($SynoUser))" +
"&passwd=$([uri]::EscapeDataString($SynoPass))" +
"&session=FileStation&format=sid"
try {
$loginResp = Invoke-RestMethod -Uri $loginUri
Dump 'login response' $loginResp
if (-not $loginResp.success) {
Write-Output "LOGIN FAILED: $($loginResp | ConvertTo-Json -Depth 5)"
exit 1
}
$sid = $loginResp.data.sid
Write-Output "sid: $sid"
} catch {
Write-Output "Login exception: $_"
exit 1
}
try {
# ------------------------------------------------------------------------
Section 'Step 2: Users'
# ------------------------------------------------------------------------
# SYNO.Core.User list with all additional fields
$userResp = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.User&version=1&method=list" +
"&offset=0&limit=500&type=local" +
"&additional=%5B%22email%22%2C%22description%22%2C%22expired%22%2C%22cannot_chg_passwd%22%2C%22passwd_never_expire%22%2C%22passwd_last_change%22%2C%22passwd_must_change%22%2C%22groups%22%5D" +
"&_sid=$sid")
Dump 'SYNO.Core.User list' $userResp
# ------------------------------------------------------------------------
Section 'Step 3: Groups'
# ------------------------------------------------------------------------
$groupResp = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.Group&version=1&method=list" +
"&offset=0&limit=500&type=local" +
"&additional=%5B%22description%22%2C%22group_type%22%2C%22members%22%5D" +
"&_sid=$sid")
Dump 'SYNO.Core.Group list' $groupResp
# Group members (separate call in some DSM versions)
$groups = $groupResp.data.groups
foreach ($g in $groups) {
try {
$memResp = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.Group.Member&version=1&method=list" +
"&group_name=$([uri]::EscapeDataString($g.name))" +
"&offset=0&limit=500&_sid=$sid")
Dump "members of group '$($g.name)'" $memResp
} catch {
Write-Output "Group member lookup for '$($g.name)' failed: $_"
}
}
# ------------------------------------------------------------------------
Section 'Step 4: Shares'
# ------------------------------------------------------------------------
$shareResp = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.Share&version=1&method=list" +
"&shareType=all&offset=0&limit=200" +
"&additional=%5B%22hidden%22%2C%22encryption%22%2C%22share_quota%22%2C%22recyclebin%22%2C%22enable_share_cow%22%2C%22enable_share_compress%22%2C%22name%22%2C%22vol_path%22%2C%22desc%22%5D" +
"&_sid=$sid")
Dump 'SYNO.Core.Share list' $shareResp
# ------------------------------------------------------------------------
Section 'Step 5: Per-share permissions'
# ------------------------------------------------------------------------
$shares = $shareResp.data.shares
foreach ($sh in $shares) {
try {
# Permissions by user
$permUser = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.Share.Permission&version=1&method=list" +
"&name=$([uri]::EscapeDataString($sh.name))" +
"&user_group_type=local_user&offset=0&limit=500" +
"&_sid=$sid")
Dump "share '$($sh.name)' - local user permissions" $permUser
# Permissions by group
$permGroup = Invoke-RestMethod -Uri ("$SynoBase/webapi/entry.cgi?api=SYNO.Core.Share.Permission&version=1&method=list" +
"&name=$([uri]::EscapeDataString($sh.name))" +
"&user_group_type=local_group&offset=0&limit=500" +
"&_sid=$sid")
Dump "share '$($sh.name)' - local group permissions" $permGroup
} catch {
Write-Output "Permission lookup for '$($sh.name)' failed: $_"
}
}
# ------------------------------------------------------------------------
Section 'Step 6: SMB share config (supplementary)'
# ------------------------------------------------------------------------
# SMB exposure state per share via FileService.SMB.Share or similar
try {
$smb = Invoke-RestMethod -Uri "$SynoBase/webapi/entry.cgi?api=SYNO.Core.FileServ.SMB&version=1&method=get&_sid=$sid"
Dump 'SMB service config' $smb
} catch {
Write-Output "SMB service config lookup failed: $_"
}
} finally {
# ------------------------------------------------------------------------
Section 'Logout'
# ------------------------------------------------------------------------
try {
$logout = Invoke-RestMethod -Uri "$SynoBase/webapi/$authPath?api=SYNO.API.Auth&version=$authMax&method=logout&session=FileStation&_sid=$sid"
Dump 'logout' $logout
} catch {
Write-Output "Logout failed: $_"
}
}
Section 'Done'
Write-Output "Completed at $(Get-Date)"