MarcGel
MarcGel

Reputation: 301

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"

Upvotes: 0

Views: 989

Answers (1)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174485

$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

Upvotes: 1

Related Questions