skip to Main Content

I’m having a PowerShell syntax issue: I can’t seem to match members of one array to another array, but I can confirm the members exist in both arrays. Can someone point out my logic error?

Background:

I want to search Entra for all members of a specific group, based on a search string of the member’s properties (e.g. DisplayName and Surname). Given limitations to the Get-Mg* -filter calls, I’ve come up with this approach:

$groupName = 'staff'            # all results should be in staff group
$userMatchString = 'Be'         # e.g. [Be]n Smith, Jim [Be]am...
$domainMatch = 'example.com'    # custom domain - no Entra guests

## choose a central M365 group filter
$group = get-mggroup -Filter "DisplayName eq '$($groupName)'"
$groupMemberIds=Get-MgGroupMember -GroupId $group.id  -ConsistencyLevel eventual -All | select Id

## find Entra users matching a search string
## Due to graph filter limitations we do separate calls per attribute
$userMatches=@(Get-MgUser -ConsistencyLevel eventual -Count userCount -Filter "startsWith(DisplayName, '$($userMatchString)')" | where { $_.Mail -like "*@$($domainMatch)"})

$userMatches+=@(Get-MgUser -ConsistencyLevel eventual -Count userCount -Filter "startsWith(Surname, '$($userMatchString)')" | where { $_.Mail -like "*@$($domainMatch)" })

At this point $groupMemberIds contains every qualifiable person to search for and $userMatches contains every Entra search result (that may or may not be in the group).

This confirms (for my resultset) that the first record is already a match: $groupMemberIds | where { $_.Id -eq $userMatches[0].Id }

Can someone help me with syntax for (any userMatches ) in (groupMemberIds)?

This doesn’t work – it returns ‘false’ for all of them:
$userMatches | ForEach-Object { $($_.Id -in $groupMemberIds) }

Any ideas? Thanks!

UPDATE:
With my code I’m apparently comparing string to PSCustomObject… trying to figure out how to compare these as apples to apples…

$userMatches[0].Id.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

$groupMemberIds[0].gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

4

Answers


  1. Chosen as BEST ANSWER

    In order to compare these apples to apples, I need to specify the .id property of the array members versus the array members themselves.

    This version with $groupMemberIds.id is returning matches, as it is now a String comparison:

    $userMatches | ForEach-Object {
        if ($_.Id -in $groupMemberIds.id) { 
            "{0} was found in {1}" -f  $_.DisplayName,$groupName 
            }
        }
    

  2. I’m unsure as to which limitation you’re referring to but what it seems you’re looking to achieve is certainly doable through the OData filter. I’m not sure if you can do it with the Get-MgGroupMember cmdlet but using Invoke-MgGraphRequest you can do:

    $groupName = 'staff'            # all results should be in staff group
    $userMatchString = 'Be'         # e.g. [Be]n Smith, Jim [Be]am...
    $domainMatch = 'example.com'    # custom domain - no Entra guests
    
    $group = Invoke-MgGraphRequest GET "v1.0/groups?$filter=displayName eq '$groupName'"
    $groupId = $group.value.Id
    $uri = "v1.0/groups/$groupId/members?`$filter=startsWith(displayName, '$userMatchString') and endsWith(mail, '$domainMatch')&`$count=true"
    
    $result = do {
        $response = Invoke-MgGraphRequest GET $uri -Headers @{
            ConsistencyLevel = 'eventual'
        }
        $uri = $response.'@odata.nextLink'
        if ($response.value) {
            $response.value
        }
    }
    while ($uri)
    
    # do stuff with result
    $result | Select-Object id, displayName, mail
    
    Login or Signup to reply.
  3. There’s a much easier approach. When you use Get-MgGroupMember you will retrieve all the user properties you need to do your filtering. But your script is stripping these values when you pipe your output to | Select id.

    You can do it like this:

    $groupName = 'staff'            # all results should be in staff group
    $userMatchString = 'Be'         # e.g. [Be]n Smith, Jim [Be]am...
    $domainMatch = 'example.com'    # custom domain - no Entra guests
    
    ## choose a central M365 group filter
    $group = get-mggroup -Filter "DisplayName eq '$($groupName)'"
    $groupMembers=Get-MgGroupMember -GroupId $group.id -ConsistencyLevel eventual -All
    
    ## find Entra users matching a search string
    ## Due to graph filter limitations we do separate calls per attribute
    $userMatches = $groupMembers | Where { `
        ($_.DisplayName -like "*$userMatchString*") -and `
        ($_.Mail -like "*@*$domainMatch*") `
    }
    
    # Show user matches
    $userMatches | Select DisplayName,Mail | ft
    
    Login or Signup to reply.
  4. Although my other answer solves the root question I had (comparing the arrays), this alternate answer is arguably a more efficient way to compare the results. Kudos to Architect Jamie and Santiago Squarzon for their help!

    Full example:

    $groupName = 'staff'            # all results should be in staff group
    $userMatchString = 'Be'         # e.g. [Be]n Smith, Jim [Be]am...
    $domainMatch = '@example.com'   # custom domain - no Entra guests
    
    ## choose a central M365 group filter
    $group = get-mggroup -Filter "DisplayName eq '$($groupName)'"
    
    ## find Entra users matching a search string
    ## Due to graph filter limitations we do separate calls per attribute
    $userMatches=@(Get-MgUser -ConsistencyLevel eventual -Count userCount -Filter "startsWith(DisplayName, '$($userMatchString)')" | where { $_.Mail -like "*@$($domainMatch)"})
    $userMatches+=@(Get-MgUser -ConsistencyLevel eventual -Count userCount -Filter "startsWith(Surname, '$($userMatchString)')" | where { $($_.Mail).EndsWith($domainMatch) })
    
    ## check 1:1 if the users we found are in our group.
    ## this call allows multiple groupID matching so it could be extended
    ## to filter if the user is in any vs all vs threshold amount of groups...
    $usermatches | where { (Confirm-MgUserMemberGroup -UserId $_.Id -GroupIds @($group.Id)) }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search