Create and Team-Enable a New Group

The SharePoint Developer support team recently posted an interesting article about how to create a new Microsoft 365 group using the SharePoint Online REST API or the Graph (Groups) API. The examples use Azure AD B2C Collaboration  Resource Owner Password Credentials (ROPC) to secure credentials. ROPC is also the way that Apple plans to upgrade mail app configurations on iOS devices to force the use of modern authentication when connecting to Exchange Online.

Although the code as presented works, using the Microsoft Graph PowerShell SDK would be easier. Let me explain why and throw some light onto points that the original article doesn’t cover.

Get Your Labels

The article describes how to create a new Microsoft 365 group with a sensitivity label. The first thing to do is discover the GUID of the sensitivity label to use. Only sensitivity labels configured for container management can be used, so we find this set by either going to the Information protection section of the Microsoft Purview Compliance portal or running some PowerShell. Here’s how to connect to Exchange Online, then to the compliance endpoint, and finally return the set of container management labels.

Note: You can only create Microsoft 365 groups with sensitivity labels when you run the SDK interactively with delegate permissions. There’s no obvious reason why application permissions should not work, but the New-MgGroup cmdlet won’t run if you include the sensitivity label when running with application permissions, such as using Azure Automation with a managed identity.

Connect-ExchangeOnline
Connect-IPPSSession
Get-Label | ? {$_.ContentType -eq "Site, UnifiedGroup"} | ft DisplayName, ImmutableId

DisplayName         ImmutableId
-----------         -----------
Non-business use    a49e1277-93db-4a2f-8105-43c5196b4fef
General Access      e42fd42e-7240-4df0-9d8f-d14658bcf7ce
Guest Access        c29e68f9-bc4f-413b-a741-6db8e38ad1c6
Limited Access      d6cfd185-f31c-4508-ae40-229ff18a9919
Confidential Access c99e52c6-f5ff-4050-9313-ca6a3a35710f

Create Your Group

With the GUID of the chosen sensitivity label defined in a variable, we can go ahead and create the new group. Three differences exist in this code:

  • The privacy property for the new group is not defined. You don’t need this because the privacy (private or public) setting is controlled by the sensitivity label. In fact, if you define a privacy setting and then select an incompatible sensitivity label (you select Private and the label says Public), the request to create the group will fail.
  • The body for the Graph API request is defined in the $NewGroupParams hash table.
  • The New-MgGroup cmdlet is used to create the new group by passing the body containing the parameters for the new group.

I’ve updated the code to add a call to find the identifier for a specific person to use as the group owner.

Connect-MgGraph -Scopes Group.ReadWrite.All, GroupMember.ReadWrite.All
$GroupOwner = (Get-MgUser -UserId Terry.Hegarty@office365itpros.com).Id
$Owner = "https://graph.microsoft.com/v1.0/users/" + $GroupOwner
$SensitivityLabelId = "d6cfd185-f31c-4508-ae40-229ff18a9919"
$NewGroupParams = @{
    "displayName" = "Microsoft Graph PowerShell SDK"
    "mailNickname"= "Microsoft.Graph.PowerShell.SDK"
    "description" = "People who like to discuss how to use the Microsoft Graph PowerShell SDK"
    "owners@odata.bind" = @($Owner)
    "groupTypes" =  @(
                       "Unified"
                     )
    "mailEnabled" =  "true"
    "securityEnabled" = "true"
    "assignedLabels" = @(
                        @{"LabelId"=$SensitivityLabelId}
                        )
} 

$Group = New-MgGroup -BodyParameter $NewGroupParams

Everything works smoothly and the new group is available in Azure AD, SharePoint Online, and Exchange Online.

Using Invoke-MgGraphRequest

The other way to create the group is by using the URI and body parameter with a POST request run through the Invoke-MgGraphRequest cmdlet. Essentially, this uses the same method as in the original article, replacing Invoke-RestMethod with Invoke-MgGraphRequest.

$Uri= "https://graph.microsoft.com/v1.0/groups"
$Group = Invoke-MgGraphRequest -Method Post -Uri $Uri -Body $NewGroupParams

Here’s the odd thing. Creating a new Microsoft 365 Group using Invoke-MgGraphRequest creates the group in Azure AD and SharePoint Online immediately but delays the creation of the group in Exchange Online. For instance, I created a group at 15:34 and it didn’t show up in Exchange Online until 30 minutes later, at 16:14. Sometimes the delay was shorter, but there was always a delay.

Another difference is that new groups created with New-MgGroup obey Exchange address policies for groups if present. The new groups created with Invoke-MgGraphRequest use a MOERA address for their primary SMTP address and then respect the address policy after the group exists in Exchange Online.

People have complained in the past about delays when creating new groups. Here’s an example from the Microsoft Technical Community and another from Stackoverflow.com, but I thought those issues were no longer a problem. I guess I was wrong.

The only explanation I can come up with is:

  • New-MgGroup is like the New-UnifiedGroup cmdlet and creates the new group with a dual-write to Exchange Online and Azure AD, which synchronizes to SharePoint Online.
  • Using the Graph URI with Invoke-MgGraphRequest creates the new group in Azure AD, which synchronizes the new group to SharePoint Online. Later, a backfill synchronization operation creates the new group in Exchange Online.

Team-Enabling the New Group

Given the popularity of Teams, you might decide to team-enable the new group. This code sets up a body containing the template to use for the new team (standard) and the identifier of the Microsoft 365 group. We then run the New-MgTeam cmdlet process the request.

$NewTeamParams = @{
  "template@odata.bind"="https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
  "group@odata.bind"="https://graph.microsoft.com/v1.0/groups('$($Group.Id)')"
}
New-MgTeam -BodyParameter $NewTeamParams

The same could be done using Invoke-MgGraphRequest:

$NewTeamParams = @{
  "template@odata.bind"="https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
  "group@odata.bind"="https://graph.microsoft.com/v1.0/groups('a771ae9d-6a93-4ccb-bbd0-dcfa55138f2f')"
}
$NewTeamParams = $NewTeamParams | Convertto-Json
$Uri = "https://graph.microsoft.com/v1.0/teams"
Invoke-MgGraphRequest -Uri $Uri -Method Post -Body $NewTeamParams

Either way, we end up with a new team created with settings inherited from a new Microsoft 365 group created using cmdlets from the Microsoft Graph PowerShell SDK (Figure 1).

A new team created with the New-MgTeam cmdlet
Figure 1: A new team created with the New-MgTeam cmdlet

Multiple Ways to Get the Job Done

Like the animated figures in our new team-enabled group, we should be happy to discover just how easy it is to create new groups and teams using the Microsoft Graph PowerShell SDK. I’m not saying that this is the easiest way. It’s certainly less complicated to use the New-UnifiedGroup cmdlet from the Exchange Online module to create new Microsoft 365 groups and the New-Team cmdlet from the Microsoft Teams module to team-enable a group, but it’s nice to have choice, and the Microsoft Graph PowerShell SDK is a good way to proceed if you only want to deal with cmdlets from a single module (albeit there are 30+ sub-modules in the SDK).

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.

Comments

  1. Frank

    this new “better” graph powershell they are rolling out and replacing everything with is honestly the absolute worst, most convoluted BS I have ever seen. Absolutely terrible that for what used to be a simple one liner after oyu declare some variables, you now need to jump through all these hoops. What an absolute load

Leave a Reply