Search code examples
powershelladgroup

PowerShell Function to check group member not working


I found this function I'd like to use in a script I'm writing, but it keeps coming back $false when I can see an account is in a group and I can't figure out why?

function Check-IsGroupMember{

Param($user,$grp)

$strFilter = "(&(objectClass=Group)(name=" + $grp +"))"

$objDomain = New-Object System.DirectoryServices.DirectoryEntry

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
 $objSearcher.SearchRoot = $objDomain
 $objSearcher.PageSize = 1000
 $objSearcher.Filter = $strFilter
 $objSearcher.SearchScope = "Subtree"

$colResults = $objSearcher.FindOne()

$objItem = $colResults.Properties
 ([string]$objItem.member).contains($user)

}
Usage:

Check-IsGroupMember "name of user" "DomainAdmins"

Solution

  • $objItem.member contains the DistinguishedName value of each principal who is a member of the group.

    Even though the proper name of a person might be John Doe, the common name of the user account object may still be Doe, John, John G. Doe or anything else. This means that Contains() check (which is just a simple substring search) is not guaranteed to work as you expect.

    The only real way to check is to either run another search for the user to find his/her DistinguishedName.

    Personally, I would go for the AD PowerShell module from RSAT, rather than using a DirectorySearcher:

    function Test-GroupMembership 
    {
      Param(
        [string]$UserName,
        [string]$GroupName
      )
    
      $User  = Get-ADUser -Identity $UserName
      $Group = Get-ADGroup -Identity $GroupName -Properties member
    
      $Group.member -contains $User.DistinguishedName
    }
    

    If size limit is your problem, you can use the DirectoryServer to retrieve a ranged result of the member attribute:

    function Test-GroupMembership 
    {
      [CmdletBinding()]
      Param(
        [string]$UserName,
        [string]$GroupName
      )
    
      # Fetch User
      $User = Get-ADUser -Identity $UserName
    
      # return on failure
      if(-not $User){
        Write-Error -Message ('User "{0}" not found' -f $GroupName)
        return $false
      }
    
      # Use DirectorySearcher to retrieve ranged member attribute
      $GroupSearcher = '' -as [adsisearcher]
      $GroupSearcher.Filter = '(&(objectClass=group)(name={0}))' -f $GroupName
      $GroupSearcher.SearchScope = 'Subtree'
      $GroupSearcher.SearchRoot = '' -as [adsi]
    
      # AD reponds with at least 1500 values per multi-value attribute since Windows Server 2003
      $Start = 1
      $Range = 1500
      $GroupMembers = @()
    
      $HasMoreMembers = $false
    
      # Keep retrieving member values until we've got them all
      do{
    
        # Use range operator to "page" values
        # Ref: https://msdn.microsoft.com/en-us/library/aa367017(v=vs.85).aspx
        $RangedMember = 'member;range={0}-{1}' -f $Start,$($Start + $Range - 1)
        $GroupSearcher.PropertiesToLoad.Add($RangedMember) | Out-Null
    
        # Retrieve group        
        $Group = $GroupSearcher.FindOne()
    
        # return on failure
        if(-not $Group) {
          Write-Error -Message ('Group "{0}" not found' -f $GroupName)
          return $false
        }
    
        # If we've reached the end of the member list, 
        # AD will return a property where the upper range
        # value is *, so it might not be the same property 
        # name we specified in PropertiesToLoad
        $ReturnedMember = @($Group.Properties.PropertyNames) -like 'member;*'
    
        # Add all members to the $GroupMembers variable
        foreach($member in $Group.Properties."$ReturnedMember") { 
          # Test if user is in the member list
          if($member -eq $User.DistinguishedName){
            return $true
          }
        }
    
        # If we've reached the end, exit the loop
        if($ReturnedMember -eq $RangedPropertyName){
          $HasMoreMembers = $true
        }
    
      } while ($HasMoreMembers)
    
      # User wasn't found
      return $false
    }
    

    To provide a bit of consistency in user experience, please use Approved Verbs for command names in PowerShell (eg. Test-* instead of Check-*)

    [adsisearcher] is a type accelerator for the DirectorySearcher class