Do It Yourself Dynamic Microsoft 365 Groups with PowerShell

Entra ID dynamic groups (or dynamic Microsoft 365 groups) require Entra P1 licenses. But if you don’t want to pay for those licenses, you can accomplish much the same effect with PowerShell, if you don’t mind creating some scripts.

The basics of a dynamic group are a membership rule that determines the group membership and background processing to keep the group membership updated. In this discussion, we use a filter against Entra ID members to find group members who are managers and an Azure Automation runbook to maintain the membership.

Apart from anything else, creating and managing a pseudo-dynamic group is a good example of using Microsoft Graph PowerShell SDK cmdlets to work with group memberships, including some of the limitations that exist when finding user accounts to process.

Create a New Group

The first thing to do is to create a new Microsoft 365 group by running the New-MgGroup cmdlet. The important thing here is to set the value of the GroupTypes parameter to be “Unified,” indicating that the new group is a Microsoft 365 group.

$Group = New-MgGroup -Description "Dynamic Managers" -DisplayName "Employees with a Manager title" -MailEnabled:$True -SecurityEnabled:$False -MailNickname "Dynamic.Managers" -GroupTypes "Unified"

A true dynamic Entra ID group also has DynamicMembership in the GroupTypes property.

Finding Group Members

With the target group available, we can populate its membership. The simplest filter to find users to add to the group is against the JobTitle property:

[array]$Managers = Get-MgUser -Filter "jobtitle eq 'Manager' and usertype eq 'member'" -All -PageSize 500

The command works, but it’s unsatisfactory in a practical sense because it only finds users that have Manager as their job title (in other words, an exact match). Many variations of manager titles exist, so searching the JobTitle property for a partial match against Manager appears to hold out more hope:

[array]$Managers = Get-MgUser -Filter "usertype eq 'member'" -Search "jobtitle:Manager" -ConsistencyLevel Eventual -All -PageSize 500

Alas, the command fails to find users with job titles like “Senior Development Manager” because the Graph doesn’t support tokenized search against the JobTitle property and defaults to the same behavior as using a filter with the startsWith operator. Even combining the startsWith and endsWith operators will only find users with manager at the start or end of their job title:

Get-MgUser -Filter "(endsWith(jobtitle,'manager') or startsWith(jobtitle,'manager')) and usertype eq 'member'" -Consistency Eventual -All -PageSize 500

The root of the problem is that the Graph only supports the contains operator for certain resources, and Users is not one of those resources. The restriction is evident when creating a membership rule for a dynamic group through the Entra admin center (Figure 1).

Creating a membership rule based on the job title property in the Entra admin center.

Dynamic Microsoft 365 groups.
Figure 1: Creating a membership rule based on the job title property in the Entra admin center

After exhausting the possibilities of filtering and searching with the Get-MgUser cmdlet, two options remain: fetch all users and use a client-side filter to find managers or use a custom attribute to define who is a manager. Given that client-side filters are usually not a great idea unless small amounts of data need to be fetched from the Graph, I usually opt for the latter approach. Using custom attributes is fast and if the attribute is maintained correctly, no matter what a user’s job title is, if the custom attribute doesn’t contain the right value, it’s a reasonable guarantee that the user isn’t a manager. This command filters against custom attribute 15 to find user accounts marked as managers:

[array]$ManagerAccounts = Get-MgUser -Filter "onPremisesExtensionAttributes/extensionAttribute15 eq 'Manager' and userType eq 'Member'" -ConsistencyLevel eventual -All -CountVariable Managers

Adding Members to the Group

It’s possible to add individual members to a group, but when creating new groups or maintaining the membership of large groups, it’s better to do a bulk update. Entra ID supports adding up to 20 members to a group in a single transaction. The code below:

  • Find the current members of the target group (the group we created above).
  • Removes any managers who are already members from the Managers array.
  • Creates a list of managers to add and populates the list in the format required by the bulk update (essentially, identifying the manager as a user directory object with the account identifier).
  • Selects a batch composed of the first 20 user accounts from the list.
  • Calls the Update-MgGroup cmdlet to process the batch and add the 20 user accounts to the group.
  • Removes the 20 processed objects from the list. If any more objects are available, creates the next batch and loops back to Update-MgGroup to add those accounts to the group. This continues until all accounts in the list are processed.
$TargetGroup = Get-MgGroup -Filter "displayname eq 'Employees with a Manager title'" 
[array]$MembersTargetGroup = Get-MgGroupMember -GroupId $TargetGroup.Id -All | Select-Object -ExpandProperty Id
$MembersToAdd = $ManagerAccounts.Id | Where-Object { $MembersTargetGroup -notcontains $_ }
$Data = [System.Collections.Generic.List[Object]]::new()
$MembersToAdd | ForEach-Object {$Data.Add("https://graph.microsoft.com/V1.0/directoryobjects/{0}" -f $_)}
While ($Data.count -ne 0) {
    $Parameters = @{"members@odata.bind" = $Data[0..19] }
    Update-MgGroup -GroupId $TargetGroup.Id -BodyParameter $Parameters
    If ($Data.count -gt 20) {
        $Data.RemoveRange(0,20)
    } Else {
        $Data.RemoveRange(0,$Data.count)
    }
}

Our pseudo-dynamic Microsoft 365 group is now fully populated with members.

Maintaining Group Membership

Populating a group’s membership is only the start. For our pseudo-dynamic group to be effective, it’s important to maintain the membership so that users who become members join the group and those who stop being managers are removed.

The code is like that detailed above with the exception that another check is necessary to find users who are currently group members but who are no longer managers. Figure 2 shows the results of a test run of the script to update group membership in Azure Automation.

Azure Automation processes membership changes for the pseudo-dynamic Microsoft 365 group.
Figure 2: Azure Automation processes membership changes for the pseudo-dynamic Microsoft 365 group

When you’re happy with the code, you can assign the runbook to a schedule to have Azure Automation run it automatically. A daily run is probably sufficient, but you can tune the schedule to run more or less frequently as your needs dictate.

To allow the script to run, the automation account must be assigned the Group.Read.All and GroupMember.ReadWrite.All Graph application permissions and the Microsoft.Graph.Groups module must be imported as a module into the automation account (the Microsoft.Graph.Authentication module is a prerequisite).

Because the current release of the Microsoft Graph PowerShell SDK has some problems with the V7.1 and V7.2 runtime versions used by Azure Automation (a problem that Microsoft might have fixed before you read this text), I used the V5.1 PowerShell runtime for both modules and runbooks. You can download the code that I used from the Office 365 for IT Pros GitHub repository.

The DIY Principle Works, But in Practice It’s Best to Buy Licenses

What is demonstrated here is that it is perfectly feasible to replicate how the membership management for dynamic Microsoft 365 groups work through a mixture of PowerShell and Azure Automation. Hopefully, you can use some of the ideas explored here in real projects.

Although DIY dynamic groups are feasible, over the long term, it’s always better to use an inbuilt feature when possible because Microsoft then takes on the worry to maintain and enhance the feature. If you need dynamic Microsoft 365 work, I recommend that you acquire the necessary Entra P1 licenses. You’ll need a license for every account that comes within the scope of the membership rules used with dynamic groups. Conceivably, every account in the tenant might need a license. Entra P1 delivers much more value than just dynamic groups, so it’s probably worth the investment.

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Leave a Reply