msgraph-sdk-powershell icon indicating copy to clipboard operation
msgraph-sdk-powershell copied to clipboard

Unable to find a way to fetch cloud only groups with zero members in it. All examples or graph APIs that are available only provide output of 10-12 groups

Open hg185024 opened this issue 1 year ago • 3 comments

Install the Microsoft.Graph PowerShell module

#Install-Module -Name Microsoft.Graph -Force -AllowClobber

Import the module

#Import-Module Microsoft.Graph

Connect to Microsoft Graph (You will be prompted to sign in)

Connect-MgGraph

Get all Microsoft 365 groups

$groups = Get-MgGroup -Filter "mailEnabled eq false and securityEnabled eq true " -Top 100

Iterate through each group and check for zero membersUnified

foreach ($group in $groups) {

$members = Get-MgGroupMember -GroupId $group.Id

if ($members.Count -eq 0) {

    Write-Host "Group $($group.DisplayName) has zero members."

    # You can perform additional actions here if needed

}

}

Disconnect from Microsoft Graph

Disconnect-MgGraph

Install the Microsoft.Graph PowerShell module if not already installed

Install-Module -Name Microsoft.Graph -Force -AllowClobber

Import the module

Import-Module Microsoft.Graph

Connect to Microsoft Graph (You will be prompted to sign in)

Connect-MgGraph

Variables

$pageSize

100 # Adjust as needed $token

$null

Fetch groups with paging

do {

$groups

Get-MgGroup -Top $pageSize -PageToken $token -Select "id,displayName"

foreach ($group in $groups) {

$members

Get-MgGroupMember -GroupId $group.Id

if ($members.Count -eq 0) {

Write-Host "Group $($group.DisplayName) has zero members."

You can perform additional actions here if needed

    }
}

$token

$groups.PageToken } while ($token -ne $null)

Disconnect from Microsoft Graph

Disconnect-MgGraph

Describe the bug

A clear and concise description of what the bug is.

It doesn't give me the full list of groups where members are zero. Also, it doesn't have any option to filter cloud only groups.

To Reproduce Steps to reproduce the behavior: Run any of the above powershell. My tenant has 8k+ groups both on-prem synced & cloud.

Expected behavior

A clear and concise description of what you expected to happen.

Debug Output

Run the problematic command with -Debug and paste the resulting debug stream below. ⚠ ATTENTION: Be sure to remove any sensitive information that may be in the logs.

Module Version

Please run Get-Module Microsoft.Graph* after cmdlet execution and paste the output below. If a module cannot be installed or imported, please run Get-Module -ListAvailable and paste the output.

Environment Data

Please run $PSVersionTable and paste the output below. If running the Docker container image, indicate the tag of the image used and the version of Docker engine.

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Add any other context about the problem here.

I raised incident 2402090010003214 with support & they advised to create a github issue

hg185024 avatar Mar 05 '24 16:03 hg185024

Gidday,

I think I've managed to prepare what you're looking for:

#Get all groups. Filter on Id,DisplayName, and onPremisesSyncEnabled to return results faster
$AllGroups = Get-MgGroup -All -Property Id,DisplayName,onPremisesSyncEnabled

#Optional - filter to cloud groups
$Groups = $AllGroups | Where-Object {$null -eq $_.OnPremisesSyncEnabled}

#Optional - filter to on-prem groups
$Groups = $AllGroups | Where-Object {$null -ne $_.OnPremisesSyncEnabled}

ForEach ($Group in $Groups){
    $Temp = $null
    $Temp = Get-MgGroupMember -GroupId $Group.Id -Property Id #Only need the ID property to reflect an adequate count
    if (($Temp.id).count -eq 0){
        Write-Host "Group: $($Group.DisplayName) has no members"
    }
}

I have encountered a few problems myself which might be worth having the devs looking into:

  • The property onPremisesSyncEnabled doesn't seem to support ne null, when it says it does. This has necessitated requesting all groups, and then filtering on the pipeline: https://learn.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties So while the following works: Get-MgGroup -All -Property Id,DisplayName,onPremisesSyncEnabled -filter "onPremisesSyncEnabled eq true" This does not: Get-MgGroup -All -Property Id,DisplayName,onPremisesSyncEnabled -filter "onPremisesSyncEnabled eq null"
  • CountVar doesn't seem to work for Get-MgGroupMember, which necessitates doing returning all the group members, and then doing a manual count to work around this (some blog posts already talking about it: https://rakhesh.com/azure/graph-and-group-membership-count/) and also https://learn.microsoft.com/en-us/graph/api/group-list-memberof?view=graph-rest-1.0&tabs=http#example-2-get-only-a-count-of-all-memberships

SeniorConsulting avatar Mar 05 '24 21:03 SeniorConsulting

@BHoggs has suggested an improvement to reduce the number of lines, and filtering to cloud groups or on-premise groups on the API side by using the not() operator

Shorter script below:

#Optional - filter to cloud groups. Filter on Id,DisplayName, and onPremisesSyncEnabled to return results faster
$Groups = Get-MgGroup -All -Property Id,DisplayName,onPremisesSyncEnabled -filter "not(onPremisesSyncEnabled eq true)" -ConsistencyLevel eventual -CountVariable CountVar

#Optional - filter to on-prem groups. Filter on Id,DisplayName, and onPremisesSyncEnabled to return results faster
$Groups = Get-MgGroup -All -Property Id,DisplayName,onPremisesSyncEnabled -filter "onPremisesSyncEnabled eq true)"

ForEach ($Group in $Groups){
    $Temp = $null
    $Temp = Get-MgGroupMember -GroupId $Group.Id -Property Id #Only need the ID property to reflect an adequate count
    if (($Temp.id).count -eq 0){
        Write-Host "Group: $($Group.DisplayName) has no members"
    }
}

As always, there are many ways to acheive this, you might also be able go the other way around and get all groups and group members first, then filter later on:

$Groups = Invoke-MgGraphRequest -Method GET -URI "https://graph.microsoft.com/v1.0/groups?`$select=id,displayName,onPremisesSyncEnabled&`$expand=members(`$select=id)"

ForEach ($Group in $Groups.value){
    if (($null -ne $Group.OnPremisesSyncEnabled) -AND ($Group.Members.Id.count -eq 0)){
        Write-Host "Group: $($Group.DisplayName) has no members"
    }
}

The one above filters to on-premises groups only. If you want to use cloud groups only, you should change the -ne in if statement to -eq i.e. if (($null -eq $Group.OnPremisesSyncEnabled) -AND ($Group.Members.Id.count -eq 0)){

SeniorConsulting avatar Mar 05 '24 23:03 SeniorConsulting

The most efficient way available is this:

$groups = Get-MgGroup -ExpandProperty "members($select=id)" -Property "id,displayName" -PageSize 999 -All | Select-Object Id, DisplayName, Members | Where-Object { $_.Members.Count -eq 0 }

now, this list could still contain false positives (due to a limitation of Expand process, if the first 20 members are soft-deleted, it will incorrectly show 0 members).

To get the actual count of members, you need to execute after: $groups | ForEach-Object { [PSCustomObject]@{ Id = $_.Id; MembersCount = Get-MgGroupMemberCount -GroupId $_.Id -ConsistencyLevel "Eventual" } } | Where-Object MembersCount -eq 0

Licantrop0 avatar May 31 '24 20:05 Licantrop0