Basic Azure AD Group Management with the Microsoft Graph PowerShell SDK

Creating, Finding, and Populating Azure AD Groups

Last week, I discussed how to perform basic Azure AD user account management operations using cmdlets from the Microsoft Graph PowerShell SDK. Now it’s time to discuss the management of group objects.

To work with the cmdlets discussed here, you should connect to the Microsoft Graph with the Group.ReadWrite.All and GroupMember.ReadWrite.All permissions:

$RequiredScopes = @("Group.ReadWrite.All", "GroupMember.ReadWrite.All", "User.ReadWrite.All")
Connect-MgGraph -Scopes $RequiredScopes

See this post for more information about connecting to the Graph and permissions.

Creating New Azure AD Groups

Azure AD groups include:

  • Microsoft 365 groups/Teams (including groups used by Yammer).
  • Security groups.
  • Dynamic Azure AD groups (including those used by Microsoft 365 groups/Teams).
  • Distribution lists.
  • Mail-enabled security groups.

The New-MgGroup cmdlet can create Microsoft 365 groups, dynamic groups, and security groups. It can’t create distribution lists or mail-enabled security groups (these are essentially Exchange Online rather than Azure AD objects). Although New-MgGroup can create groups of different types, it is often better to use the dedicated cmdlet for a particular type of group to ensure that Microsoft 365 performs all the necessary provisioning, like New-UnifiedGroup or New-Team.

Here’s an example of using New-MgGroup to create a Microsoft 365 group (the key point is to set the GroupTypes parameter to be “Unified”):

New-MgGroup -DisplayName "Sales Operations Team" -GroupTypes Unified -MailNickName Sales.Operations -MailEnabled:$True -SecurityEnabled:$False -Description "A group for Sales Operation management"

It’s a good idea to capture the result of the cmdlet in a variable. If the command is successful, you’ll then have a variable containing properties of the new group including its identifier. As we’ll see, you’ll need the identifier to interact with the group using other SDK cmdlets.

The downside of creating a Microsoft 365 group using the New-MgGroup cmdlet is that you will probably end up fixing up some of the group’s properties afterwards. For instance, New-MgGroup adds the signed-in account as the group owner, which you might not want. In addition, you can’t update properties like group privacy or assign a sensitivity label, so these must be set afterwards.

Creating a Dynamic Microsoft 365 Group

One scenario where New-MgGroup scores is where you want to create a dynamic Microsoft 365 Group, as this cannot be done using the New-UnifiedGroup cmdlet. This command creates a group using a membership rule to find people whose usage location (for Microsoft 365 services) is the U.S:

$Group = New-MgGroup -DisplayName "U.S. Based Employees" -Description "Dynamic group containing U.S. Based Employees" -MailEnabled:$True -SecurityEnabled:$False -MailNickname US.Employees -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(User.usagelocation -eq ""US"")" -MembershipRuleProcessingState "On"

Update Group Properties

PowerShell modules like Exchange Online and Azure AD usually include Set- cmdlets to update the properties of objects. The SDK uses Update- cmdlets, so to update a group, you run the Update-MgGroup cmdlet. For example, this command updates a group’s description:

Update-MgGroup -GroupId dc9e6f8b-6734-4180-af25-aa40fae79280 -Description "People lucky enough to have Office 365 E5 licenses"

Currently, the Microsoft Graph Groups API treats a group’s proxyAddresses property as read-only, which means that you can’t add or remove a proxy address using the Update-MgGroup cmdlet. Use an Exchange Online cmdlet like Set-UnifiedGroup instead.

Updating Group Membership

The New-MgGroupMember cmdlet populates the group membership. In this example, we get the group identifier, use Get-MgUser to find a set of suitable group members, and finally add them to the group:

$GroupId = (Get-MgGroup -Filter "displayName eq 'Sales Operations Team'").Id
[array]$Users = Get-MgUser -Filter "department eq 'Sales'"
ForEach ($User in $Users) {
  New-MgGroupMember -GroupId $GroupId -DirectoryObjectId $User.Id }

Unfortunately, checking that the right members are present afterwards is not as simple as it should be (and is with either the Get-AzureADGroupMember or Get-UnifiedGroupLinks cmdlets). Sources in Microsoft indicate that change might come soon in this area to improve matters. For now, instead of Get-MgGroupMember returning a list of group members, you must pipe the output to the Get-MgUser cmdlet:

Get-MgGroupMember -GroupId $GroupId -All | ForEach {Get-MgUser -UserId $_.Id}

Adding a group owner is a little complicated because the owner is stored by reference to its object rather than as a simple property. The New-MgGroupOwnerByRef cmdlet requires the identifier for the owner’s account to be passed in a hash table:

New-MgGroupOwnerByRef -GroupId $GroupId -AdditionalProperties @{"@odata.id"="https://graph.microsoft.com/v1.0/users/2a3b60f2-b36b-4758-8533-77180031f3d4"}

The current version of the SDK (1.9.3) doesn’t include cmdlets to remove group members or owners. For now, do this with the Remove-UnifiedGroupLinks cmdlet (Microsoft 365 group) or Remove-AzureADGroupOwner/Member (security groups).

Removing Groups

The Remove-MgGroup cmdlet removes a Microsoft 365 group or security group. For example:

Remove-MgGroup -GroupId f6dd8a3e-d50c-4af2-a9cf-f4adf71ec82b

The cmdlet can’t remove distribution lists or mail-enabled security groups.

Restore Deleted Groups

Deleted groups remain in a soft-deleted state for 30 days following their deletion. During this time, it’s possible to restore the group using the Restore-MgGroup cmdlet. To find the set of soft-deleted groups awaiting permanent removal, take the code to find soft-deleted users in this article and amend the Get-MgDirectoryDeletedItem cmdlet to look for microsoft.graph.group objects instead of microsoft.graph.user.

The report lists the set of soft-deleted groups, including their identifiers. To restore a group, run Restore-MgGroup and pass the identifier of the group to restore:

Restore-MgGroup -GroupId 314bab9d-af9b-43b8-b627-af38075d40b0

Finding Group Objects

The Get-MgGroup cmdlet fetches details of Azure AD groups. To retrieve a single group, use its display name as a filter:

Get-MgGroup -Filter "DisplayName eq 'Leadership Team'"

If you add the All parameter, you’ll get all the groups in the tenant.

[array]$Groups = Get-MgGroup -All

The command returns groups of all types. To filter out the various types of groups, we can check different properties to identify each type of group. Table 1 lists useful properties to check.

PropertyUsed by
MailEnabled = TrueDistribution lists
Microsoft 365 groups
Mail-enabled security groups
SecurityEnabled = TrueSecurity groups
Mail-enabled security groups
GroupTypes = UnifiedMicrosoft 365 groups
GroupTypes = DynamicMembershipDynamic Azure AD groups
GroupTypes = Unified, DynamicMembershipDynamic Microsoft 365 groups
ResourceProvisioningOptions = TeamTeam-enabled Microsoft 365 groups
Table 1: Filters for different types of Azure AD group

The simplest filters are those which find groups based on a property. For example, to find all security-enabled groups:

Get-MgGroup -Filter “securityEnabled eq true” -All

Find all mail-enabled groups:

Get-MgGroup -Filter “mailEnabled eq true” -All

The GroupTypes and ResourceProvisioningOptions properties require complex filters with Lambda operators. For example, to find the set of Microsoft 365 groups in the tenant:

[array]$M365Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All 

To find the set of dynamic Microsoft 365 Groups:

Get-MgGroup -Filter "groupTypes/any(c:c eq 'dynamicmembership') and groupTypes/any(x:x eq 'unified')" -All

To find the set of Microsoft 365 groups enabled for Teams:

[array]$Teams = Get-MgGroup -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" -All

In addition, client-side filter can refine the results returned by the server. For instance, after fetching all security-enabled groups, we use a client-side filter to find the set with dynamic membership:

[array]$SecurityGroups = Get-MgGroup -Filter “securityEnabled eq true” -All 
[array]$DynamicSecurityGroups = $SecurityGroups | ? {$_.GroupTypes -eq “DynamicMembership”}

Filter Speed

The filters used by Get-MgGroup are server-side, meaning that the data is filtered when the server returns it to PowerShell. Because they’re Graph-based and return fewer properties than cmdlets like Get-UnifiedGroup, these commands are very fast, which makes them worth considering if you have scripts which fetch subsets of groups for processing.

As an example, I replaced calls to the Get-UnifiedGroup and Get-AzureADMSGroup cmdlets in a script to report Microsoft 365 groups under the control of the Groups expiration policy with Get-MgGroup and processing time fell from 30 seconds to 1.9 seconds.

Complex Azure AD Queries Against Groups

Get-MgGroup supports complex queries against Azure AD. Essentially, a complex query is one that the Microsoft Graph resolves against a separate property store. It’s not always obvious when a complex query is necessary. Microsoft could hide this need in code instead of forcing PowerShell coders to remember when they must add the ConsistencyLevel parameter to mark a query as complex. Searching the display name of groups for a term is an example of a complex query.

[array]$Groups = Get-MgGroup -Search '"displayname:Office 365"' -ConsistencyLevel Eventual

Another example is to use the Filter parameter to find groups which start with a value. For instance, we might want to find groups whose display name starts with Office:

[array]$Groups = Get-MgGroup -Filter "startsWith(DisplayName, 'Office')" -ConsistencyLevel Eventual

An Evolving Story

Microsoft’s documentation for migration of Azure AD cmdlets admits “There is currently no tool to automatically converts scripts in Azure AD PowerShell to Microsoft Graph PowerShell.” I don’t anticipate that such a tool will appear. As described here, its Graph foundation mean that the ways of performing actions against Azure AD groups with the Microsoft Graph PowerShell SDK differ from how things work with the Azure AD module. It will take time and effort to master the details. Hopefully, the advice contained here helps that evolution.


Keep up to date with developments like the migration from the cmdlets in the Azure AD module to the Microsoft Graph SDK for PowerShell by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers understand the most important changes happening across Office 365.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.