Report Active Directory Group Membership with dsget
- Feb 15
- 2 min read

Reporting Active Directory Group Membership is probably one of the most common tasks for both AD admins and security professionals. Wide variety of tools are available to do the job. However, native tools like Get-ADGroupMember come with caveats which will most likely be noticed in a bit more complex environments with AD trusts and historical misconfigurations. For example:
Get-ADGroupMember fails with "unspecified error" if group contains foreignSecurityPrincipal
member attribute from Get-ADGroup works even if group contains foreignSecurityPrincipal. However, and it's not well documented, user's primary group will not be listed when querying member or memberOf. In many environments, admins change primary group and in many cases set it to a privileged group. So your report will be missing critical data if you rely on member or memberOf attribute.
The solution is the built-in tool which still comes with latest version of Windows with ADDS role (or tools) installed - the dsget. It only outputs DNs of members so additional massaging is required, but it will report all members of the group with no errors or exceptions.
Below is a ready to use function based on dsget. Few notes:
UAC decoder is a pre-req. I borrowed the function from this great post.
Populate list of domains under ValidateSet with names of your domains.
function Decode-UserAccountControl ([int]$UAC)
{
$UACPropertyFlags = @(
"SCRIPT",
"ACCOUNTDISABLED",
"RESERVED",
"HOMEDIR_REQUIRED",
"LOCKOUT",
"PASSWD_NOTREQD",
"PASSWD_CANT_CHANGE",
"ENCRYPTED_TEXT_PWD_ALLOWED",
"TEMP_DUPLICATE_ACCOUNT",
"NORMAL_ACCOUNT",
"RESERVED",
"INTERDOMAIN_TRUST_ACCOUNT",
"WORKSTATION_TRUST_ACCOUNT",
"SERVER_TRUST_ACCOUNT",
"RESERVED",
"RESERVED",
"DONT_EXPIRE_PASSWORD",
"MNS_LOGON_ACCOUNT",
"SMARTCARD_REQUIRED",
"TRUSTED_FOR_DELEGATION",
"NOT_DELEGATED",
"USE_DES_KEY_ONLY",
"DONT_REQ_PREAUTH",
"PASSWORD_EXPIRED",
"TRUSTED_TO_AUTH_FOR_DELEGATION",
"RESERVED",
"PARTIAL_SECRETS_ACCOUNT"
"RESERVED"
"RESERVED"
"RESERVED"
"RESERVED"
"RESERVED"
)
return (0..($UACPropertyFlags.Length) | ? {$UAC -bAnd [math]::Pow(2,$_)} | %{$UACPropertyFlags[$_]})
} # end Function Decode-UserAccountControl
function Get-GroupMembership
{
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$GroupName,
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[ValidateSet("domain1", "domain2", "domain3")]
$Domain
)
$group = Get-ADGroup $GroupName -Server $domain -ErrorAction SilentlyContinue
if ($group)
{
$membership_report = @()
$members = dsget group $($group.DistinguishedName) -member -expand | ? {$_} | % {$_.trim('"').Trim()}
foreach ($member in $members)
{
Write-Host "Working on $member" -ForegroundColor Green
$UAC_Decoded = $null
$member = $member | Out-String
$obj = Get-ADObject -Filter {DistinguishedName -eq $member} -Properties *
$Friendly_Name = if ($obj.ObjectClass -eq "user")
{
$obj.DisplayName
}
elseif ($obj.ObjectClass -eq "foreignSecurityPrincipal")
{
(New-Object System.Security.Principal.SecurityIdentifier($($obj.Name))).Translate([System.Security.Principal.NTAccount]).Value
}
elseif ($obj.ObjectClass -eq "msDS-GroupManagedServiceAccount")
{
$obj.Name
}
elseif (!$obj)
{
$member
}
if ($obj.userAccountControl)
{
$UAC_Decoded = Decode-UserAccountControl $($obj.userAccountControl)
}
$membership_report += $member | Select @{N="Group"; E={$group}},@{N="Member Friendly Name"; E={$Friendly_Name}}, `
@{N="Enabled"; E={if ($UAC_Decoded) {if ($UAC_Decoded -contains "ACCOUNTDISABLED") {"False"} else {"True"}} else {"N/A"}}}, ` # https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
@{N="sAMAccountName"; E={$obj.sAMAccountName}}, ` @{N="userPrincipalName"; E={$obj.userPrincipalName}}, ` @{N="ObjectClass"; E={$obj.ObjectClass}}, ` @{N="Created"; E={$obj.Created}}, ` @{N="lastLogonTimestamp"; E={if ($obj.lastLogonTimestamp -and $obj.lastLogonTimestamp -ne "0") { [DateTime]::FromFileTime($($obj.lastLogonTimestamp)).ToString('yyyy/MM/dd hh:mm:ss tt') }}}, ` @{N="Password Last Set"; E={([datetime]::FromFileTime($obj.pwdLastSet)).ToString("yyy-MM-dd HH:mm:ss")}}, ` @{N="No password is required"; E={if ($UAC_Decoded) {if ($UAC_Decoded -contains "PASSWD_NOTREQD") {"True"} else {"False"}} else {"N/A"}}}, ` @{N="Description"; E={$obj.Description}}, ` @{N="CanonicalName"; E={$obj.CanonicalName}}, ` @{N="DistinguishedName"; E={$obj.DistinguishedName}}
}
$membership_report
} # end if group
else {Write-Host "Group $groupname was not found in domain $domain" -ForegroundColor Red}
} # end function Get-GroupMembership
References:



