fix(Test-ReplicationPermissions): resolve UPN-formatted credentials

Get-ADUser -Identity accepts a DN, GUID, SID, or sAMAccountName - not
a UPN. The SID/tokenGroups resolution stripped only a 'DOMAIN\'
prefix, so a UPN-formatted credential (user@domain.tld) had no
backslash to strip, -Identity threw on the full UPN, and the
exception was swallowed by the surrounding catch (Write-Warning +
return), silently skipping the entire DCSync ACL pre-check.

Now branches on the credential format: DOMAIN\user strips the prefix
as before, user@domain.tld resolves via
-Filter "UserPrincipalName -eq '...'", and a bare sAMAccountName is
used as-is. The follow-up tokenGroups lookup now uses the resolved
DistinguishedName instead of re-deriving the username format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 11:51:41 +02:00
parent ec00518952
commit 91d6bcb216
+18 -4
View File
@@ -348,14 +348,28 @@ function Test-ReplicationPermissions {
$callerSids = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
try {
$samName = $Credential.UserName -replace '^.*\\', ''
$adUser = Get-ADUser -Identity $samName -Server $Server -Credential $Credential `
# Get-ADUser -Identity accepts a DN, GUID, SID, or sAMAccountName - but NOT a UPN. A
# UPN-formatted credential (user@domain.tld) has no backslash, so naively stripping a
# 'DOMAIN\' prefix left the full UPN in place, -Identity threw, and this whole pre-check
# was silently skipped (caught below) regardless of how the username was typed.
$rawUserName = $Credential.UserName
if ($rawUserName -match '^[^\\]+\\(.+)$') {
$adUser = Get-ADUser -Identity $Matches[1] -Server $Server -Credential $Credential `
-Properties SID, DistinguishedName, adminCount -ErrorAction Stop
} elseif ($rawUserName -match '@') {
$adUser = Get-ADUser -Filter "UserPrincipalName -eq '$rawUserName'" -Server $Server -Credential $Credential `
-Properties SID, DistinguishedName, adminCount -ErrorAction Stop | Select-Object -First 1
if (-not $adUser) { throw "No AD user found with UserPrincipalName '$rawUserName'." }
} else {
$adUser = Get-ADUser -Identity $rawUserName -Server $Server -Credential $Credential `
-Properties SID, DistinguishedName, adminCount -ErrorAction Stop
}
[void]$callerSids.Add($adUser.SID.Value)
# tokenGroups is a constructed attribute containing all SIDs in the user's token,
# including nested group memberships - more reliable than walking MemberOf recursively
$adUserWithTokenGroups = Get-ADUser -Identity $samName -Server $Server -Credential $Credential `
# including nested group memberships - more reliable than walking MemberOf recursively.
# Look up by DistinguishedName (unambiguous) rather than re-deriving the username format.
$adUserWithTokenGroups = Get-ADUser -Identity $adUser.DistinguishedName -Server $Server -Credential $Credential `
-Properties tokenGroups -ErrorAction Stop
foreach ($sidBytes in $adUserWithTokenGroups.tokenGroups) {
$sid = New-Object System.Security.Principal.SecurityIdentifier(@([byte[]]$sidBytes), 0)