Microsoft 365 Groups – Office 365 for IT Pros https://office365itpros.com Mastering Office 365 and Microsoft 365 Mon, 26 Aug 2024 13:47:00 +0000 en-US hourly 1 https://i0.wp.com/office365itpros.com/wp-content/uploads/2024/06/cropped-Office-365-for-IT-Pros-2025-Edition-500-px.jpg?fit=32%2C32&ssl=1 Microsoft 365 Groups – Office 365 for IT Pros https://office365itpros.com 32 32 150103932 Why Entra ID can Restore Some Types of Deleted Groups and Not Others https://office365itpros.com/2024/08/28/restore-deleted-groups-issues/?utm_source=rss&utm_medium=rss&utm_campaign=restore-deleted-groups-issues https://office365itpros.com/2024/08/28/restore-deleted-groups-issues/#comments Wed, 28 Aug 2024 07:00:00 +0000 https://office365itpros.com/?p=66170

Ability to Restore Deleted Groups Depends on Graph APIs

Yesterday, I covered a gap that exists between the Purview development group and the Exchange Online development group when it comes to applying scoped roles to audit log searches. Today, a blog post by ex-MVP Tony Murray-Smith reminds me about another functionality gap that exists in the area of groups. The problem described occurred when a user deleted a security group by mistake only to discover that the Entra admin center doesn’t support a method to restore deleted groups of this type.

In fact, Microsoft 365 groups are the only type of group that Entra supports for restoration via its admin center. There’s no way to restore a deleted distribution list, dynamic distribution list, security group, or mail-enabled security group. Apart from dynamic distribution lists, these objects are recognized by Entra ID and accessible through the Groups API. However, the only group objects supported by the List Deleted Items and Restore Deleted Items (directory objects) APIs remain Microsoft 365 groups. And if a Graph API isn’t available to support restoration, the administrative portals cannot create functionality from thin air.

This situation has persisted since the introduction of cmdlets to restore deleted Microsoft 365 groups in 2017 followed by a GUI option in the Exchange admin center, Microsoft 365 admin center, and Entra admin center. Microsoft subsequently removed the option to restore deleted groups from the new EAC, so the current GUI-based options to restore deleted Microsoft 365 groups are in the Entra admin center and Microsoft 365 admin center. And if you want to use PowerShell, there’s the Restore-MgDirectoryDeletedItem cmdlet.

Option to restore deleted groups in the Microsoft 365 admin center
Figure 1: Option to restore deleted groups in the Microsoft 365 admin center

The Gap Between the Exchange DS and Entra ID

The question is why Entra ID only supports the restoration of Microsoft 365 groups. I think the answer lies in two parts. First, the desire within Microsoft to make its brand-new cloud-only Office 365 groups (now Microsoft 365 groups) the “best group for everything” following their launch at the Ignite conference in May 2015.

The infrastructure to fully support Microsoft 365 groups took time to develop, and building the capability to reconnect all the different resources that a group might use made the process more complicated for Microsoft 365 groups. Being able to restore SharePoint Online, Teams, the group mailbox, and so on was a big undertaking that Microsoft quickly discovered needed to be tackled after the launch of Office 365 groups, especially after some early customers discovered that they couldn’t be restored. The functionality duly arrived in 2017. The campaign to make Microsoft 365 groups do everything is far less intense now than it was some years ago, but its legacy is evident sometimes.

The EXODS Objects

The second issue is heritage. Distribution lists and mail-enabled security groups originated in Exchange Server. Exchange Online still has its own directory (EXODS) to store details for mail-enabled objects. Synchronization and dual-write update operations keep Entra ID and EXODS aligned so that updates performed in one directory synchronize immediately to the other. The Graph APIs support distribution lists and security groups, including mail-enabled security groups, but Entra ID and the Graph APIs ignore dynamic distribution lists and can’t update settings for distribution lists and mail-enabled security groups because these objects are homed within Exchange Online.

Good reasons exist for why the differentiation exists. Dynamic distribution lists require Exchange Online to resolve their membership because the membership supports objects like mail-enabled public folders that don’t exist in Entra ID. Dynamic distribution lists also support nested lists. Regular distribution lists and their mail-enabled security group variants have many settings that aren’t supported in Entra ID, like message approval.

As far as I can remember, it has never been possible to restore deleted distribution lists (and some of the online answers are very misleading, like this example). Once an administrator removes a distribution list, it’s gone. The only thing that can be done is to recreate the distribution list from scratch. That might be possible if someone knows the membership and the list settings, but that might not be the case.

Some Work Necessary in This Area

Microsoft should do some work to make it possible to restore all forms of deleted groups. That work will need contributions from teams responsible for Entra ID, the Graph API, and Exchange Online. Mistakes do happen and administrators remove important distribution lists or mail-enabled security groups when they shouldn’t. Being told that it’s necessary to recreate an object from scratch is a royal pain, and it’s something that shouldn’t still be a problem in 2024. Customers assume that if they can restore one type of deleted group, they should be able to restore any type of deleted group.

Then again, other pains exist around distribution list management, like the Microsoft’s failure to produce a utility to move distribution lists from on-premises servers to the cloud. Tim McMichael’s DLConversionV2 solution is the best available. He’ll be discussing distribution list management at TEC 2024 in Dallas in October. Maybe I should ask Tim about restoring groups that aren’t Microsoft 365 groups.


Learn about using Exchange Online and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2024/08/28/restore-deleted-groups-issues/feed/ 2 66170
The End for Office 365 Connectors Comes Into Sight https://office365itpros.com/2024/06/11/office-365-connectors-end/?utm_source=rss&utm_medium=rss&utm_campaign=office-365-connectors-end https://office365itpros.com/2024/06/11/office-365-connectors-end/#comments Tue, 11 Jun 2024 07:00:00 +0000 https://office365itpros.com/?p=65108

Support for Office 365 Connectors Ceasing for Microsoft 365 Groups and SharePoint Online

Message center notification MC798683 (4 June 2024) announces the retirement of Microsoft 365 Groups connectors, a form of what are called Office 365 connectors. The retirement process commences on August 5, 2024, and finishes on September 5, 2024. After that time, connectors will no longer be supported within Outlook (Win32), OWA, and the new Outlook for Windows (aka Monarch).

Connectors take notifications from online data sources and post messages into a target destination. In this case, the target is the Inbox in the mailbox of the Microsoft 365 group configured with the connector. These connectors are used with Outlook groups rather than Teams. You can’t configure a connector for the other folders in a group mailbox, and you can’t configure a connector for any other type of mailbox.

Messages delivered through an Office 365 connector are limited to 28 KB and aren’t intended to be complete articles. Instead, they let users know that something has happened, give them a short snippet about the event, and provide a link to follow for more complete information. Using a connector to post messages from an RSS feed is one of the most common uses, but third-party companies like Asana and Trello have created connectors to bring snippets about information from their services to Outlook and other Microsoft 365 targets.

Microsoft recommends that organizations replace group connectors with the Power Automate app, which has its own set of connectors for different data sources, including the ability to create a cloud flow to post messages to the group mailbox. Some of the Power Automate Connectors (like Salesforce and Jira) require a Power Automate premium license.

Connectors and SharePoint Online

A further blow for Office 365 Connectors comes in message center notification MC793656 (16 May 2024), which announces the retirement of connectors from SharePoint Online webparts. Microsoft says that this is due to “limited usage.” Based on anecdotal evidence and personal experience, I can’t recall ever seeing an Office 365 connector configured with a SharePoint Online webpart.

In any case, from June 15, 2024, site owners are unable to add connectors to SharePoint Online. On August 1, 2024, they’ll be unable to update or manage existing connectors and the connectors will stop receiving inbound notifications.

Teams, Office 365 Connectors, and Workflows

Teams still supports Office 365 connectors, which are configured on a per-channel basis because the target for new notifications are channel conversations. Each notification creates a new conversation.

MC798683 points out that Teams channels also support workflows created using the workflows app (“powered by” Power Automate), and workflows recently turned up in the […] menu for Teams chats (MC683929, last updated 24 May 2024).

I shall have to pay more attention to workflows in the future. I know that the basic stuff works very well (like bringing an RSS feed into a channel). I’m more interested in finding out how to replace the incoming webhook connector, which is used in many ways to bring information from applications into Teams.

So far, my experiments with the Post to a channel when a webhook request is received workflow have not been successful. This seems to work in the same way (publish a URL to post messages to) and it’s easy to find the URL, but more difficult to get the workflow to run. I eventually managed and published my experience about posting an adaptive card to Teams.

Moving to a Single Answer for No-Code Automation

All of this seems to be part of a cunning plan to turn Microsoft 365 users into citizen developers by popularizing the use of Power Automate and the Microsoft Power Platform (Figure 1) for no-code automation wherever possible. According to Microsoft (January 2024), Power Automate has 33 million monthly active users in 350,000 organizations. My assumption is that PowerShell and the Graph are the answer for code-based automation.

Microsoft Power Platform. 

Office 365 Connectors
Figure 1: Microsoft Power Platform

It’s hard to argue against rationalization and it does make sense to settle on a single no-code automation platform for Microsoft 365, something that wasn’t viable when Office 365 Connectors appeared around 2016. As always, don’t be surprised when change happens inside Microsoft 365. Just be prepared to cope with the change.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2024/06/11/office-365-connectors-end/feed/ 7 65108
Graph and PowerShell Hiccups for the Groups and Teams Report Script https://office365itpros.com/2024/03/22/groups-and-teams-activity-report/?utm_source=rss&utm_medium=rss&utm_campaign=groups-and-teams-activity-report https://office365itpros.com/2024/03/22/groups-and-teams-activity-report/#respond Fri, 22 Mar 2024 08:00:00 +0000 https://office365itpros.com/?p=64226

Fixing Bugs and Applying Workarounds for the Groups and Teams Activity Report

Microsoft 365 Groups and Teams Activity Report.

The Microsoft 365 Groups and Teams activity report is generated by a PowerShell script that I’ve been working on years. The first version is listed as created in July 2016, which is soon after Office 365 Groups made their debut.

Some recent changes caused me to refresh the Groups and Teams Activity Report script (the current version is 5.13 and it is available from GitHub). The major issues are listed below.

Continuing Woes with SharePoint Usage Data

The Graph Usage Reports API continues to have a problem with SharePoint Online usage data. The URL for a SharePoint site is not included in the data, which makes it very difficult to match the usage statistics with a site. As a workaround, the script fetches details of all sites in the tenant using the List Sites API to build a list of site URLs, which is then matched up with the SharePoint usage data. Matching is imperfect but works 99% of the time, which is close enough for a workaround that I hope will become unnecessary soon when Microsoft fixes the Usage Reports API.

Disappearing Group Owner Names

Some people noted that groups were being reported with no owners when they absolutely had some owners. The script calls the Groups API to retrieve owner information using GET requests like this:

https://graph.microsoft.com/v1.0/groups/33b07753-efc6-47f5-90b5-13bef01e25a6/owners?

Weirdly, the information retrieved only included the identifier for group owner accounts. The display name needed by the report was blank. I know that Microsoft encourages developers to include a Select statement in Graph queries to limit the number of properties retrieved for objects. This increases performance and reduces the amount of data that must be transferred from the service to an app. I therefore changed the request to:

https://graph.microsoft.com/v1.0/groups/33b07753-efc6-47f5-90b5-13bef01e25a6/owners?$select=id,displayName,mail

Everything worked, which is good, but when I retested with the original call a few days later, all the expected properties were there.

@odata.type       : #microsoft.graph.user
id                : eff3cd58-1bb8-4899-94de-795f656b4a18
businessPhones    : {+353 1 2080705}
displayName       : Tony Redmond
givenName         : Tony
jobTitle          : Chief Executive Officer
mail              : Tony.Redmond@office365itpros.com
mobilePhone       : +353 86 01629851
officeLocation    : Derrigimlagh
preferredLanguage : en-IE
surname           : Redmond
userPrincipalName : Tony.Redmond@office365itpros.com

My conclusion is that a bug (or perhaps an attempt to introduce a performance enhancement) suppressed the output of the properties for some period in the recent past. If this is the case and Microsoft reverted to previous behavior, it would explain what happened. But it could be something else, and the learning from the experience is that it is better to be explicit when requesting data from the Graph. Use Select to tell the Graph the set of properties needed by an app and all should be well.

Converting Strings to Dates

The data generated by the usage reports API includes the date when the monitored object was last active. This information is important in terms of knowing if a group or team is active. The date is in string format and must be converted to a datetime object to calculate the number of days since the last activity. Normally, casting the date as a datetime object is enough, but then you run into the problem that date format differs across cultures and the script throws the “string was not recognized as a valid datetime” error.

The script hadn’t had the problem before, but then I had a report that the code to convert the last activity date for a team failed. The date seemed OK (21-Sept-2023) and the code worked perfectly on my workstation, but failed elsewhere when the date format defined for PowerShell in the user’s chosen culture didn’t recognize 21-Sep-2023 as a valid date. The solution is to define the expected input string format for the cast. Here’s the current code:

[datetime]$LastItemAddedToTeams = [datetime]::ParseExact($ThisTeamData.LastActivity, "dd-MMM-yyyy", $null)

Hopefully, this fix will resolve the issue no matter what local culture is chosen for PowerShell. The learning here is that Microsoft 365 and Graph APIs output dates in different formats so some care is needed to handle dates properly, especially if you expect code to run in different countries.

Learnings for the Groups and Teams Activity Report

If I was a professional PowerShell developer, I probably would have taken more care with date objects. However, no one can be blamed when their scripts misbehave due to problems introduced by Microsoft. It’s a warning to keep an eye out for changes – or to build better error handling into scripts.

Speaking of which, I might convert the Groups and Teams Activity Report script to use the Microsoft Graph PowerShell SDK. This would simplify matters because the code wouldn’t have to deal with pagination and renewing access tokens (because the script is used to process reports for tens of thousands of groups, it can take hours to run, and the access token must be renewed hourly). Simpler code is easier to maintain… or so the theory goes.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2024/03/22/groups-and-teams-activity-report/feed/ 0 64226
Blocking the Welcome Message for Microsoft 365 Groups https://office365itpros.com/2023/12/21/block-welcome-message/?utm_source=rss&utm_medium=rss&utm_campaign=block-welcome-message https://office365itpros.com/2023/12/21/block-welcome-message/#comments Thu, 21 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62922

Many Ways to Manage Microsoft 365 Groups with PowerShell

A recent question about how to block welcome messages sent when new members join Microsoft 365 groups (Figure 1) sparked a discussion about the best way to create a new group. Of course, there is no best way. The right way depends on the circumstances around the creation of a new group and what you want to achieve.

A welcome message for a new Microsoft 365 group member.

Block welcome message
Figure 1: A welcome message for a new Microsoft 365 group member

If all you want to do is to create a new Microsoft 365 group with PowerShell, the easiest method is to run the New-UnifiedGroup cmdlet from the Exchange Online management module. New-UnifiedGroup is the original cmdlet Microsoft developed when they created Office 365 Groups in 2014. It is a compound cmdlet, meaning that it draws information from several Microsoft 365 workloads like Entra ID, Exchange Online, and SharePoint Online. It is a highly effective cmdlet, but New-UnifiedGroup cannot block welcome messages when creating a group. As we’ll see later, the Set-UnifiedGroup cmdlet can block welcome messages for an existing group.

If the new group is to support a team, we can create a group with the New-Team cmdlet from the Microsoft Teams module. The difference between the two cmdlets are the settings applied to the new group. New-UnifiedGroup creates a general-purpose Microsoft 365 group that can be team-enabled if necessary. New-Team creates a new group and then populates the settings to make the group a fully functional team, but it cannot block welcome messages either.

Because Teams has its own communications, New-Team disables some resource behavior options designed for use with email like AutoSubscribeNewMembers and AlwaysSubscribeMembersToCalendarEvents. Although Microsoft designed these settings for use with email-based Outlook groups, they can affect how Teams works. In this case, the settings affect who receives email notifications for channel meetings.

Groups and the Graph

Microsoft 365 administrative interfaces like the Microsoft 365 admin center do not use PowerShell cmdlets to create groups. Instead, they make Graph requests using the Groups API. You can do the same to create a new group by calling the Create Group API and passing a body for the request holding the settings for the new group.

The New-MgGroup cmdlet from the Microsoft Graph PowerShell SDK is based on the Create Group API with many of the settings passed in the request body extracted as separate parameters. This code is an example of running the New-MgGroup cmdlet to create a new Microsoft 365 group (the groupTypes property is set to ‘Unified’):

$Group = New-MgGroup -Description "Electricity Project" -DisplayName "Project members of the Electricity Steering team" -MailEnabled:$True -SecurityEnabled:$False -MailNickname "Electricity.Project" -GroupTypes "Unified" 

The New-MgGroup cmdlet is limited to creating Microsoft 365 groups and security groups. It cannot be used to create mail-enabled security groups or distribution lists, even if these objects appear in Entra ID and can be retrieved by the Get-MgGroup cmdlet. These objects essentially belong to Exchange Online. Synchronization replicates the objects to Entra ID and allows management of groups through the Entra ID interfaces. If you need to manage an email-related property, it must be done through Exchange.

Block Welcome Messages for Groups

The New-MgGroup cmdlet can perform tasks that other cmdlets cannot, like marking a group to support role assignments. This brings me back to the original question of disabling the welcome message sent to new group members. There’s no obvious way to do this with the New-UnifiedGroup or New-Team cmdlets, but you can block welcome messages with New-MgGroup.

The trick to disable welcome messages is to set the group provisioning options to include WelcomeEmailDisabled. This setting instructs the Groups service not to send welcome messages as new members join the group. To do this, update the resourceBehaviorOptions property with WelcomeEmailDisabled and pass it to the New-MgGroup cmdlet. Here’s an example of creating a Microsoft 365 group with the welcome message disabled. As new members join the group, the Groups service will note that welcome messages are disabled and stay mute.

$OwnerId = ("https://graph.microsoft.com/v1.0/users/{0}" -f (Get-MgUser -UserId Lotte.Vetler@office365itpros.com).Id ) 

$NewGroupSettings = @{ 
    "displayName" = "Paris Employees" 
    "mailNickname"= "Paris.Employees" 
    "description" = "Employees working in the Paris region" 
    "owners@odata.bind" = @($OwnerId) 
    "groupTypes" = @("Unified") 
    "mailEnabled" = "true" 
    "securityEnabled" = "false" 
    "ResourceBehaviorOptions" = @("WelcomeEmailDisabled") 
}  

$Group = New-MgGroup -BodyParameter $NewGroupSettings 

The structure used to pass settings appears complex until you realize that it is composed of a set of arrays and hash tables. The overall structure is a hash table with keys (the settings) and values. Where a setting holds multiple values, it uses an array (like the resource behavior options).

The WelcomeMessageEnabled Property

The discussion so far covers using a resource behavior option to disable welcome messages for groups at the time of creation. The important thing to remember is that these options are immutable. In other words, they can’t be changed. At least, I haven’t found a method to remove the WelcomeEmailDisabled resource behavior option.

Microsoft 365 Groups support another property called WelcomeMessageEnabled, which controls sending welcome messages to new members of Outlook groups. To disable welcome messages for Outlook groups, update this property to $False.

Set-UnifiedGroup -UnifiedGroupWelcomeMessageEnabled:$false -Identity 'Paris Employees' 

After a couple of minutes, the updated setting becomes effective. The advantage of this approach is that you can revert the setting to True to allow the flow of welcome messages to resume. Everything works as long as the WelcomeEmailDisabled option is not set for the group. If it is, the resource behavior option takes precedence and Groups ignores the WelcomeMessageEnabled setting.

Learnings

What have we learned from this discussion? The biggest lesson is not to assume that just because one cmdlet can’t do a job, other cmdlets can’t help. The proliferation of PowerShell modules and cmdlets available to Microsoft 365 tenants can be confusing, but I guess choice is better than limitation.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/12/21/block-welcome-message/feed/ 3 62922
How to Control the Creation of Microsoft 365 Groups with the Microsoft Graph PowerShell SDK https://office365itpros.com/2023/10/18/control-group-creation-sdk/?utm_source=rss&utm_medium=rss&utm_campaign=control-group-creation-sdk https://office365itpros.com/2023/10/18/control-group-creation-sdk/#comments Wed, 18 Oct 2023 01:00:00 +0000 https://office365itpros.com/?p=61987

Control Group Creation to Avoid Group Sprawl

Microsoft’s documentation covering the topic of “Manage who can create Microsoft 365 Groups” begins with: “By default, all users can create Microsoft 365 groups. This is the recommended approach because it allows users to start collaborating without requiring assistance from IT.”

I can’t say how strongly I disagree with this perspective. All it does is result in group sprawl, or more likely, teams sprawl. We learned the lesson with Exchange Server public folders in 1996 when users created new folders with abandon. Organizations are still clearing up the mess today, which is one of the reasons for the persistence of public folders in Exchange Online. The same need will arise to clean up unused and unwanted teams if organizations follow Microsoft’s advice to allow group creation by any and all. Microsoft promised to develop functionality to help with group sprawl in 2021. So far, there’s little sign of progress in this space, unless you include the ownerless group policy (2022) and the group expiration policy (available since 2020).

Group Creation Using the Microsoft Graph PowerShell SDK

The Microsoft documentation explains how to restrict group creation by running PowerShell to configure the Entra ID groups policy. Unhappily, the current version of the documentation uses cmdlets from the Azure AD Preview module, which is due for deprecation in March 2024, The same work can be done using cmdlets from the Microsoft Graph PowerShell SDK, which is what I cover here.

The basic approach is:

  • Create a security group to control group creation. The members of this group will be allowed to create new Microsoft 365 groups via user applications like Outlook and Teams. Accounts holding roles like Global administrator, Teams service administrator, Groups administrator, SharePoint administrator, User administrator, and Exchange administrator can always use administrative interfaces like PowerShell or the Microsoft 365 admin center to create new groups. The members of this group need Entra ID Premium P1 licenses.
  • Update the Entra ID groups policy to block group creation by anyone except the members of the security group.

I have no idea why Microsoft doesn’t make control over Microsoft 365 group creation available through an option in the Microsoft 365 admin center. My cynical side says that this is because they don’t want tenants to control group creation, so they force administrators to use PowerShell.

Create a Security Group to Control Group Creation

A simple security group is sufficient to define the set of accounts allowed to create new Microsoft 365 groups (Figure 1). You can either create a new group or use an existing group. Creating a new group is probably best because you can give the group an appropriate name and description and be sure that the group will only be used to control group creation.

A security group created to control group creation
Figure 1: A security group created to control group creation

Create a Groups Policy Object

Microsoft 365 uses a directory setting object to hold the settings to control creation and other aspects of Microsoft 365 groups. By default, tenants use default settings. To change these settings, you must create a copy of the template directory settings object and modify it. Here’s how to create a new directory settings object by retrieving the identifier of the default object and creating a new object for the tenant:

Connect-MgGraph -Scopes Directory.ReadWrite.All
$PolicyId = (Get-MgBetaDirectorySettingTemplate | Where-Object {$_.DisplayName -eq "Group.Unified"}).Id 
New-MgBetaDirectorySetting -TemplateId $PolicyId

The New-MgBetaDirectorySetting cmdlet fails if a tenant-specific directory settings object already exists.

Updating the Groups Policy to Limit Creation

With a groups policy object in place, we can update the settings. You can see the default settings by running:

Get-MgBetaDirectorySetting | Where-Object {$_.DisplayName -eq "Group.Unified"} | ForEach Values

To control group creation, two settings are updated:

  • EnableGroupCreation: This setting controls if users can create new groups. The default is true. We update it to false.
  • GroupCreationAllowedGroupId: This setting holds the identifier for the group whose members are allowed to create new groups.

The setting names are case-sensitive and should be passed exactly as shown.

To update the settings, fetch the identifier for the group (or have it available). Then populate an array with the current settings before updating the two settings described above. Finally, update the directory settings object with the new policy settings. Here’s the code:

$GroupId = (Get-MgGroup -Filter "displayName eq 'GroupCreationEnabled'").Id
$TenantSettings = Get-MgBetaDirectorySetting | Where-Object {$_.DisplayName -eq "Group.Unified"}
[array]$Values = $TenantSettings.Values
($Values | Where-Object Name -eq 'EnableGroupCreation').Value = "false"
($Values | Where-Object Name -eq 'GroupCreationAllowedGroupId').Value = $GroupId
Update-MgBetaDirectorySetting -DirectorySettingId $TenantSettings.Id -Values $Values

Figure 2 shows these commands being run.

Running the PowerShell code to control group creation
Figure 2: Running the PowerShell code to control group creation

Updating the group policy settings (for instance, to switch the group defining who can create new groups) uses the same approach: find values, update values, update the directory setting object.

If you make a mess of the Groups policy, you can start over by removing the directory settings object and creating a new policy. Here’s how to remove the policy:

$PolicyId = (Get-MgBetaDirectorySetting | Where-Object {$_.DisplayName -eq "Group.Unified"}).Id
Remove-MgBetaDirectorySetting -DirectorySettingId $PolicyId

Keeping Groups Under Control

Even if you decide to limit group creation, it’s a good idea to keep a close eye on what groups and teams are in active use and trim (or archive) those that don’t meet usage thresholds. The Teams and Groups activity report script can help with this process. Another point to consider is that Teams doesn’t come with any form of directory to allow users check if a team already exists for a topic. It’s possible to create such a directory, but making people check the list is a different challenge.

Another example of using directory objects to control groups is to block guest access for individual groups and teams. You can do this with sensitivity labels or by updating the directory setting for individual Microsoft 365 groups with PowerShell.


]]>
https://office365itpros.com/2023/10/18/control-group-creation-sdk/feed/ 3 61987
How to Create Dynamic Microsoft 365 Groups (and Teams) for Departments https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-microsoft-365-groups https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/#respond Tue, 10 Oct 2023 01:00:00 +0000 https://office365itpros.com/?p=61844

Create Dynamic Microsoft 365 Groups and Teams with PowerShell

No sooner had I published the article about creating dynamic administrative units with PowerShell, the first email arrived asking if the same was possible for dynamic Microsoft 365 groups. The answer is “of course,” but with the caveat that it’s not just a matter of some minor updates to the script.

That being said, the outline for the script to create dynamic groups is broadly the same:

  • Find the licensed users in the tenant and extract a list of departments. The departments should be accurate and some care should be taken to eliminate inconsistencies. For instance, some people might be listed as belonging to IT while others belong to the Information Technology department. Decide on one value and apply it to all.
  • You might not want to create groups for all departments. The script defines an array of excluded departments that are removed from the set to process.
  • Find the set of dynamic Microsoft 365 groups. We need this information to check if a dynamic group already exists for a department.
  • For each department, check if a group already exists. If not, define some parameters for the new group, including the membership rule that Entra ID uses to calculate the group members, and run the New-MgGroup cmdlet to create the group.
  • Following a successful creation, proceed to team-enable the new group by running the New-MgTeam cmdlet. This is an optional step, but seeing that Teams is the heaviest workload for Microsoft 365 groups, it seemed like a good thing to include.

Let’s examine some of the steps.

Scripting the Creation of a Dynamic Microsoft 365 Group

Here’s an example of creating a new dynamic Microsoft 365 group for the department whose name is stored in the $Dept variable:

Write-Host ("Checking groups for department {0}" -f $Dept)
$Description = ("Dynamic Microsoft 365 group created for the {0} department on {1}" -f $Dept, (Get-Date))
$DisplayName = ("{0} Dynamic group" -f $Dept)
$MailNickName = ("Dynamic.{0}.Group" -f ($Dept -replace " ",""))
$MembershipRule = '(User.Department -eq "' + $Dept +'")'

If ($DisplayName -in $Groups.DisplayName) {
   Write-Host ("Group already exists for {0}" -f $Dept) -ForegroundColor Red
} Else {
# Create the new dynamic Microsoft 365 Group
   $NewGroup = New-MgGroup -DisplayName $DisplayName -Description $Description ` 
   -MailEnabled:$True -SecurityEnabled:$False `
   -MailNickname $MailNickName -GroupTypes "DynamicMembership", "Unified" `
   -MembershipRule $MembershipRule -MembershipRuleProcessingState "On"
}

Wait Before Progressing to Teams

Flushed with the successful creation, you might want to rush to team-enable the new group. However, it’s best to wait 10-15 seconds before proceeding to allow Teams to learn about the new group from Entra ID. If you attempt to team-enable a group immediately after creation, you’ll probably see an error like this:

Failed to execute Templates backend request CreateTeamFromGroupWithTemplateRequest. Request Url: https://teams.microsoft.com/fabric/emea/templates/api/groups/bab7a3a8-2e30-4996-9405-48ca395b99c6/team, Request Method: PUT, Response Status Code: NotFound, Response Headers: Strict-Transport-Security: max-age=2592000
x-operationid: a228258204c3466dbd64c4d88373a416
x-telemetryid: 00-a228258204c3466dbd64c4d88373a416-82a9b5015f332574-01
X-MSEdge-Ref: Ref A: FC01DAADBD0D4A1A9ECBB9826707CC17 Ref B: DB3EDGE2518 Ref C: 2023-10-04T15:00:51Z
Date: Wed, 04 Oct 2023 15:00:52 GMT
ErrorMessage : {"errors":[{"message":"Failed to execute GetGroupMembersMezzoCountAsync.","errorCode":"Unknown"}],"operationId":"a228258204c3466dbd64c4d88373a416"}

Team-Enabling a Group

To team-enable a group, run the New-MgTeam cmdlet and provide a hash table containing information to allow Teams to find the new group (the Graph URI for the group) plus the Teams template to use. This code does the trick.

$GroupUri = "https://graph.microsoft.com/v1.0/groups('" + $NewGroup.Id + "')"
$NewTeamParams = @{
   "template@odata.bind"="https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
   "group@odata.bind"="$($GroupUri)"
}
$NewTeam = New-MgTeam -BodyParameter $NewTeamParams
If ($NewTeam) {
   Write-Host ("Successfully team-enabled the {0}" -f $NewGroup.DisplayName)
}

Checking Groups Post-Creation

Figure 1 shows some of the dynamic Microsoft 365 groups created in my tenant. Note the groups for “Information Technology” and the “IT Department.” Obviously my checking of user departments was deficient prior to running the script. The fix is easy though. Decide on which department name to use and update user accounts to have that. Then remove the now-obsolete group. Entra ID will make sure that the accounts with reassigned departments show up in the correct group membership.

Dynamic Microsoft 365 groups created for departments
Figure 1: Dynamic Microsoft 365 groups created for departments

In this case, only one account had “IT Department,” so I quickly updated its department property with:

Update-MgUser -UserId Jack.Smith@office365itpros.com -Department "Information Technology"

I then removed the IT Department dynamic group:

$Group = Get-MgGroup -Filter "displayName eq 'IT Department Dynamic Group'"
Remove-MgGroup -GroupId $Group.Id

Soon afterwards, the membership of the Information Department Dynamic group was correct (Figure 2) and all was well.

Membership of a dynamic Microsoft 365 group for a department
Figure 2: Membership of a dynamic Microsoft 365 group for a department

You can download the complete script from GitHub. It would be easy to adapt the code to run as an Azure Automation runbook to scan for new departments and create groups as necessary.

Simple PowerShell Results in Big Benefits

Scripting the creation of dynamic Microsoft 365 groups for each department in a tenant isn’t too difficult. The membership rule is simple but could be expanded to include different criteria. Once the groups are created, they should be self-maintaining. That is, if you make sure that the department property for user accounts is accurate.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things like dynamic Microsoft 365 groups work.

]]>
https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/feed/ 0 61844
Microsoft’s New My Groups Page https://office365itpros.com/2023/07/05/group-management-problems/?utm_source=rss&utm_medium=rss&utm_campaign=group-management-problems https://office365itpros.com/2023/07/05/group-management-problems/#comments Wed, 05 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60694

Self-Service Group Management for End Users But OWA Option is Broken

By now, your tenant should have received the code for the “My Groups experience” described in message center notification MC522581 (updated on 18 April, 2023). Even though Microsoft predicted that they would complete worldwide deployment by late May, I haven’t invested any time in reviewing what value the new experience delivers. Now that we’ve published Office 365 for IT Pros (2024 edition), I plunged into My Groups to see what it can deliver.

The New My Groups Experience

The new My Groups page (Figure 1) replaces an older page that really didn’t get much attention. Microsoft says that the upgraded and refreshed experience “enables end users to easily manage groups, such as finding groups to join, managing groups they own, and managing existing group memberships.” Of course, self-service management depends on a tenant allowing this activity.

The My Groups page
Figure 1: The My Groups page

Microsoft’s documentation for My Groups explains the available functionality. Generally, everything works well for Microsoft 365 and security groups as you can update membership and group properties, and even delete the groups. My Groups can’t handle dynamic Microsoft 365 Groups through. This isn’t surprising as the membership of these groups is dictated by queries executed against Azure AD that “normal” users probably couldn’t construct.

The biggest issue with My Groups is its lack of support for distribution lists (groups), or as they’re referred to by My Groups, “Exchange mastered” objects. Distribution lists are valid Azure AD group objects (dynamic distribution lists only exist in Exchange Online) and the methods to update distribution list properties and membership are well known. It’s therefore a mystery why Microsoft should launch a page purporting to enable end-user management of groups when the page is incapable of dealing with a major group type.

The only conclusion I can reach is that the team that developed the My Groups page has an agenda to advance Microsoft 365 groups as the answer for all forms of collaboration. Of course, this is a ridiculous stance, but metrics drive behavior and it’s not unknown for people to do odd things when they’re set a task to advance one option over another. In any case, the lack of support for distribution lists makes the My Groups page a flawed and incomplete implementation that should have been much better.

OWA Distribution Group Management

At the same time, Microsoft has make changes (temporarily) to the ability for users to manage distribution lists through OWA. From a technical perspective, this is understandable because distribution list management depended on components from the old Exchange management center (ECP) that OWA reused. With the demise of the old EAC, those components are less accessible. Now, when you choose the Distribution groups option in OWA settings, you see an unwanted advertisement to use Microsoft 365 groups and a link to a “portal” to manage distribution lists (Figure 2).

Distribution group management option in OWA
Figure 2: Distribution group management option in OWA

The portal (https://outlook.office.com/ecp/MyGroups/PersonalGroups.aspx?showhelp=false) is no more than the old ECP component. Unlike the previous implementation, it takes between ten and fifteen seconds for the ECP code to load. Eventually, the “portal” appears (Figure 3).

The ECP interface to manage distribution lists
Figure 3: The ECP interface to manage distribution lists

On the upside, changes applied through user role assignment policies to restrict users from creating new distribution lists work. On the downside, the code used to update distribution list membership is terrible and doesn’t work. At least, I got tired of waiting to add a new member after sixty seconds of watching the circle of death rotating (Figure 4).

Waiting to update distribution list membership
Figure 4: Waiting to update distribution list membership

Little Evidence of Joined Up Thinking

It’s obvious that no joined up thinking exists within Microsoft when it comes to delivering functionality to allow end user to edit groups that they own. The old OWA distribution list code worked well but only handle distribution lists, and now it’s broken. The new My Groups page works for Microsoft 365 groups but ignores distribution lists. Is it any wonder why people become exasperated with how Microsoft delivers software, especially when it’s to do with features that have worked for years that fail when engineers step in to enhance their capabilities.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/07/05/group-management-problems/feed/ 5 60694
Reporting the Assignment of Container Management Labels https://office365itpros.com/2023/05/09/container-management-labels/?utm_source=rss&utm_medium=rss&utm_campaign=container-management-labels https://office365itpros.com/2023/05/09/container-management-labels/#comments Tue, 09 May 2023 01:00:00 +0000 https://office365itpros.com/?p=60051

Managing Microsoft 365 Group Settings with Container Management Labels

Container management labels are sensitivity labels configured with management controls that Microsoft 365 groups and their associated SharePoint Online sites and teams inherit from labels assigned on the creation of new groups or by administrators afterward. Important settings include external sharing of SharePoint content (Figure 1) and whether guest members are allowed in groups.

Container management settings for a sensitivity label
Figure 1: Container management settings for a sensitivity label

The settings inherited from assigned labels cannot by changed by group or team owners. However, a group owner can change the effective settings for a site by choosing to apply a different container management label. An organization can monitor for label assignments to groups and reserve those changes if necessary. However, this isn’t standard functionality and must be enabled using a tool like PowerShell (here’s an example).

Container Management Labels

Sensitivity labels are available in tenants licensed with Office 365 E3 or above. If you’re in this position, container management labels are an excellent way to ensure consistency in group settings. An organization can deploy and use container management labels even if they don’t use sensitivity labels for information protection and document marking. In fact, even though it’s possible to use the same set of sensitivity labels for both purposes, it’s a good idea to maintain two sets of labels: one for container management and the other for information protection.

Checking Container Management Label Assignments

Two PowerShell methods are available to check group information:

  • The Get-UnifiedGroup cmdlet from the Exchange Online management module.
  • The Get-MgGroup cmdlet from the Microsoft Graph PowerShell SDK.

Because it must fetch information for many aspects of a Microsoft 365 group, Get-UnifiedGroup is a “heavy” cmdlet. The cmdlet will find all groups, but it will be slow. Get-MgGroup is faster because it retrieves fewer properties for each group. The downside is that Get-MgGroup doesn’t include sensitivity labels in its set of properties. A separate Graph call is required to fetch the label assigned to a group.

To illustrate the point, this code finds all Microsoft 365 groups in a tenant and highlights any group that doesn’t have an assigned sensitivity label.

Connect-MgGraph -Scopes Directory.Read.All

Write-Host "Finding Microsoft 365 Groups to process..."
[array]$Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All
If (!($Groups)) { Write-Host "Whoops - can't find any Microsoft 365 Groups" ; break }

ForEach ($Group in $Groups) {
 $LabelId = $Null; $LabelName = $Null

 $Uri = ("https://graph.microsoft.com/v1.0/groups/{0}?`$select=assignedLabels" -f $Group.Id)
 $LabelData = Invoke-GraphRequest -Uri $Uri
 $LabelName = $LabelData.assignedLabels.displayName
 $LabelId   = $LabelData.assignedLabels.labelId
 [array]$GroupOwners = Get-MgGroupOwner -GroupId $Group.Id
 $GroupOwnerNames = $GroupOwners.additionalProperties.displayName -join ", "
 If (!($LabelName)) {
    Write-Host ("The {0} group has no label. Owner(s) {1}" -f $Group.displayName, $GroupOwnerNames) -foregroundcolor Red
 } }
}

By contrast, Get-UnifiedGroup includes sensitivity label data in its properties. However, the property holds the GUID for an assigned sensitivity label instead of its display name. Some additional effort is required to resolve the label GUID to a display name by first fetching the set of sensitivity labels in the tenant (with the Get-Label cmdlet) and building a table of GUIDs and display names to lookup.

This code illustrates how to use the Get-UnifiedGroup cmdlet to accomplish the same goal:

Write-Host "Finding Microsoft 365 Groups to process..."
[array]$Groups = Get-UnifiedGroup -ResultSize Unlimited
If (!($Groups)) { Write-Host "Whoops - can't find any Microsoft 365 Groups" ; break }

ForEach ($Group in $Groups) {
 [array]$GroupOwnerNames = $Null; $LabelId = $Null; $LabelName = $Null

 $LabelId = $Group.SensitivityLabel.Guid
 If ($LabelId) {
   $LabelName = $LabelHash[$LabelId] }

 [array]$GroupOwners = $Group.ManagedBy
 ForEach ($Owner in $GroupOwners) {
    [string]$Owner = $Owner
    $GroupOwnerNames += (Get-Mailbox -Identity $Owner -ErrorAction SilentlyContinue).DisplayName }
 [string]$GroupOwnerNames = $GroupOwnerNames -join ", "
 If (!($LabelName)) {
    Write-Host ("The {0} group has no label. Owner(s) {1}" -f $Group.displayName, $GroupOwnerNames) -foregroundcolor Red
 }
}

The code doesn’t include the commands to connect to Exchange Online and the compliance endpoint (to get label information), nor does it include the code to build the hash table used for label lookups. These commands add about ten seconds of overhead. This isn’t usually a problem.

Testing against 250 groups, the Get-MgGroup method took 1 minute 12 seconds while Get-UnifiedGroup took 1 minute 28 seconds. Generally speaking, Graph-based cmdlets are always faster than the more complex cmdlets used by workloads like Exchange, especially when running at scale against thousands of objects.

Completing the Job

After deciding which approach to use, to finish the job, we put the results of the scan into a PowerShell list and generate a report. The final result is a little more complicated than the processing described above to format the output (Figure 2) and include some additional sections like listing groups that don’t have a label, groups that don’t have any owners, and summary data like how many assignments to groups for each label.

Reporting Container Management labels for Microsoft 365 groups
Figure 2: Reporting Container Management labels for Microsoft 365 groups

The final script is available from GitHub. Feel free to improve the output!


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/05/09/container-management-labels/feed/ 7 60051
Upgrading the Microsoft 365 Groups and Teams Membership Report Script https://office365itpros.com/2023/01/19/microsoft-365-groups-report-v2/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-groups-report-v2 https://office365itpros.com/2023/01/19/microsoft-365-groups-report-v2/#comments Thu, 19 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58775

Moving the Microsoft 365 Groups Report Script from Azure AD to the Graph SDK

Two years ago, I wrote a script to report the membership of Microsoft 365 groups and teams. The script processes user accounts to find accounts they are members of and generates detailed and summary reports.

As it turned out, I ended up writing two versions of the script: one using standard PowerShell cmdlets from the Exchange Online PowerShell and Azure AD modules, the other using Graph API requests. The Graph version is faster but some people don’t like Graph-based scripts because of the requirement to register an Azure AD app, consent to permissions, and so on.

Time and technology march on and it’s time to review any script that uses the Azure AD module because of its imminent deprecation in June 2023. Imminent sounds like a strange word to use about something that will happen in five and a half months but time slips away and there’s always something different to be done. I had the time and was already committed to upgrading the script to report “stale” guest accounts, so it seemed like a good idea to plunge into the code and replace the Azure AD and Exchange Online cmdlets with the Microsoft Graph PowerShell SDK.

Scripts to Process Azure AD Accounts and Groups

I’ve come to the view that it’s now best to use the SDK for anything to do with Azure AD accounts and groups. Because the Exchange Online management module contains cmdlets that operate against Microsoft 365 groups, I could have used those cmdlets in the script, but it’s easier when a script uses just the one module.

The two versions of the scripts are available from GitHub:

Changes to Upgrade to the SDK

Among the changes made to upgrade the script were:

  • Connect to the Graph with Connect-MgGraph, setting appropriate permissions and selecting the beta endpoint.
  • Replace the Exchange Get-Organization cmdlet with SDK Get-MgOrganization to fetch tenant name.
  • Replace Get-AzureADUser with Get-MgUser. The filter used with Get-MgUser fetches only licensed accounts (excludes guests and accounts used for room and resource mailboxes). Replacing Get-AzureADUser is one of the more common changes that people will make as they upgrade scripts. See this article for more information.
  • Replace Get-UnifiedGroup with Get-MgTeam to fetch a list of team-enabled groups.
  • Replace Get-Recipient with the Graph MemberOf API to find the set of groups a user is a member of. The Invoke-MgGraphRequest cmdlet runs the Graph query to remove the need to register an app.
  • Use Get-MgGroupOwner to return group owners instead of fetching this information from the ManagedBy property available with Get-UnifiedGroup.
  • Other miscellaneous changes of the type that you find you make when reviewing code.

The code generates the same reports as before (HTML report  – Figure 1 – and two CSV files). All the change is in the plumbing. Nothing is different above water.

HTML version of the Microsoft 365 Groups and Teams report

Microsoft 365 Groups Report
Figure 1: HTML version of the Microsoft 365 Groups and Teams report

Unpredictable Upgrade Effort

It’s hard to estimate how long it will take to upgrade a script to use the Microsoft Graph PowerShell SDK. Factors include:

  • The number of lines of code in the script.
  • The number of Azure AD cmdlets to replace.
  • How easy it is to replace a cmdlet. Microsoft publishes a cmdlet map to guide developers. The caveat is that sometimes the suggested SDK cmdlet generates different output to its Azure AD counterpart, meaning that some additional processing is necessary. Dealing with group members and owners are examples where changes are likely.

One thing’s for sure. The sooner an organization starts to inventory and upgrade its scripts, the sooner the work will be done and the less likely the effort will run into a time crunch when Microsoft deprecates the Azure AD and MSOL modules. Deprecation doesn’t mean that cmdlets necessarily stop working (some will, like the license management cmdlets). Support ceases and no further development of the deprecated modules happen, and that’s not a state you want for operational scripts. Time’s ebbing away…


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what changes to PowerShell modules mean for your tenant.

]]>
https://office365itpros.com/2023/01/19/microsoft-365-groups-report-v2/feed/ 1 58775
Microsoft Dumps Bulk Distribution List Migration to Microsoft 365 Groups in Legacy EAC https://office365itpros.com/2022/11/04/distribution-list-migration-eac/?utm_source=rss&utm_medium=rss&utm_campaign=distribution-list-migration-eac https://office365itpros.com/2022/11/04/distribution-list-migration-eac/#respond Fri, 04 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=57771

No Point in Preserving a Feature No One Uses

Tired of pushing water uphill, the Exchange development group announced plans to deprecate the EAC feature to migrate distribution lists in bulk to Microsoft 365 Groups (Figure 1). The news comes as no surprise because the feature has not been maintained since its introduction in August 2016. At least, that’s what it seems like.

The Distribution List migration feature in the legacy Exchange admin center
Figure 1: The Distribution List migration feature in the legacy Exchange admin center

Microsoft says that the feature “was designed to provide continuity for DL users and to enable continued collaboration without having to make new groups from scratch. As Microsoft 365 Groups is now a mature feature, we are deprecating the feature for migrating DLs to Groups. The deprecation will happen on February 1, 2023.” The announcement makes no mention of the ability to convert an individual distribution list to a Microsoft 365 Group that’s included in the new EAC. It’s all about bulk conversion.

Changes Since 2016 Affected Outlook Groups

First introduced in November 2014, Office 365 Groups (now Microsoft 365 Groups) are certainly a mature technology. But more importantly, the technology environment that existed when Microsoft launched the distribution list migration feature in August 2016 was very different to today. At the time, Office 365 Groups had just picked up support for Azure B2B Collaboration (guest access) and seemed poised to be the cornerstone of Microsoft 365 collaboration.

Everything changed with the preview of Teams in November 2016 (general availability in February 2017). Today, Teams has more than 270 million monthly active users. The last number for Outlook groups is “more than 10 million” (April 2017). I’m sure some growth has happened in the five years since but maybe not much. Teams has sucked a lot of the oxygen out of the Microsoft 365 collaboration space.

Today’s reality is that the importance of Outlook Groups disappeared a long time ago and the major function of Microsoft 365 Groups is as a coordinating mechanism for group resources and the provider of membership services to Teams.

Other Factors in Play in Distribution List Migration

Two other factors are in play. First, Microsoft is busy trying to move functionality from the old Exchange admin center (EAC) to a modernized version. That process is taking far too long. Cutting features enables the transition to accelerate, which is a good thing.

Second, the bulk migration feature can only process simple distribution lists. The value of many distribution lists lies in their ability to include different types of mail-enabled recipient, including other distribution lists. Dynamic distribution lists are very powerful, especially with custom recipient filters, but the migration feature doesn’t support these objects either, even though dynamic Microsoft 365 Groups (and Teams) are.

Admittingly, dynamic Microsoft 365 Groups were a long way away in August 2016, but the fact that the migration feature has not maintained pace with technical developments within Microsoft 365 is evidence that no one has paid much attention to updating the distribution list migration code recently.

Microsoft’s Suggested Replacement is a Manual Conversion

Microsoft’s suggested approach is extraordinarily manual. Essentially, it’s the same approach that you’d take to move an on-premises distribution list to the cloud (create a new Microsoft 365 group and switch the DL attributes to it).

Although their suggestion is a valid approach, I’m surprised that Microsoft hasn’t created a script to automate the process. The task is not particularly difficult, especially if only “eligible” (simple, non-nested) distribution lists are the target. Microsoft might even be able to repurpose the code in the to-be-deprecated EAC feature, if only the appetite existed to deliver a conversion script to customers. Some old migration scripts are still available in the Microsoft download center that could have been updated and brought into service. Maybe Microsoft doesn’t want the hassle of supporting the code.

It’s even possible to script the conversion of dynamic distribution lists to Microsoft 365 Groups, albeit with static group membership because of the different filtering capabilities that exist in Exchange Online and Azure AD.

Migrations can be tough. I’m sure Microsoft has some data to justify their decision to deprecate the conversion feature. Maybe they’ve noticed a reduction in the use of distribution lists or that the percentage of Microsoft 365 Groups that aren’t team-enabled is dropping. Only Microsoft knows. What’s real is that February 1, 2023, will see the disappearance of a feature that once promised so much and ended up being a neglected disappointment.

]]>
https://office365itpros.com/2022/11/04/distribution-list-migration-eac/feed/ 0 57771
Using Hidden Membership for Microsoft 365 Groups https://office365itpros.com/2022/10/04/hidden-membership-groups/?utm_source=rss&utm_medium=rss&utm_campaign=hidden-membership-groups https://office365itpros.com/2022/10/04/hidden-membership-groups/#comments Tue, 04 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57300

Keeping Group Membership Secret

Customers expressed the desire to hide the membership of Microsoft 365 Groups soon after Microsoft launched Office 365 Groups in November 2014. Microsoft duly shipped the feature in early 2015.

Many scenarios exist to cloak the membership of a group. Some educational establishments don’t like revealing the full membership of classes; corporations engaged in confidential activities (like a merger and acquisition project) might like to hide the fact that external advisors have joined an internal team. Other organizations like to hide the membership of some committees, and so on.

Creating Groups with Hidden Memberships

Only PowerShell supports the creation of a Microsoft 365 group with hidden membership. This code creates a new group with the New-UnifiedGroup cmdlet and adds some members and a second owner with the Add-UnifiedGroupLinks cmdlet. The account that runs the New-UnifiedGroup cmdlet automatically becomes an owner:

New-UnifiedGroup -Alias "Super.Secret.Team" -PrimarySmtpAddress Super.Secret.Team@office365itpros.com -HiddenGroupMembershipEnabled:$True -Name "Super Secret Team"
Add-UnifiedGroupLinks -Identity Super.Secret.Team -LinkType Member -Links Sean.Landy, Terry.Hegarty, James.Ryan, Jackson.Hoare, Jane.Sixsmith, Michael.King
Add-UnifiedGroupLinks -Identity Super.Secret.Team -LinkType Owner -Links Michael.King

When a group has hidden membership, it means that Exchange Online only reveals details of the group membership to its members (through client interfaces) and tenant administrators (through administrative interfaces). This statement isn’t 100% true. As shown in Figure 1, when users browse an address list, they can’t see the group membership, but they can see one of the group owners, who are also group members. This means that part of the group membership is exposed.

Hidden membership for a Microsoft 365 group
Figure 1: Hidden membership for a Microsoft 365 group

Distribution lists also support hidden membership. Like Microsoft 365 Groups, you can set hidden membership when creating a new distribution list or you can hide membership for an existing distribution list. For example, this command creates a new distribution list with hidden membership.

New-DistributionGroup -Alias "SecretDL" -Name "Secret Distribution List" -DisplayName "Secret Distribution List" -PrimarySmtpAddress SecretDl@office365itpros.com -HiddenGroupMembershipEnabled:$True

When a Microsoft 365 group has hidden membership, its membership cannot be revealed by updating group properties. The Set-UnifiedGroup cmdlet doesn’t support updating the HiddenGroupMembershipEnabled setting. However, you can restore visible membership for a distribution list. For example:

Set-DistributionGroup -Identity SecretDL -HiddenGroupMembershipEnabled:$False

And if you make a mistake, you can reverse course and hide the membership again.

Set-DistributionGroup -Identity SecretDL -HiddenGroupMembershipEnabled:$True

Remember that Exchange Online must generate updated OAB files for Outlook to download and apply before changes to membership visibility become completely effective in Outlook desktop.

Sensitivity Labels and Hidden Group Privacy

Only a private Microsoft 365 group can have hidden membership. PowerShell and the other administrative interfaces will stop administrators changing the access type from private to public. Another thing to consider is what sensitivity label the new group should receive. Remember that sensitivity labels can control the privacy type for a group. If you assign a sensitivity label that applies container management settings, the access type set by the label must be Private. If not, you’ll see an error.

Figure 2 shows the group settings in the Microsoft 365 admin center. The group sensitivity label is Confidential Access, which is fine because it sets the access type to Private. Any attempt to use a label that sets the access type to Public will result in a cryptic error message that’s not very clear.

 Managing the properties of a Microsoft 365 group with hidden membership in the Microsoft 365 admin center
Figure 2: Managing the properties of a Microsoft 365 group with hidden membership in the Microsoft 365 admin center

In addition, attempts to change the privacy (access type) through this interface won’t work because “visibility of a group with hidden membership cannot be updated.”

Hiding Groups from Address Lists

The “don’t show team email address in Outlook” setting controls the group’s HiddenFromAddressListsEnabled property. By default, the value of the property is False, meaning that Exchange Online includes the group in its address lists, including the Offline Address Book (OAB) and Global Address List (GAL). The effect of choosing this option is to stop users finding an entry for the group (and therefore being able to see its SMTP address) when they browse Outlook address lists. For example, there’s no sign of the group in the Outlook address book (Figure 3).

No trace of a hidden group in the GAL
Figure 3: No trace of a hidden group in the GAL

To make the group visible in address lists, update the setting in the admin center or run Set-UnifiedGroup to update the property:

Set-UnifiedGroup -Identity Super.secret.team -HiddenFromAddressListsEnabled $False

Remember that hiding the SMTP address of a group doesn’t stop people from sending messages to the group. It’s a visual block, not a hard block imposed in the transport service. If you want to restrict the people who can send messages to a group, use the AcceptMessagesOnlyFromSendersOrMembers property. This example stops the group accepting messages from anyone but group members.

Set-UnifiedGroup -Identity Super.secret.team -AcceptMessagesOnlyFromSendersOrMembers "Super.Secret.Team@Office365itpros.com"

Teams and Hidden Membership

Teams supports Microsoft 365 Groups with hidden membership. To team-enable our group, use the Add Teams option in the General tab of the group’s properties in the Microsoft 365 admin center. Alternatively, connect to Teams with PowerShell and run the New-Team cmdlet with the GroupId parameter pointing to the Azure AD identifier for the Microsoft 365 group:

Connect-MicrosoftTeams
New-Team -GroupId (Get-UnifiedGroup -Identity Super.Secret.Team | Select-Object -ExpandProperty ExternalDirectoryObjectId)

As only team members can access a team, they’re the only ones who can see the membership.

Impact on Reporting

Because administrative interfaces always have access to group membership data, setting group membership to be hidden might or might not affect the data returned by PowerShell cmdlets and Graph API requests. For example, the script to generate a report of Teams membership includes hidden membership because the code accesses each team to retrieve its membership. However, because the Graph TransitiveMemberOf API doesn’t include hidden membership in its results, the script to generate a report of membership of Microsoft 365 Groups (and Teams) doesn’t include groups and teams with hidden membership data.

Hidden is Good for Some

I don’t come across many situations where tenants use groups with hidden memberships and Office365ITPros.com hasn’t had many questions about this topic over the years. The feature is there, it works, and it solves a problem for some. I guess that’s all we need to say about it.


Keep up with the changing world of the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. Monthly updates mean that our subscribers learn about new developments as they happen.

]]>
https://office365itpros.com/2022/10/04/hidden-membership-groups/feed/ 9 57300
The Odd Azure AD Selected Visibility is Not Allowed Problem https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-admin-center-issues https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/#comments Fri, 19 Aug 2022 01:00:00 +0000 https://office365itpros.com/?p=56571

Azure AD Admin Center Doesn’t Respect Sensitivity Label Settings

Last June, I tested the preview of nested dynamic Azure AD groups and encountered an odd “Per label policy, the selected visibility is not allowed” error when attempting to create new groups in the Azure AD admin center. Pressure of time forced me to ignore the problem and create the groups I wanted with PowerShell, but time allowed me this week to return to the problem.

It didn’t take long to reproduce the problem and track down the root cause (Figure 1).

Failed to create group because "the selected visibility is not allowed"
Figure 1: Failed to create group because “the selected visibility is not allowed”

Visibility Set to Private for New Microsoft 365 Groups

I used the Graph X-Ray tool to look at the PowerShell generated by the Azure AD admin center when it adds new Microsoft 365 groups. Here’s what Graph X-ray reported:

$params = @{
	DisplayName = "Viva Topics Management"
	MailEnabled = $true
	SecurityEnabled = $true
	GroupTypes = @(
		"Unified"
	)
	Description = "People who manage Viva Topics"
	MailNickname = "VivaTopicsManagement"
	AssignedLabels = @(
		@{
			LabelId = "e42fd42e-7240-4df0-9d8f-d14658bcf7ce"
		}
	)
	Visibility = "private" }}

I copied the command and ran it interactively and saw this error:

New-MgGroup -BodyParameter $params
New-MgGroup : Property visibility is not compliant with the assigned label.
At line:18 char:1
+ New-MgGroup -BodyParameter $params
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ({ body = Micros...ftGraphGroup1 }:<>f__AnonymousType1`1) [New-MgGroup
   _Create1], RestException`1
    + FullyQualifiedErrorId : Request_BadRequest,Microsoft.Graph.PowerShell.Cmdlets.NewMgGroup_Create1

Changing the command to set the visibility property in the parameters to “Public” allowed the command to run successfully and create the group. This is what I expected because the container management settings for the sensitivity label chosen for the new group sets its visibility to Public.

The root cause is that the command generated by the Azure AD admin center sets the access type for the new group incorrectly. Instead of reading the group’s access type (visibility) from the sensitivity label, the command uses “Private” for the value. This means that the command works for any group created with a sensitivity label that sets the access type to Private but fails for public groups.

The Azure AD admin center UI doesn’t include a field to allow the visibility to be selected for a new group, so some overhaul of the UI is needed to display the visibility inherited when a sensitivity label is selected. In addition, Microsoft’s documentation for creating a new group in the Azure AD admin center doesn’t mention visibility at all, so there’s no hope in interpreting the error message.

Inconsistent Microsoft 365 Group Management

I’m unsure of how many new Microsoft 365 groups are created in the Azure AD admin center. My feeling is that most administrators create new groups through the Microsoft 365 admin center or a workload-specific portal like the Teams admin center or SharePoint Online admin center, or even a client like OWA or Teams. All these interfaces (and PowerShell) respect the container management controls imposed by sensitivity labels. If the Azure AD admin center was heavily used, I’m sure Microsoft would have heard about the problem before I reported it on August 15.

In any case, this is not the only example of inconsistency between the Azure AD admin center and the workload portals. Take the security enabled property for a group. This is set to $True by the Azure AD admin center and $False by the Microsoft 365 admin center. That doesn’t sound too serious, but it means that groups not created in the Azure AD admin center can’t be used for purposes like group-based license management. This is where you manage license assignments by allocating them to members of a group. It’s a convenient way to manage licenses (Figure 2).

Group-based license management in the Azure AD admin center
Figure 2: Group-based license management in the Azure AD admin center

The security enabled property isn’t exposed in the Azure AD admin center UI, so if you want to update a group to make it available for group-based license management, you need to use PowerShell. The steps are simple using the group management cmdlets from the Microsoft Graph PowerShell SDK. The first command finds the group to use. The second updates its SecurityEnabled property.

$Group = (Get-MgGroup -Filter "DisplayName eq 'HR Working Group'")
Update-MgGroup -GroupId $Group.Id -SecurityEnabled:$True

After the Update-MgGroup cmdlet runs, you should be able to use the group for group-based license management.

Small But Irritating Issues

Neither of the issues described here are earthshattering. Both can be worked around by competent tenant administrators who understand how Microsoft 365 works. The problem is that issues like this cause grief to inexperienced administrators and complicate the learning curve for cloud services. That’s a great pity.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/feed/ 1 56571
Microsoft Previews Nested Dynamic Azure AD Groups https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-azure-ad-group-members https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/#comments Wed, 08 Jun 2022 01:00:00 +0000 https://office365itpros.com/?p=55392

Including Members from Other Groups in Membership of Dynamic Groups

Until recently, Dynamic Azure AD Groups have not supported the ability to include members from other groups in their membership (aka, nested groups). You can construct membership rules to include the same accounts in a dynamic group, but it’s easier to say, “include the members from groups 1, 2, and 3” if those groups already exist and include the necessary accounts.

In a June 6 announcement, Microsoft introduced a preview feature to allow the membership rules for dynamic Azure AD groups to use the memberOf attribute. In essence, memberOf instructs Azure AD to extract the membership of one to up to 50 groups and include the individual members of those groups in the dynamic membership.

Creating a New Azure AD Dynamic Group

Apparently, the preview feature is available worldwide. I had no success using it in the Azure AD admin center. Any attempt to create a new group (of any type) generated the error: “per label policy, the selected visibility is not allowed” (Figure 1). No doubt this is due to some configuration I have tweaked, but the error message is obscure, to say the least. (Update: I discovered the root cause of the problem, which Microsoft say they will fix).

Azure AD fails to create a group

per label policy, the selected visibility is not allowed
Figure 1: Azure AD fails to create a group

But where the will exists, you get the job done, and PowerShell came to the rescue. I created the new dynamic group with the following command. You can see that the membership rule is that the membership comes from any user members in the specified groups.

$Group = New-MgGroup -DisplayName "System Innovation" -Description "Dynamic group containing system innovators" -MailEnabled:$True -SecurityEnabled:$False -MailNickname SystemInnovators -GroupTypes "DynamicMembership", "Unified" -MembershipRule "user.memberOf -any (group.objectId -in ['ef4af711-bf83-4ba1-81be-fd98f4098d12',' d6279df7-2eff-4566-ba93-22aa9320385b','b07c7e05-10e0-47a4-acca-767621ac8ddc'])" -MembershipRuleProcessingState "On"

The groups added were:

  • A Microsoft 365 group with assigned membership.
  • A Microsoft 365 group with dynamic membership.
  • A distribution list with a fixed membership.

Microsoft’s documentation doesn’t include any reference to using distribution lists, but as Azure AD treats distribution lists like other groups, it seemed like they should work. After all, you can run the Get-MgGroupMember cmdlet (or Get-AzureADGroupMember if you still haven’t converted from the soon-to-be-deprecated module) against a distribution list and Azure AD is happy to list the members. And as it turns out, you can include the membership of distribution lists in dynamic Azure AD groups. After an hour or so, Azure AD resolved the rule and built the membership of the new dynamic group, including the removal of any duplicates (Figure 2).

Membership of the new dynamic Azure AD group
Figure 2: Membership of the new dynamic Azure AD group

Preview Limits

During the preview, a dynamic group can have up to 50 groups in its membership, and each tenant can have up to 500 dynamic groups that use the memberOf attribute in their membership rule. If you add security groups to the membership of a dynamic group, Azure AD includes only the direct members of the security group in the dynamic group’s membership.

In addition, Microsoft says that you can’t use a dynamic group that uses the memberOf attribute to define the membership of another group that also uses memberOf. The old and well-proven adage to keep it simple (stupid) rings loud and clear. Don’t nest groups inside groups and don’t over-complicate things. Perhaps more complicated arrangements might be possible in the future, but for the preview, don’t give Azure AD complex membership rules to resolve. For more information on including groups within the membership of dynamic groups, read Microsoft’s documentation.

Another issue is that the memberOf attribute can’t be used with other rules. For instance, let’s assume that you assemble a set of users drawn from the membership of several other group. You can’t add another filter to select people whose accounts match another attribute, such as the department or country.

For now, the rules editor doesn’t work for this type of dynamic group, nor does the other Validate Rules preview feature which allows administrators to check the effectiveness of a membership rule against an account that they know should be in a group’s membership (Figure 3).

Azure AD can't validate membership of a dynamic group
Figure 3: Azure AD can’t validate membership of a dynamic group

Dynamic Teams Work Too

I updated the group’s properties to enable it for Teams. Support for dynamic teams has been around since 2018, but it’s always wise to check. The good news is that the dynamic membership for the team appears as expected (Figure 4).

Teams membership roster for the dynamic Azure AD group
Figure 4: Teams membership roster for the dynamic Azure AD group

Solid Update

There’s no doubt that this is a good change. Anything that adds to the flexibility and capability of dynamic Azure AD groups is a good thing. The bad thing is that Microsoft requires Azure AD Premium P1 for dynamic groups (Exchange Online dynamic distribution lists don’t need additional licenses). The guidance is:

This feature requires an Azure AD Premium P1 license or Intune for Education for each unique user that is a member of one or more dynamic groups. You don’t have to assign licenses to users for them to be members of dynamic groups.

It would be nice if dynamic groups were included in Office 365 E3, but life is cruel sometimes…


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/feed/ 6 55392
Don’t Give Up on Azure AD Guest Accounts https://office365itpros.com/2022/06/06/azure-ad-guest-accounts-valuable/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-guest-accounts-valuable https://office365itpros.com/2022/06/06/azure-ad-guest-accounts-valuable/#comments Mon, 06 Jun 2022 01:00:00 +0000 https://office365itpros.com/?p=55362

Azure B2B Collaboration Likely to be Dominant for Immediate Future

The introduction of shared channels in Teams is a big deal, as is the advent of Azure AD Direct Connect, the vital underpinning that enables tenants to accept the credentials granted to accounts in other Microsoft 365 tenants. Cross-tenant access policies control who can share channels in your organization and who can share channels in other organizations.

Some might have missed the news that cross-tenant access settings can also control Azure B2B collaboration (guest accounts). This is also a big step forward because it addresses the need some tenant administrators have expressed to be able to control the organizations where their users can join groups using Azure AD guest accounts. Control over inbound guests has been available with the Azure AD B2B Collaboration policy for several years, so adding outbound control is welcome.

Sharing with Teams

Last week, Microsoft posted about the ways to collaborate externally using Teams. The descriptions of Azure AD Direct Connect, Azure B2B Collaboration, and external federation (one-to-one chat and calls with people in other organizations) were accurate, and the information was well-timed. Many Microsoft 365 tenants are figuring out their external collaboration strategy and wondering where to put their effort over the coming years,

I’ve heard a few commentators say that Azure AD guest accounts now have limited usefulness. The theory is that everyone will move to shared channels. I think that’s overstating the case more than a little. Shared channels are very convenient, especially in their accessibility. There’s no need to switch to another tenant to access information. Shared channels are right there, listed by your favorite Teams client alongside the other teams and channels from your home tenant.

For now, shared channels are only sharable with accounts from other Microsoft 365 tenants. Shared channels will reveal their full potential when they support access from Microsoft Services Accounts (MSAs), a feature that Microsoft is working on.

Azure AD Guest Accounts Still Super-Valuable

But even when that happens, I think Azure AD guest accounts still continue to offer a lot of value. Switching to another tenant can slow access down a tad, but switching performance is much faster now than it used to be (even if it’s not one of the improvements listed in Microsoft’s latest news about Teams performance), and I think I am now immune to the need to switch to a tenant to collaborate with people in that organization. Muscle memory is a great thing and it’s one of the reasons why many of the teams I use feature lots of Azure AD guest accounts (Figure 1).

Team with multiple Azure AD guest accounts in its membership
Figure 1: Azure AD Guest Accounts are a big part of a many teams

Once added to a tenant, a guest has full access to the teams and groups they are a member of. Within a single team, they can access up to 200 regular channels and, if added as a member, they can participate in another 30 private channels. A shared channel has its own roster of members and if a team supports multiple shared channels, the channel owner must share with external users in each channel.

Most of the external organizations I work with have teams with multiple channels. Some are channel-happy and explore the limits set by Teams. Others are more restrained. Even so, the teams usually have several channels to support discussions about different topics. Private channels don’t seem to be popular but are present and I use some in other tenants. Shared channels might be a better choice than private channels in many cases, especially when MSA support is available.

The Question of Guest Hygiene

Mention guest hygiene and you’re probably thinking about the mess unwelcome guests can make in your house. Microsoft says that using Azure B2B Collaboration means that organizations should implement “a guest hygiene process to remove guest accounts when no longer needed.” In other words, instead of remaining passive and letting Azure AD guest accounts accumulate over time, organizations should review old guest accounts and figure out if they should remain.

A simple check against age will often highlight unused guest accounts. If you have Azure AD Premium P2 licenses, Microsoft offers a more sophisticated account review process. No matter what approach you take, it’s a good idea to run an annual review to clean out old accounts.

Overall, it’s likely that Azure AD guest accounts will remain the foundation for most external collaboration based on Microsoft 365 groups and teams. Shared channels will nibble away and take some of the work, but it won’t take over from Azure AD guest accounts in the immediate future.

]]>
https://office365itpros.com/2022/06/06/azure-ad-guest-accounts-valuable/feed/ 2 55362
Why Teams Sometimes Won’t Allow External Users In https://office365itpros.com/2022/05/26/teams-blocks-external-users/?utm_source=rss&utm_medium=rss&utm_campaign=teams-blocks-external-users https://office365itpros.com/2022/05/26/teams-blocks-external-users/#comments Thu, 26 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55245

Teams Blocks External Users as Guests and From Sharing Channels

A reader asked why Teams blocks external users. In this case, they had difficulties adding a new guest account to a team’s membership. Anytime they attempted to add the guest by typing in their email address, Teams responds with “we didn’t find any matches” (Figure 1).

Teams won't add a new guest
Figure 1: Teams won’t add a new guest

The error text isn’t very useful, and Microsoft could improve it. What it means is that Teams couldn’t match the email address of the external user against the set permitted for the team.

The usual problem is that something blocked guest access for the team. This can happen because:

  • The organization blocks guest access for all teams.
  • The organization uses sensitivity labels to control guest access, and the label assigned to the team blocks guests.
  • If the organization doesn’t use sensitivity labels, administrators can block guest access for a specific team by updating the Azure AD directory settings for the Microsoft 365 group (this is what sensitivity labels do when they block access).
  • The user attempting to add the guest doesn’t have the necessary permission. Normally, team owners can add guests, but the organization can restrict this capability to administrators.

If guests can join other teams, no organization-wide block on guests is present. If one is, administrators can lift it by updating the Microsoft 365 Groups settings in the Microsoft 365 admin center (Figure 2).

Organization setting allowing guests to join Teams and Microsoft 365 Groups
Figure 2: Organization setting allowing guests to join Teams and Microsoft 365 Groups

Container Management Blocks

If the organization uses sensitivity labels for container management, the block might be present because the team inherited the setting from its sensitivity label, so it’s the next thing to check. Go to the Information protection section of the Microsoft Purview Compliance portal and check the label assigned to the team. Its settings (or maybe just the description – Figure 3) will tell you if the label blocks guest members.

Sensitivity label settings could block guest access
Figure 3: Sensitivity label settings could block guest access

Not all organizations use sensitivity labels for container management. The block on guest access can be applied using PowerShell, so you’d need to check the group settings to make sure that they permit guest access (or not).

Finally, check the External collaboration settings under External identities in the Azure AD admin center to check that someone hasn’t restricted the ability of group owners to add guests.

Azure AD B2B Collaboration Blocked Domains

While discussing External collaboration settings, we should cover a related issue, which is when group owners can’t add a guest account because the Azure AD B2B collaboration policy blocks the guest’s domain. When this happens, Teams accepts the external email address, but then fails when it attempts to create the guest account (Figure 4).

Teams can't add a guest from a blocked domain
Figure 4: Teams can’t add a guest from a blocked domain

The solution is to amend the Azure B2B collaboration policy to remove the block on the domain. If this isn’t possible, the external person can never become a guest using an email address from the blocked domain.

Can’t Share a Shared Channel

Teams displays the unhelpful error text as a catch-all for multiple conditions. Teams flags the same error if you attempt to share a shared channel with an external user from another Microsoft 365 tenant when cross-tenant access settings don’t allow access from the external user’s domain (Figure 5).

Teams can't add an external user to a shared channel because no trust exists
Figure 5: Teams can’t add an external user to a shared channel because no trust exists

The same kind of logic applies. You asked Teams to share a channel. It checked the set of domains it can share channels with and found that the requested domain isn’t in the set, so issued the “we didn’t find any matches” error.

In this case, the solution is to amend the cross-tenant access settings in your tenant to allow inbound access for external users from the other domain, and to ask the administrator of the other domain to permit outbound access to your domain. Cross-tenant access works on a mutual trust basis, so you can’t share a channel with someone from another unless their tenant is happy for this to happen.

Take Your Time

After making any changes, it’s important to be patient and allow the changes to replicate within Azure AD and Teams. Eventually (after about 24 hours), the planets align, and permissions are in place, and you’ll be able to add external users as guests to team memberships or share channels with people in other tenants.


Learn about managing guest access for Teams and Microsoft 365 Groups and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2022/05/26/teams-blocks-external-users/feed/ 4 55245
Use Azure AD Access Reviews to Check for Inactive Guests https://office365itpros.com/2022/05/17/azure-ad-access-review/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-access-review https://office365itpros.com/2022/05/17/azure-ad-access-review/#comments Tue, 17 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55074

Remove Inactive Guests from Microsoft 365 Groups

Azure AD access reviews are a premium Identity Governance feature that helps organizations conduct periodic reviews of user and guest access to resources, including the membership of Microsoft 365 groups. Automation of this kind is most valuable in large enterprises where administrators can find it difficult to keep track of groups, guests, permissions, and role assignments. Further automation and reporting of access reviews are possible using a Graph API.

Tenants can enable a 30-day free trial of Azure AD Premium P2, which you’ll need if you want to test access reviews before deciding to make a long-term commitment. Licensing for guest accounts in the groups within the scope of the review is covered by Azure AD’s Monthly Active User (MAU) billing model, which requires an Azure subscription.

Finding Inactive Guests

Soon after Microsoft introduced Azure AD guest support for Office 365 Groups in late 2016, it became clear that not much administrative support was available to manage guest accounts. Since then, the number of guest accounts in tenants has exploded, largely due to the success of Teams, but also because SharePoint Online creates guest accounts for document sharing. However, the toolset available to manage the burgeoning guest accounts is still sparse.

Recently, Microsoft introduced a new preview feature for Azure AD access reviews to allow organizations to conduct an access review for inactive guest accounts, defined as “those who have not signed in either interactively or non-interactively to the tenant.”

Creating an access review to look for inactive guests is simple. The review covers:

  • All Microsoft 365 Groups with guest members, checking only guest users.
  • The period to determine inactivity can be anything from 1 to 730 days.

Other tabs have settings to cover whether the review is a one-off event or happens on a schedule, what to do if reviewers don’t respond, and what happens when the review period completes.

Figure 1 shows the access review I created to locate guests inactive for the last 365 days.

Creating an Azure AD access review for inactive guest accounts
Figure 1: Creating an Azure AD access review for inactive guest accounts

Reviewing Inactive Guests

After creating the review, Azure AD background processing locates Microsoft 365 groups in the tenant that have guest members. Azure AD uses sign-in records for the review period to determine if any guests in a group are deemed inactive, Azure AD sends email to the group owner (Figure 2) to ask them to review the inactive groups and decide if the membership in the group should continue for the inactive guests.

Email notification for a group owner to review inactive guests
Figure 2: Email notification for a group owner to review inactive guests

Clicking the Start review link in the message brings the group owner to a page in MyAccess.microsoft.com to allow them to see the inactive guests and make a decision for each (Figure 3). In this case, Azure AD was unable to find any sign-in data for the guest account.

Performing an Azure AD access review for an inactive guest
Figure 3: Performing an Azure AD access review for an inactive guest

A group owner can decide to ignore the review, in which case the settings for the access review determines what happens. This might be to do nothing; it could also be to remove access for the inactive guest. It’s best if group owners perform the review, even if administrators might have to cajole them to do the work.

After the review period finishes, Azure AD implements the review decisions and removes the inactive guests or leaves them in place.

Sounds Good but What About Outlook Groups

Running an access review to remove inactive guests from group membership sounds like a great idea and the implementation works. However, there’s one big flaw in the scheme and that’s the dependency on sign-in data. This is understandable because it’s an Azure AD review and the best data available to Azure AD to figure out if a guest account is in use is their sign-in history.

The problem is that some guest accounts can be active without ever signing into a tenant. Guest members of Outlook groups (the original implementation of Office 365 groups) use email to communicate and don’t need to ever sign in to the tenant hosting the group unless they want to access other group resources, like its SharePoint Online site or Planner.

I have multiple Outlook groups in this category. The access review highlighted most of the guests in these groups. The only guests that the access review did not tag were those that sign into the tenant to use Teams or another application. Perhaps Microsoft will introduce additional checks to help detect truly inactive guest accounts when this feature moves from preview to generally available status.

The bulk of Microsoft 365 group activity now focuses on Teams, which is an application that signs in every hour during a session. There’s no danger that Azure AD won’t know when guest accounts used with Teams are inactive.

Do-It-Yourself Inactive Guest Reviews

You don’t need to pay for Azure AD access reviews to find potentially inactive guest accounts. Over the years, I’ve written about this topic, most recently to describe my approach to detecting, reporting, and managing inactive guest accounts using PowerShell. An even simpler approach is to create a report for all guest accounts over a certain age together with their group membership.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/05/17/azure-ad-access-review/feed/ 2 55074
Basic Entra ID Group Management with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/03/29/create-entra-id-group/?utm_source=rss&utm_medium=rss&utm_campaign=create-entra-id-group https://office365itpros.com/2022/03/29/create-entra-id-group/#comments Tue, 29 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54278

Create Entra ID Groups with SDK Cmdlets

Updated 28 December 2023

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

Use the Entra ID admin center to create Entra ID groups and manage them afterward.
Figure 1: Use the Entra ID admin center to create Entra ID groups and manage them afterward

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 -NoWelcome

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

Creating Different Kinds of Entra ID Groups

Entra ID groups include:

  • Microsoft 365 groups/Teams (including groups used by Yammer).
  • Security groups.
  • Dynamic 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., nor can Graph API requests or Graph SDK cmdlets update the membership of distribution lists or mail-enabled security groups because these group types are essentially Exchange Online rather than Entra ID 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 }

In the past, checking that the right members are present afterwards is not as simple as it should be. 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}

You can look at the AdditionalProperties property retuned by Get-MgGroup to find information about the group members. For example, this command returns a list of display names for group members:

$GroupData = Get-MgGroupmember -GroupId $GroupId
$GroupData.AdditionalProperties.displayName

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"}

To remove a member from a Microsoft 365 group, use the Remove-MgGroupMemberByRef cmdlet. This cmdlet doesn’t work with distribution lists or mail-enabled security groups. This command removes the user object pointed to by the GUID from a target group identified by the $GroupId variable.

Remove-MgGroupMemberByRef -DirectoryObjectId 08dda855-5dc3-4fdc-8458-cbc494a5a774 -GroupId $GroupId

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. You must do this with the Exchange Online cmdlets.

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 the Restore-MgDirectoryDeletedItem-MgGroup cmdlet and pass the identifier of the group:

Restore-MgDirectoryDeletedItem -DirectoryObjectId 4e9393c3-67e9-4f95-a0df-70103a667c0a

Finding Group Objects

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

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

You can also search using the StartsWith filter:

 Get-MgGroup -Filter "startsWith(displayname, 'Leadership')"

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 groups
GroupTypes = Unified, DynamicMembershipDynamic Microsoft 365 groups
ResourceProvisioningOptions = TeamTeam-enabled Microsoft 365 groups
Table 1: Filters for different types of Entra ID groups

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 Queries Against Entra ID Groups

Get-MgGroup supports complex queries against Entra ID. 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 Entra ID 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.

]]>
https://office365itpros.com/2022/03/29/create-entra-id-group/feed/ 4 54278
Why It’s Difficult to Transfer Membership Rules from Exchange Online to Azure AD https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/?utm_source=rss&utm_medium=rss&utm_campaign=membership-rules-exchange-teams https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/#comments Fri, 18 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54000

Dynamic Distribution Lists to Dynamic Microsoft 365 Groups

Earlier this week, I described how to create a Microsoft 365 group and team from an Exchange Online dynamic distribution list. The code creates a group with static membership, but the input dynamic distribution list has its membership computed by Exchange Online using a recipient filter (aka a membership rule). Why can’t we take the filter used by the dynamic distribution list and apply it to create a dynamic Microsoft 365 group, which in turn becomes a team with dynamic membership. Well, as it turns out, it’s not quite as simple as taking a filter from one Microsoft 365 workload and using it in another.

Translating Recipient Filters for Dynamic Microsoft 365 Groups

Conceptually, it is possible to convert a dynamic distribution list to a be the membership rule for a dynamic Azure AD group. Two challenges exist: filter syntax and filter properties.

The query stored in a dynamic distribution list looks like this:

((((((Title -eq 'Architect') -or (Title -eq 'Senior Architect'))) -or (((Title -eq 'Principal Architect') -and (ExchangeUserAccountControl -ne 'AccountDisabled'))))) -and (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'PublicFolderMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuxAuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'SupervisoryReviewPolicyMailbox')))

We know this is a custom recipient filter created using PowerShell because the properties it uses are not covered by the precanned filters created using EAC. The custom filter comes first followed by a bunch of exclusions inserted by Exchange to make sure that system mailboxes are not in the returned set. Exchange adds these exclusions automatically when it saves the recipient filter for a dynamic distribution list.

It’s technically possible to take a recipient filter from a dynamic distribution list and parse it to extract the custom part using Regex expressions. By using a function to remove special characters, I was able to process the recipient filter shown above like this:

$Filter = (Get-DynamicDistributionGroup -Identity "System Architects").RecipientFilter
$i = $Filter.IndexOf("-and (ExchangeUser")
$f = $Filter.Substring(0,$i)
$ExoFilter = Remove-StringSpecialCharacter -String $f -SpecialCharacterToKeep '-', " "

The output is:

Title -eq Architect -or Title -eq Senior Architect -or Title -eq Principal Architect

However, a complicating factor is that Exchange has changed the format of the exclusions it inserts over time. This means that you can never be sure how the recipient filter is formatted, and my code didn’t work when tested against several other dynamic distribution lists in my tenant, some of which go back to 2014.

In any case, the output I generated isn’t a valid Azure AD filter, and some additional work is needed to make it work with a dynamic Azure AD group (team). Briefly:

  • Title is the name of the Exchange property. It is JobTitle in Azure AD. Also, user properties are prefixed with “User,” meaning that you end up with User.JobTitle.
  • The -eq and -or operators in Exchange lose the leading hyphen in Azure AD.

Different Filterable Properties

A more fundamental issue is that while Exchange supports many mail-enabled properties for custom recipient filters in dynamic distribution lists, the set of filterable properties don’t match the set available for Azure AD. You might be able to convert some queries, but you won’t be able to convert others. The difference is accounted for by the fact that Exchange queries against its own directory, which stores details of mail-enabled objects, while Azure AD queries its directory. The two directories have different schemas.

Once I realized the extent of the incompatibility between the two sets of properties, I stopped trying to figure out how an automatic conversion could be done. Too much time would be needed to figure out the permutations and combinations involved in formatting membership rules. And given the number of times a conversion might be necessary, the easiest solution is to let human administrators generate the membership rules.

Previewing Azure AD Filters

The GUI in the Azure AD admin center to deal with dynamic groups include a rules editor. You can paste the outline of a membership rule taken from an Exchange dynamic distribution list and modify it there. The Azure AD admin center also includes a nifty preview feature to validate that a membership rule works. After making whatever changes are necessary to create a valid rule for Azure AD, you can test the rule by nominating one or more users that you know should match the membership rule. Click the validate button and Azure AD will tell you if the directory can find the users you selected using the rule (Figure 1).

Azure AD checks the membership rule for a dynamic Microsoft 365 group
Membership filter
Figure 1: Azure AD checks the membership rule for a dynamic Microsoft 365 group

Exchange Online doesn’t have a similar way to validate the membership of a dynamic distribution list. Maybe that’s why Microsoft considers dynamic Azure AD groups to be a premium feature and charges accordingly.

Creating a Dynamic Azure AD Group with PowerShell

For the record, you can create a dynamic Azure AD group with PowerShell. In this instance, I use the New-MgGroup cmdlet from the Microsoft Graph PowerShell SDK (the New-AzureADMSGroup cmdlet from the preview version of the Azure AD module will work too). The important point is that the group has dynamic membership rather than static and has a rule to control the membership:

$Group = New-MgGroup -DisplayName "System Architects (Dynamic x2)" -Description "People with an architect job title" -MailEnabled:$True -SecurityEnabled:$True -MailNickName "System.Architects.Dynamic2" -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(User.JobTitle -eq ""Architect"" or  User.JobTitle eq ""Senior Architect

After creating the dynamic Azure AD group, you can team-enable it with the New-Team cmdlet by passing the identifier of the newly created group.

New-Team -GroupId $Group.Id

Incompatible schemas, properties, and syntax might stop the automatic conversion of membership rules, but you can at least get the job done with a little manual effort.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/feed/ 1 54000
Converting Dynamic Distribution Lists to Microsoft 365 Groups and Teams https://office365itpros.com/2022/03/15/convert-dynamic-distribution-list-teams/?utm_source=rss&utm_medium=rss&utm_campaign=convert-dynamic-distribution-list-teams https://office365itpros.com/2022/03/15/convert-dynamic-distribution-list-teams/#respond Tue, 15 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=53989

Creating Teams from Exchange Online DDLs

After writing about the recent revamp of Exchange Online dynamic distribution lists, I was asked if it was possible to create a team from the membership of a dynamic distribution list. The answer is that the steps are straightforward to create a static Microsoft 365 group. Things get more complicated if you contemplate using a dynamic Microsoft 365 group.

Available in both Exchange Online and Exchange Server, dynamic distribution lists are very powerful. That is, if the organization directory is well-maintained with details about people, job titles, department names, offices, country, and so on. The membership of dynamic distribution lists can include any kind of mail-enabled recipient, including other groups. And that’s the first challenge to face: the Microsoft 365 groups used by Teams support a flat membership (no nested groups) composed solely of accounts belonging to the host organization (members and guests): only user mailboxes can migrate to become members of a target Microsoft 365 group.

The second challenge comes into play if you decide that the target Microsoft 365 group should have dynamic membership. The issue here is that dynamic distribution lists use filters executed against Exchange Online’s directory while dynamic Microsoft 365 groups use filters based on Azure AD. Different filters, different syntax, and different properties. More on this later.

Converting a Dynamic Distribution List to a Team with Static Membership

Starting with the simple issue of finding the members of a dynamic distribution list and using this information to create a new Microsoft 365 group, the steps are straightforward:

  • Identify the source dynamic distribution list.
  • Get the members of the dynamic distribution list and throw away any that can’t be members of a Microsoft 365 group.
  • Check that the owner of the source dynamic distribution list is a valid mailbox.
  • Create the new Microsoft 365 group using properties like name and description inherited from the source dynamic distribution group. The person who manages the dynamic distribution list becomes the owner of the Microsoft 365 group.
  • Add the members to the new Microsoft 365 group to the membership.
  • Team-enabled the new Microsoft 365 group.

The script I created is available in GitHub. Normal caveats apply: the code works but it doesn’t have much error checking. It’s there to prove a principle, not be an off-the-shelf solution.

Finding the Source

Multiple ways exist to identify a source dynamic distribution list. This example prompts the user to select one. The code could become a lot more complex to allow the user to make a mistake and select from a numbered list, and so on, but for the purpose of the example all we want is the object identifier for a valid dynamic distribution list:

$InputDDL = Read-Host "Enter the name of the Dynamic Distribution List to convert to a Microsoft 365 Group"
[array]$SourceDDL = Get-DynamicDistributionGroup -Identity $InputDDL -ErrorAction SilentlyContinue

If (!($SourceDDL)) {Write-Host ("Sorry! We can't find the {0} dynamic distribution list" -f $InputDDL); break}
If ($SourceDDL.Count -gt 1) {
   CLS
   Write-Host "We found multiple matching dynamic distribution lists"
   Write-Host "-----------------------------------------------------"
   Write-Host " "
   $SourceDDL | Format-Table DisplayName, Alias, PrimarySMTPAddress
   Write-Host " "
   Write-Host "Please try again..."; break }

[string]$SourceDDLId = $SourceDDL.ExternalDirectoryObjectId

Two methods exist to return the membership of the dynamic distribution list:

  • Run Get-Recipient using the filter stored in the dynamic distribution list.
  • Use the new Get-DynamicDistributionGroupMember cmdlet.

The first method resolves against the Exchange directory and its results are up to date. The second fetches membership data as at the last time Exchange processed the list (more information here). After retrieving the membership using the chosen method, we apply a filter to extract mailboxes.

# Now that we have a source DDL, let's get its membership
[array]$SourceMembers = Get-Recipient -RecipientPreviewFilter (Get-DynamicDistributionGroup -Identity $SourceDDLId).RecipientFilter
# could also be 
# [array]$SourceMembers = Get-DynamicDistributionGroupMember -Identity $SourceDDL.Id
# Throw away anything but user mailboxes because that's all a Microsoft 365 group supports
[array]$ValidMembers = $SourceMembers | ? {$_.RecipientTypeDetails -eq "UserMailbox"}

The next piece of code establishes the owner of the new group. Microsoft 365 groups must have an owner, so if the ManagedBy property of the source list results in an invalid result (for instance, it’s empty), we need to assign ownership to a default account. One way of doing this is to find the set of Exchange administrators for the organization and select one of them, which is done here using the Get-MgDirectoryRoleMember cmdlet from the Microsoft Graph PowerShell SDK and filtering out any service principals assigned the Exchange administrator role. You could simplify the script by hardcoding a default group member.

# We've got to assign an owner to the new Microsoft 365 group, so we need to have a default in case the source DDL doesn't have an owner
# Find the set of accounts that are Exchange admins (you can also use Get-AzureADDirectoryRoleMember here)
[array]$ExoAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId "53add08e-5b0c-4276-a582-9ce02fb6c947" | Select Id, AdditionalProperties 
# Throw away any service principals which might have the Exchange Admin role
$ExoAdmins = $ExoAdmins | ? {$_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.user'} | Select -ExpandProperty Id
# Select the first and use them as the default owner
$ExoDefaultAdmin = Get-MgUser -UserId $ExoAdmins[0] | Select -ExpandProperty UserPrincipalName
# Check that the group owner is a mailbox
$GroupOwner = Get-ExoMailbox -Identity $SourceDDL.Managedby -ErrorAction SilentlyContinue
# If it's null or something weird like a shared mailbox, use the default owner
If (($GroupOwner -eq $Null) -or ($GroupOwner.RecipientTypeDetails -ne "UserMailbox")) {
   $GroupOwner = $ExoDefaultAdmin }
Else {
   $GroupOwner = $GroupOwner.PrimarySmtpAddress
  }

# Populate other group properties
$AliasDDL = $SourceDDL.Alias + "M365"
$GroupDisplayName = $SourceDDL.DisplayName + " (Group)"

Creating the New Group and Team

With everything ready, we can go ahead and create the new Microsoft 365 Group, add the members, and team-enable the group. All the members can be added with a single Add-UnifiedGroupLinks command because we have an array of email addresses. Exchange processes each item in the array and adds it as a member.

# Create the new Microsoft 365 Group
Write-Host "Creating the new Microsoft 365 group..."
$Description = "Created from the " + $SourceDDL.DisplayName + " dynamic distribution list on " + (Get-Date -Format g)
$NewGroup = New-UnifiedGroup -DisplayName $GroupDisplayName –AccessType Private -Alias $AliasDDL -RequireSenderAuthenticationEnabled $True -Owner $SourceDDL.ManagedBy -AutoSubscribeNewMembers -Notes $Description
# Add the members to the group
Write-Host "Adding members from the dynamic distribution list to the Microsoft 365 group..."
Add-UnifiedGroupLinks -Identity $NewGroup.ExternalDirectoryObjectId -LinkType Members -Links $ValidMembers.PrimarySmtpAddress
Write-Host "Enabing Microsoft Teams for the Microsoft 365 group..."
New-Team -Group $NewGroup.ExternalDirectoryObjectId

The code doesn’t add a sensitivity label, so if you use these to apply container settings to groups and teams, you should add the label when creating the new group by passing the identifier for the selected label in the SensitivityLabel parameter.

The team created from a dynamic distribution list
Figure 1: The team created from a dynamic distribution list

That’s it. We have a new team built from the membership of a dynamic distribution list. The code is straightforward and works without a hitch, but if we throw dynamic membership for the Microsoft 365 group/team into the equation, things become much more complex. I’ll cover that subject in another post.


Learn about Teams, Exchange Online, and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s importance and how best to protect your tenant.

]]>
https://office365itpros.com/2022/03/15/convert-dynamic-distribution-list-teams/feed/ 0 53989
All About the Microsoft 365 Groups and Teams Activity Report https://office365itpros.com/2022/03/14/microsoft-365-groups-teams-activity-report/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-groups-teams-activity-report https://office365itpros.com/2022/03/14/microsoft-365-groups-teams-activity-report/#comments Mon, 14 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54023

An Ongoing PowerShell Project

Updated: January 27, 2023

The Microsoft 365 Groups and Teams Activity Report is a longstanding project of mine. I originally wrote the PowerShell script when Office 365 Groups were quite new and then refreshed it to deal with Microsoft Teams. The idea is to report statistics about the activity of groups such as:

  • Number of conversations in the group inbox (for Outlook groups).
  • Number of files in the group’s SharePoint site and the storage quota used.
  • Number of conversations in channels (for team-enabled groups).

With the data, you can see what groups or teams might be inactive and are candidates for archiving or removal.

The output is a report in HTML (Figure 1) and CSV formats. Administrators can slice and dice the data in the CSV file to present it whatever way they want. Some like to import the data into Power BI and visualize it there.

HTML version of the Microsoft 365 Groups and Teams activity report
Figure 1: HTML version of the Microsoft 365 Groups and Teams activity report

Note: If your group names include non-ASCII characters like é, use the Export-Excel cmdlet from the ImportExcel module to export the report file to Excel. Exporting to a CSV does not include the non-ASCII characters in group names.

Speeding the Script Up

The most recent enhancement discarded many of the calls to “expensive” PowerShell cmdlets like Get-UnifiedGroup and replaced them with Microsoft Graph queries. I did this to increase performance of the script and enable it to run in some large tenants with over 20,000 groups (teams). I’m sure that the script will process more than that number, but I haven’t gone higher. In any case, if you need to process very large numbers of groups, you should probably use a different tool and maybe even split processing up across batches of groups (for instance, A-C, D-E, and so on).

The latest version of the Graph-based script is 5.13. You can download the full script from GitHub. The latest updates include:

  • Better error handling.
  • Replaced call to Exchange Get-OrganizationConfig cmdlet with Graph API request.
  • Updated processing of groups with no owners. This aspect was further improved in 5.8.
  • Output more information about script processing.
  • Rewrote function to refresh access token for Graph access after 57 minutes. This is to accommodate long-running scripts, like one tenant which runs the report against 40K teams. In V5.9, I added a new function to check the access token and renew it if necessary after processing each group.
  • The script automatically downloads the latest Teams usage data from the Graph. This removes the need to manually download the data from the Teams admin center and means that the data used is always the latest available.
  • V5.10 addresses a problem where the date and items in folder data returned by the Get-MailboxFolderStatistics and Get-ExoMailboxFolderStatistics cmdlets are arrays!!
  • V5.13 handles the issue caused when the usage API cannot include the site URL for SharePoint sites in its output.

I’ll update this post when new versions appear.

Because it’s much slower, I don’t develop the pure PowerShell version anymore. The last version that I worked on is 4.8. The pure PowerShell script lags both the performance and functionality of its Graph counterpart, but you can download it from GitHub.

Teams Usage Report

Update: V5.5 and later versions remove the need to download the Teams usage report from the Teams admin center. The script now does this automatically.

If you’re going to run the report, you can speed things up even more by going to the Analytics & Reports section of the Teams admin center to download a CSV file with Teams usage data. If you don’t download the file, the script will still run. However, instead of being able to check usage data (like the number of channel posts) from the file, the script must check the number of compliance records stored in the team’s group mailbox.

Because checking compliance records uses a call to the Get-ExoMailboxFolderStatistics cmdlet instead of reading a record from a hash table, the operation is much more expensive in performance terms. On average, it takes an extra couple of seconds to process each team-enabled group, which quickly mounts up when the script must process hundreds or thousands of teams. As an example, to process 210 groups (83 teams), the script took 1034 seconds without a teams usage data file. With the file, the elapsed time for the same set reduced to 388 seconds.

On the upside, checking compliance records returns the count of every channel conversation post since the creation of a team (subject to any retention policies in force) whereas checking against the data file gives a snapshot of activity over the last 90 days. Knowing what happened over the last 90 days is usually sufficient to know if a team is active.

To generate the Teams usage data file, do the following:

  1. Go to the Usage Reports section under Analytics & Reports.
  2. Select the Teams usage report.
  3. Select 90 days as the period.
  4. Click Run report.
  5. When the report completes, select the Export to Excel option (Figure 2).
  6. When the CSV file is ready, download from the Downloads tab.
  7. Rename the downloaded file to match the file used by the script (by default, this is c:\temp\TeamsUsageData.csv. You can change the location and file name in the $TeamsDataCSVFile variable if you wish.

Generating a Teams usag
Figure 2: Generating a Teams usage data file

Teams Private and Shared Channels

If you provide the script with a teams usage data file, the data includes messages posted to private channels. It will soon include messages posted to shared channels. If you don’t use a data file, the script only includes messages posted to standard channels because it doesn’t check the mailboxes of private channel members or the special cloud mailboxes used by shared channels.

Use the Script as You Want

I don’t pretend this script is a work of PowerShell art. It could probably do with a complete rewrite. However, it works, and it’s something that tenants can use to create their own version of what they think an activity report should do. After all, it’s just PowerShell, so play with the code and let your imagination run riot!


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/03/14/microsoft-365-groups-teams-activity-report/feed/ 81 54023
How to Report Groups Under the Control of the Microsoft 365 Groups Expiration Policy https://office365itpros.com/2022/02/09/microsoft-groups-expiration-policy/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-groups-expiration-policy https://office365itpros.com/2022/02/09/microsoft-groups-expiration-policy/#comments Wed, 09 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53428

Yammer Communities Now Covered by Groups Expiration Policy

Updated 24 April 2023

A reader question about the Microsoft 365 Groups expiration policy caused me to review some PowerShell code I wrote to report the next renewal dates for the set of groups within the scope of the expiration policy. The question was related to Yammer (now Viva Engage), to know if the Microsoft 365 Group expiration policy covers the groups by Yammer and will remove inactive groups when necessary. The answer is yes; Microsoft updated policy processing last year to accommodate the Microsoft 365 groups used by Yammer communities when networks run in Microsoft 365 native mode. Microsoft confirmed coverage for Yammer communities by the groups expiration policy in MC324202 (published today). Microsoft 365 roadmap item 82186 also deals with the scenario and says that general availability occurred in January 2022.

In 2020, Microsoft changed the way the Microsoft 365 Groups expiration policy works to introduce automatic renewal. Instead of bothering group owners with email to remind them to renew the group, a background job looks for evidence that the group is active. If the evidence exists, Microsoft 365 renews the group automatically. Unfortunately, a limited set of signals govern renewal:

  • SharePoint Online: View, edit, download, move, share, or upload files.
  • Outlook: Join group, read/write message in the group mailbox, or like a message (in OWA).
  • Teams: Visit a Teams channel.
  • Yammer: View a post within a Yammer community or an interactive email in Outlook.

It’s debatable if a group is active if just one group member visits a Teams channel or views a post in a Yammer community. The Microsoft Graph gathers a wide array of signals about user activity and there’s surely a more precise method to determine group activity than the actions cited above. The Groups and Teams activity report is an example of how to code your own assessment of group activity.

In any case, the answer remains that you can add the Microsoft 365 groups used by Viva Engage communities to the Groups expiration policy through the Microsoft Entra admin center (Figure 1).

Figure 1: Defining groups to include in the expiration policy

You can also add groups to the policy with PowerShell.

The Groups expiration policy is appliable to selected groups or to all Microsoft 365 groups in the tenant. Users who are members of the groups covered by the policy must have Azure AD Premium P1 licenses.

PowerShell Code to Report Group Expiration

Writing code to report the expiration dates for groups isn’t difficult. The date when the group needs to be next renewed is in the ExpirationTime property. The only complication is to find when the group was last renewed. This data isn’t returned by the Get-UnifiedGroup cmdlet, so we need to use the Get-AzureADMSGroup cmdlet. Once we know where to get the dates, we can report what we find. This code runs after connecting to the Exchange Online and Azure AD PowerShell modules.

Write-Host "Finding Microsoft 365 Groups to check…"
[array]$ExpirationPolicyGroups  = (Get-UnifiedGroup -ResultSize Unlimited | ? {$_.ExpirationTime -ne $Null} | Select DisplayName, ExternalDirectoryObjectId, WhenCreated, ExpirationTime )
If (!($ExpirationPolicyGroups)) { Write-Host "No groups found subject to the expiration policy - exiting" ; break }
Write-Host $ExpirationPolicyGroups.Count “groups found. Now checking expiration status.”
$Report = [System.Collections.Generic.List[Object]]::new(); $Today = (Get-Date)
ForEach ($G in $ExpirationPolicyGroups) {
        $Days = (New-TimeSpan -Start $G.WhenCreated -End $Today).Days  # Age of group
        $LastRenewed = (Get-AzureADMSGroup -Id $G.ExternalDirectoryObjectId).RenewedDateTime
        $DaysLeft = (New-TimeSpan -Start $Today -End $G.ExpirationTime).Days
        $ReportLine = [PSCustomObject]@{
           Group       = $G.DisplayName
           Created     = Get-Date($G.WhenCreated) -format g
           AgeinDays   = $Days
           LastRenewed = Get-Date($LastRenewed) -format g
           NextRenewal = Get-Date($G.ExpirationTime) -format g
           DaysLeft    = $DaysLeft}
          $Report.Add($ReportLine)
} # End Foreach
CLS;Write-Host "Total Microsoft 365 Groups covered by expiration policy:" $ExpirationPolicyGroups.Count
Write-Host “”
$Report | Sort DaysLeft | Select Group, @{n="Last Renewed"; e= {$_.LastRenewed}}, @{n="Next Renewal Due"; e={$_.NextRenewal}}, @{n="Days before Expiration"; e={$_.DaysLeft}}

Total Microsoft 365 Groups covered by expiration policy: 74

Group                                Last Renewed     Next Renewal Due Days before Expiration
-----                                ------------     ---------------- ----------------------
Potholers (Team)                     02/02/2020 07:16 21/02/2022 07:16                     13
Office 365 Questions                 19/05/2017 11:12 14/03/2022 15:04                     34
Corona Virus News                    10/03/2020 21:56 30/03/2022 22:56                     51
Contract Workers                     12/03/2020 08:57 01/04/2022 09:57                     52
Plastic Production (Team)            25/03/2020 08:48 14/04/2022 09:48                     65

The code works, with two caveats:

  1. Get-UnifiedGroup is not a fast cmdlet. It’s OK to run it against a couple of hundred groups, but once that number grows, the time needed for the cmdlet to retrieve details of the groups to process gets longer and longer.
  2. The Get-AzureADMSGroup cmdlet is affected by Microsoft’s decision to retire the Azure AD module. Although the cmdlet will continue to run after June 30, 2023, you don’t know when it will cease functioning.

The solution for both speed and supportability is to use a Microsoft Graph API query to fetch group details.

Using the Graph to Fetch Microsoft 365 Groups to Report Expiration Details

Essentially, what we need to do is to replace the call to Get-UnifiedGroup with a Graph API query to return the set of groups in the tenant. The bonus is that the query returns the last renewed time, so there’s no need to use Get-AzureADMSGroup.

As with any script that calls Graph queries from PowerShell, you need a registered application in Azure AD to hold the permissions required to run the queries used by the script. In this case, we only need the Group.Read.All permission. After securing an access token, we can fetch the set of groups in the tenant using a lambda filter. The code shown below uses a function (Get-GraphData) to execute the Invoke-RestMethod cmdlet to fetch the data and page until all groups are retrieved. You can see the code for the Get-GraphData function in this script.

After fetching the set of groups, we create a report detailing the group name, its creation date, the date last renewed, and expiration date. The code used to process the data returned by Get-UnifiedGroup is modified to deal with the property names returned by the Graph query.

$uri = "https://graph.microsoft.com/beta/groups?`$filter=ExpirationDateTime ge 2014-01-01T00:00:00Z AND groupTypes/any(a:a eq 'unified')&`$count=true" 
[array]$Groups = Get-GraphData -AccessToken $Token -Uri $uri
If (!($Groups)) { Write-Host "No groups found subject to the expiration policy - exiting" ; break }
$Report = [System.Collections.Generic.List[Object]]::new(); $Today = (Get-Date)
ForEach ($G in $Groups) {
        $Days = (New-TimeSpan -Start $G.CreatedDateTime -End $Today).Days  # Age of group
        #$LastRenewed = $G.RenewedDateTime
        #$NextRenewalDue = $G.ExpirationDateTime
        $DaysLeft = (New-TimeSpan -Start $Today -End $G.ExpirationDateTime).Days
        $GroupsInPolicy++
        $ReportLine = [PSCustomObject]@{
           Group                   = $G.DisplayName
           Created                 = Get-Date($G.CreatedDateTime) -format g
          "Age in days"            = $Days
          "Last renewed"           = Get-Date($G.RenewedDateTime) -format g
          "Next renewal"           = Get-Date($G.ExpirationDateTime) -format g
          "Days before expiration" = $DaysLeft}
          $Report.Add($ReportLine)
} # End ForeachCLS;Write-Host "Total Microsoft 365 Groups covered by expiration policy:" $Groups.Count
Write-Host “”
$Report | Sort "Days before expiration"| Select Group, "Last renewed", "Next renewal", "Days before expiration" | Out-GridView

As you’d expect, things run much faster. Retrieving data through a Graph query is always quicker than using a PowerShell cmdlet and eliminating the call to Get-AzureADMSGroup for each group helps speed things up even further. Figure 2 shows the output.

Expiration dates for Microsoft 365 Groups

Microsoft 365 Groups Expiration policy
Figure 2: Expiration dates for Microsoft 365 Groups

An even easier solution is to replace the calls to Get-UnifiedGroup and Get-AzureADMSGroup with the Get-MgGroup cmdlet from the Microsoft Graph PowerShell SDK. Here’s the code, which is almost as fast as using the Graph API:

Write-Host "Finding Microsoft 365 Groups to check…"
[array]$ExpirationPolicyGroups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All | ? {$_.ExpirationDateTime -ne $Null }
If (!($ExpirationPolicyGroups)) { Write-Host "No groups found subject to the expiration policy - exiting" ; break }
Write-Host $ExpirationPolicyGroups.Count “groups found. Now checking expiration status.”
$Report = [System.Collections.Generic.List[Object]]::new(); $Today = (Get-Date)
ForEach ($G in $ExpirationPolicyGroups) {
        $Days = (New-TimeSpan -Start $G.CreatedDateTime -End $Today).Days  # Age of group
        $DaysLeft = (New-TimeSpan -Start $Today -End $G.ExpirationDateTime).Days
        $ReportLine = [PSCustomObject]@{
           Group       = $G.DisplayName
           Created     = Get-Date($G.CreatedDateTime) -format g
           AgeinDays   = $Days
           LastRenewed = Get-Date($G.RenewedDateTime) -format g
           NextRenewal = Get-Date($G.ExpirationDateTime) -format g
           DaysLeft    = $DaysLeft}
          $Report.Add($ReportLine)
} # End Foreach
CLS;Write-Host "Total Microsoft 365 Groups covered by expiration policy:" $ExpirationPolicyGroups.Count
Write-Host “”
$Report | Sort DaysLeft | Select Group, @{n="Last Renewed"; e= {$_.LastRenewed}}, @{n="Next Renewal Due"; e={$_.NextRenewal}}, @{n="Days before Expiration"; e={$_.DaysLeft}}

Speeding Up Queries

The question about Yammer communities forced me to look at code and find an instance where I needed to replace a cmdlet before its deprecation next June. At the same time, I managed to speed up the code by introducing a Graph query. Things worked out for the best, but it does illustrate the need to check and update old scripts on an ongoing basis.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/02/09/microsoft-groups-expiration-policy/feed/ 10 53428
New Way Available to Fetch a List of Microsoft Teams with the Microsoft Graph https://office365itpros.com/2022/01/13/list-teams-api-graph/?utm_source=rss&utm_medium=rss&utm_campaign=list-teams-api-graph https://office365itpros.com/2022/01/13/list-teams-api-graph/#respond Thu, 13 Jan 2022 01:00:00 +0000 https://office365itpros.com/?p=52958

Finding Teams in Groups

The fastest way to fetch the list of Teams in a Microsoft 365 tenant programmatically is to use the Graph API. PowerShell is fast enough in small tenants, but once there’s more than a couple of hundred teams (groups) to process, the Graph is usually a better choice. Unless you’ve got time to wait, of course.

I cover the topic in an article explaining how to fetch a list of Teams using the Groups endpoint. Because the Groups API returns all types of groups, you apply a filter to find the set of Microsoft 365 Groups which are team-enabled. For PowerShell, the commands needed to execute the Graph API call are:

$Uri = “https://graph.microsoft.com/V1.0/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')”
[array]$Teams = Invoke-WebRequest -Method GET -Uri $Uri -ContentType "application/json" -Headers $Headers | ConvertFrom-Json

The filter used with the query is a Lambda operator. In this case, it requests the Graph to return any Groups it finds where the provisioning option is set to “Team.” Using a filter to find Teams is a well-known technique exploited by developers: we use it in the Graph-based version of the Teams and Groups Activity Report script.

New API to Make it Easier to List Teams

In December, Microsoft published a new List Teams API in the beta version of the Graph. The API access the Teams endpoint `to fetch a list of teams without using a filter and works with both delegated and application permissions. Apps need consent for one of the Team.ReadBasic.All, TeamSettings.Read.All, TeamSettings.ReadWrite.All permissions to use the API. You can apply filters to the List Teams API to find specific teams if you don’t want the full set. The equivalent code to fetch all the teams in the tenant is:

$Uri = "https://graph.microsoft.com/beta/teams"
[array]$Teams = Invoke-WebRequest -Method GET -Uri $Uri -ContentType "application/json" -Headers $Headers | ConvertFrom-Json

Remember that the Graph uses paging to return data, so code needs to be prepared to process multiple pages of data to acquire the full set of teams.

In both cases, the set of Teams returned by the query is in an array called Value. In other words, to see the details of individual teams, you access the array using $Teams.Value. For example, the first team is available using $Teams.Value[0], the second with $Teams.Value[1], and so on. The count of teams returned in the array is available with $Teams.Value.Count. In the case of the Teams List API, it’s also available as $Teams.’@odata.count’.

Beta Queries

The List Teams API currently returns just three properties for a team. Here’s what you get for a team:

$Teams[0]

id                          : 109ae7e9-1f94-48d1-9972-64abab87b89a
createdDateTime             :
displayName                 : French Customers
description                 : Delivering great service to French customers
internalId                  :
classification              :
specialization              :
visibility                  :
webUrl                      :
isArchived                  :
isMembershipLimitedToOwners :
memberSettings              :
guestSettings               :
messagingSettings           :
funSettings                 :
discoverySettings           :

To get the other team properties (for instance, the archive status for a team), you must query the properties of an individual team. This isn’t difficult, and the same downside exists if you use the Groups endpoint to fetch a list of Teams. That endpoint returns many properties for a group, but not the teams properties.

The Graph Explorer is a good way to try out the new API. Make sure that you sign into your tenant and have consent to use at least the Team.ReadBasic.All permission. Then input a query and see what happens. Figure 1 shows the result of running the query: https://graph.microsoft.com/beta/teams?$filter=startswith(displayName, ‘Office 365’) to return the set of teams whose display name starts with Office 365. As noted above, along with the properties of the individual teams, the query also returns the count in @odata.count.

Using the Graph Explorer with the List Teams API
Figure 1: Using the Graph Explorer with the List Teams API

No Need to Update Now

If your programs or scripts often need to retrieve a list of teams for processing, you should keep an eye on the List Teams API to track its development. For now, there’s no need to update existing code. In time, it might be the case that the new API delivers better performance than going through the Groups endpoint or that the query returns all team properties without having to retrieve individual teams. Better performance and functionality are always welcome. Let’s see what happens as the new API makes its way from beta to production.


Learn how to exploit the Office 365 data available to tenant administrators through PowerShell and the Graph with the help of the many examples in the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/01/13/list-teams-api-graph/feed/ 0 52958
How to Enable Users to Receive Copies of Email They Send to Microsoft 365 Groups https://office365itpros.com/2021/12/14/outlook-groups-receive-copies/?utm_source=rss&utm_medium=rss&utm_campaign=outlook-groups-receive-copies https://office365itpros.com/2021/12/14/outlook-groups-receive-copies/#comments Tue, 14 Dec 2021 01:00:00 +0000 https://office365itpros.com/?p=52704

Outlook Groups Still Popular

When Microsoft launched Office 365 Groups in November 2014, the plan was to use these objects to replace distribution groups. Like all plans, events took over and forced change. Distribution groups remain intact (and Microsoft is updating their functionality) and the role of Office 365 Groups (renamed Microsoft 365 Groups in April 2020) has evolved to become a membership and identity service for apps like Teams, Power BI, and Yammer. However, devotees of Outlook can still use Microsoft 365 Groups in Outlook and OWA (where they have the moniker “Outlook Groups”), complete with a dedicated menu bar, presence in Outlook favorites, and section in Outlook resources. Outlook mobile also supports Outlook Groups.

An Outlook group has the following characteristics:

Like other Microsoft 365 Groups, an Outlook group has a SharePoint Online team site, shared OneNote notebook, and can connect to Planner.

Subscribers and Copies

An Outlook groups distribute copies of messages sent to the group based on its subscriber list. The AutoSubscribeNewMembers group setting controls if Exchange adds new group members to the subscriber list automatically. Another setting called AlwaysSubscribeMembersToCalendarEvents controls if group members receive copies of calendar events (sometimes it’s necessary to update the groups used by Teams to change these settings).

Users can control if they want to be on a group’s subscriber list with the Follow in Inbox feature. Figure 1 shows the Outlook desktop settings; OWA presents the settings differently, but the same effect applies. In this instance, I am not a subscriber to the group because I don’t receive all email and events. However, I do receive copies of any replies posted to messages I send to the group.

Group settings in Outlook for Windows
Figure 1: Group settings in Outlook for Windows

Senders and Copies

In 2017, Microsoft changed the way Groups processed email when it delivered copies to subscribers to stop delivering a copy to the message sender for new messages and replies. On the surface, the change was reasonable because senders have copies of messages in their Sent Items folder and senders can always add themselves as a CC or BCC recipient if they want Exchange to deliver a copy of a specific message to their inbox. Well-intentioned as the change was, it upset many people who liked receiving a copy of anything they posted to groups.

InA few months ago, Microsoft introduced a way to allow users to receive copies of their messages posted to Outlook groups. The requirements are:

  • The user or an Exchange administrator must update their mailbox settings to set EchoGroupMessageBackToSubscribedSender to True. As the name implies, the setting controls if the Exchange transport service echoes messages sent to a group by a subscribed sender. By default, this setting is False.
  • The user must subscribe to each group for which they wish to receive copies of their posted messages.

I can’t find any message center notification covering the use of the EchoGroupMessageBackToSubscribedSender setting (it’s entirely possible that I missed it). However, from the “common tasks to manage Microsoft 365 groups” page, I can’t find any other Microsoft reference to the cmdlet except in an October 29 change in GitHub to a page covering Hybrid deployment. Other references exist elsewhere, such as this August 31 Stack Overflow discussion. It’s curious that Microsoft doesn’t document this capability more thoroughly.

User Updates

User can update their mailbox settings through OWA options. Go to the Groups section and set the Send me a copy of email I send to a group option (Figure 2).

OWA setting controlling if users receive copies of messages they post to Outlook groups
Figure 2: OWA setting controlling if users receive copies of messages they post to Outlook groups

Administrators can do the same by running the Set-MailboxMessageConfiguration cmdlet.

Set-MailboxMessageConfiguration -Identity Kim.Akers -EchoGroupMessageBackToSubscribedSender $True

In either case, the setting covers all Outlook groups and can take up to an hour before the change is effective and Exchange will deliver a copy of any message posted by the user to the groups they subscribe to.

Users can choose if they want to subscribe to group and now have control over if they receive copies of their own messages. I am loathe to recommend that administrators should step in to update subscriber settings on a group-wide basis because it means that you would overwrite the settings for people who have opted out of receiving copies of group email and events. However, a need might arise to make everyone a subscriber and update their mailbox settings to allow them to receive copies of their own messages. If so, this PowerShell works.

# Update group subscriber list with all members and set their mailboxes to receive copies of messages posted to Outlook groups
$Group = Read-Host "What group do you want to update?"
Write-Host "Checking" $Group "..."
$GroupId = (Get-UnifiedGroup -Identity $Group -ErrorAction SilentlyContinue).ExternalDirectoryObjectId
If (!($GroupId)) { Write-Host "Sorry... we can't find" $Group "... exiting" ; break}
[array]$GroupMembers = Get-UnifiedGroupLinks -Identity $GroupId -LinkType Member
Write-Host "Adding group members as subscribers to" $Group
Add-UnifiedGroupLinks -Identity $GroupId -LinkType Subscriber -Links $GroupMembers.PrimarySmtpAddress
Write-Host "Updating mailbox settings for group members to allow them to receive copies of their messages posted to the group"
$GroupMembers | ForEach-Object { Set-MailboxMessageConfiguration -Identity $_.ExternalDirectoryObjectId -EchoGroupMessageBackToSubscribedSender $True }

One interesting thing about this code is that you do not need to call Add-UnifiedGroupLinks to add each group member to the subscriber list. If you have an array of members, which we do after calling Get-UnifiedGroupLinks to fetch the member list, you can use that as the input and Add-UnifiedGroupLinks will multiple members with one call.

More Updates for Groups

In closing, let me note two other changes coming soon to Outlook Groups. First, MC302487 (December 8) says that users assigned the Send As permission for a group will no longer have to select the group name when sending messages from the group. This change applies to OWA only and roll-out begins in mid-January. Second, MC303512 (December 10) brings equally important news that instead of displaying a folder icon for an Outlook group, OWA will use the Groups icon from mid-December (this change hasn’t turned up in my targeted release tenant yet). I’m sure the new icon will make all the difference.


Learn more about how Office 365 really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2021/12/14/outlook-groups-receive-copies/feed/ 1 52704
Synchronizing Sensitivity Labels to Update SharePoint Online Sites https://office365itpros.com/2021/11/11/update-sharepoint-online-sites-sensitivity-labels/?utm_source=rss&utm_medium=rss&utm_campaign=update-sharepoint-online-sites-sensitivity-labels https://office365itpros.com/2021/11/11/update-sharepoint-online-sites-sensitivity-labels/#respond Thu, 11 Nov 2021 01:00:00 +0000 https://office365itpros.com/?p=52327

Investigating Unlabeled SharePoint Sites

Microsoft is fond of equipping its administrative consoles with cards containing insights which administrators might action. Yesterday, I noticed that the SharePoint Online admin center highlighted that my tenant had many sites had no sensitivity label (Figure 1).

Unlabeled sites reported by the SharePoint Online admin center
Figure 1: Unlabeled sites reported by the SharePoint Online admin center

As you might recall, Microsoft 365 uses sensitivity labels to apply settings to “containers” (teams, groups, and sites). Controlling the external sharing capability of SharePoint Online sites is a good example of the power of this approach. By default, I assign sensitivity labels to when creating new Microsoft 365 groups and teams, so it surprised me to discover the unlabeled state of so many sites.

Explaining Unlabeled Sites

Using the Manage unlabeled sites link, I examined the sites. Because I use sensitivity labels for the sites used for groups and teams, I expected to find that some sites in the tenant had no labels. These include:

  • Hub sites.
  • Communication sites.
  • System sites (such as the one used to manage Viva Topics).

Knowing that teams created using templates didn’t ask team owners to assign a sensitivity label until Microsoft fixed the problem in October 2021 (MC281936, Microsoft 365 roadmap item 84232), I could account for some other unlabeled sites. However, stripping all the explainable sites from the 126 noted by SharePoint still left a bunch that I couldn’t explain except by concluding that at some points in the past, the synchronization of sensitivity labels didn’t work as well as it should between SharePoint Online and the other workloads. This is an important thing to fix because if SharePoint Online doesn’t know about a sensitivity label assigned to a site, it can’t apply the management controls defined in that label.

For the record, the synchronization of sensitivity labels for new groups works well. This might be the vestige of a long-solved problem.

Fixing Up Site Sensitivity Labels

To address the problem, I decided to write some PowerShell. The first stage was to find all the sites created for teams and Microsoft 365 Groups that didn’t have a label. To do this, the code:

  • Runs the Get-SPOSite cmdlet to find all sites created using the team site template.
  • Run Get-SPOSite against each site to find sites without a sensitivity label. You need to access each site to find if it has a label because Get-SPOSite doesn’t return this property when run against multiple sites.
  • Store the unlabeled sites in a list.

Here’s the code I used:

[array]$Sites = Get-SPOSite -Limit All -Template Group#0
If (!($Sites)) { Write-Error "No sites for Microsoft 365 Groups found... exiting!" ; break}
   Else { Write-Host ("Processing {0} sites" -f $Sites.Count) }

$SitesNoLabels = [System.Collections.Generic.List[Object]]::new()
ForEach ($Site in $Sites) { #Check each site to see if it has a sensitivity label
        $SiteData = Get-SPOSite -Identity $Site.Url
        If ([string]::IsNullOrWhiteSpace(($SiteData.SensitivityLabel)) -eq $True) {
           Write-Host ("Site {0} has no label" -f $SiteData.Url) 
           $SiteInfo = [PSCustomObject][Ordered]@{  
              URL    = $SiteData.Url
              Title   = $SiteData.Title   }
           $SitesNoLabels.Add($SiteInfo) }
} #End ForEach Sites

The properties of a Microsoft 365 group store the GUID of the sensitivity label, if one is assigned to the group/team. The next step is to retrieve the sensitivity label information for all groups. It’s possible to match a group with a site because the group properties include the site URL. I therefore:

  • Used the Get-UnifiedGroup cmdlet to find all Microsoft 365 Groups. This won’t be a fast operation in large tenants, but it’s acceptable because this is a one-time operation. In the largest tenants, consider replacing the Get-UnifiedGroup cmdlet with the Groups Graph API (see the call to fetch all Microsoft 365 groups in a tenant described in this article).
  • Removed any group that didn’t have a SharePoint site URL in its properties (sometimes an error in the provisioning process leaves this property blank. Microsoft 365 will eventually synchronize the site URL from SharePoint Online to Exchange Online).
  • Store the site URL and sensitivity label GUID in a hash table. A list would also do, but it’s much faster to lookup against a hash table.

Here’s the code for this segment:

Write-Host "Retrieving sensitivity label information for Microsoft 365 Groups"
[array]$Groups = Get-UnifiedGroup -ResultSize Unlimited 
$Groups = $Groups | ? {$_.SharePointSiteUrl -ne $Null}
$GroupsTable = @{}
$Groups.ForEach( {
       $GroupsTable.Add([String]$_.SharePointSiteUrl, $_.SensitivityLabel) } )

We now have a list of sites without labels and a table with the labels assigned to the underlying groups. The next step is to check each site against the groups table to see if we can find what label the site should have. If we find a match, we can update the site. The next code segment does the following:

  • Loop to check each unlabeled site.
  • Use the site URL as a lookup against the groups table.
  • If the site URL matches, use the label GUID to update the site with the Set-SPOSite cmdlet.

This code applies sensitivity labels to sites using the information from Microsoft 365 Groups:

[int]$Updates = 0; [int]$NoUpdates = 0
ForEach ($Site in $SitesNoLabels) {
    $Label = $Null
    $Label = $GroupsTable.Item($Site.Url)
    If ($Label) { # Update the site with the label we find
       Write-Host ("Updating site {0} with label {1}" -f $Site.Url, $Label.Guid) 
       Set-SPOSite -Identity $Site.Url -SensitivityLabel $Label.Guid 
       $Updates++ }
    Else {
       Write-Host ("Can't find sensitivity label for site {0} - group might be deleted" -f $Site.Url)
       $NoUpdates++ }
} #End ForEach Sites

The complete script is available from GitHub.

A Better Card

Of the 126 unlabeled sites reported by SharePoint Online, 116 were team sites. The technique described above managed to apply sensitivity labels to 103 sites. The remaining 13 are deleted sites kept by SharePoint Online because of a retention policy (the associated Microsoft 365 group is gone). The card displayed in the SharePoint Online admin center looks better (Figure 2) and all the sites belonging to Microsoft 365 groups and teams have their correct labels. All is well.

The unlabeled sites card tells a much happier story
Figure 2: The unlabeled sites card tells a much happier story

Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2021/11/11/update-sharepoint-online-sites-sensitivity-labels/feed/ 0 52327
Lightweight Plans Coming in Fluid Component for Teams Meetings https://office365itpros.com/2021/08/31/lightweight-plans-fluid-component-teams-meetings/?utm_source=rss&utm_medium=rss&utm_campaign=lightweight-plans-fluid-component-teams-meetings https://office365itpros.com/2021/08/31/lightweight-plans-fluid-component-teams-meetings/#comments Tue, 31 Aug 2021 01:00:00 +0000 https://office365itpros.com/?p=51301

Rosters are Lightweight Plans

Six months after their original attempt, Microsoft seems ready to relaunch rosters or lightweight plans (MC279089, August 18) by enabling the feature “on Graph for all tenants starting in mid-September and expect to complete by mid-October.” The mention of the Graph is a little confusing because of the use of PowerShell to disable or enable rosters (see below). It means that Microsoft will enable lightweight plans by lighting up the necessary Graph API in Office 365 tenants.

Lightweight plans are Planner plans without Microsoft 365 Groups. Originally, Planner had a 1:1 relationship with Groups and each plan had an associated group. Teams then broke the 1:1 connection by supporting multiple plans per team (group). Lightweight plans have their own list of members (the roster). Members have Azure AD accounts in the tenant. The plans themselves do not exist as Azure AD objects. Instead, Planner manages lightweight plans like other (group-enabled) plans using the same Planner browser interface (references to resources available to group-enabled plans are suppressed). Planner deletes lightweight plans automatically upon the removal of the last member.

Using Fluid-Based Lightweight Plans in Teams Meetings

Microsoft says that the only way to create lightweight plans is through the Graph API for Planner, which Microsoft is extending to deal with roster containers. This brings me neatly to how Microsoft will use lightweight plans. In MC279089, a reference is made to New hybrid work innovations (June 2021), where Microsoft CVP Jared Spataro discussed new features coming to Microsoft 365, including the Fluid Framework.

We know that Teams will soon support fluid components in chat. The natural connection between lightweight plans and Teams is in meetings, specifically to allowing meeting organizers to add a fluid component to track tasks assigned during meetings (the meeting participants form the plan roster).

Fluid components already include a task list, but the tasks captured in this component exist only in the fluid file created in the originator’s OneDrive for Business account. The advantage of linking a fluid component to a Planner lightweight plan is that the tasks captured in the lightweight plan can synchronize with the rest of the Microsoft 365 task ecosystem and be available in apps like To Do or, perhaps more importantly, the Teams Tasks app. Synchronization should mean that the tasks assigned during meetings show up under the Assigned to Me list within My Tasks in the Tasks app. We’ll have to see how the implementation works in Teams. I imagine that the same component will show up in Outlook meetings later (OWA first and later Outlook desktop using the Edge WebView2 component).

Planner’s Ongoing Poor PowerShell Support

In my March post, I commented about the poor support of PowerShell by Planner. Things haven’t improved very much since. Why Planner goes through the current rigmarole instead of supporting the distribution of the PowerShell module via the PowerShell Gallery is beyond me.

If you want to disable rosters (or turn them back on later), you must:

  • Download a Zip file containing a PowerShell module file (psm1) from Microsoft.
  • Unzip the file to somewhere suitable and block the script module and DLL files.
  • Adjust the execution policy for the workstation to allow execution of the downloaded files.
  • Import the module file (remember to include the full location of the psm1) into a PowerShell session.
  • Check the Planner configuration with the Get-PlannerConfiguration cmdlet.
  • Adjust the roster setting with the Set-PlannerConfiguration cmdlet.

For instance, here’s what I did to load the files, examine the configuration, and update the configuration to disable the creation of rosters.

Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process

Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): y

Import-Module "c:\temp\plannertenantadmin\plannertenantadmin.psm1"

Get-PlannerConfiguration
AllowRosterCreation AllowTenantMoveWithDataLoss AllowPlannerMobilePushNotifications AllowCalendarSharing
------------------- --------------------------- ----------------------------------- --------------------
               True                       False                                True                 True

Set-PlannerConfiguration -AllowRosterCreation $False

Exporting Planner User Data

The Planner script module contains the Export-PlannerUserContent cmdlet, which administrators can use to export Planner data for a selected user. For example, to export my Planner data, I ran:

Export-PlannerUserContent -UserAadIdOrPrincipalName Tony.Redmond@office365itpros.com -ExportDirectory C:\temp\plannertenantadmin

The cmdlet took 35 seconds to export 13.7 MB of Planner data from 51 plans. The data for each plan is in a separate JSON-format file (Figure 1) containing details of the tasks and other information used by Planner.

Figure 1: Planner data exported for a user

MVP Alex Holmeset has written about using the Planner Graph API to extract data for users and import the data into accounts in a new tenant. Maybe the existence of this capability will make moving Planner data around just a tad easier, even if there’s no equivalent import cmdlet (yet) to process the JSON files.

Moving Forward

Lightweight plans open new possibilities for any app which needs to capture and manage tasks for a small set of users. I’ve been a happy user of Planner for years and use the app to manage the development and progress of the Office 365 for IT Pros eBook. The Tasks app in Teams delivers a nice overhead of personal (To Do/Outlook) and organizational (Planner) tasks. Adding tasks assigned in meetings managed through lightweight plans should be a useful extension. We’ll see when the functionality turns up in apps.

]]>
https://office365itpros.com/2021/08/31/lightweight-plans-fluid-component-teams-meetings/feed/ 9 51301
Inconsistencies Using Reserved Aliases with Groups in Microsoft 365 https://office365itpros.com/2021/08/30/reserved-aliases/?utm_source=rss&utm_medium=rss&utm_campaign=reserved-aliases https://office365itpros.com/2021/08/30/reserved-aliases/#comments Mon, 30 Aug 2021 01:00:00 +0000 https://office365itpros.com/?p=51279

Current Implementation Blocks Some but Not All Use of Reserved Aliases with Groups

SMTP email addresses are composed of an alias (otherwise called a mail nickname) and a domain. The alias is assigned to a mailbox or other mail-enabled object to allow it to receive email. User clients tend to generate aliases automatically when creating new groups. Administrative interfaces like the Microsoft 365 admin center or PowerShell allow more control over the alias given to new mail-enabled objects. And then we come to the question of reserved email aliases.

A reserved alias is a sensitive name that’s usually kept for specific purposes. Azure AD defines a set of reserved or highly-privileged aliases which it doesn’t allow for (some) mail-enabled groups. The purposes that these aliases often serve including being the contact address for an email system or web site. Obviously, you don’t want a common-or-garden group to hijack an email address which people might assume is used for a different purpose.

It is not unusual for email systems to ring-fence reserved aliases. Google Workspace does the same, explaining that “The following words can’t be used in the email addresses of groups that you create in groups.google.com:

  • abuse
  • admin
  • administrator
  • hostmaster
  • majordomo
  • postmaster
  • root
  • ssl-admin
  • webmaster

Azure AD adds “secure” and “security” to the list.

Testing the Creation of Groups with Reserved Aliases

The Azure AD documentation says that an Azure AD global administrator can create groups with reserved aliases. This isn’t altogether true as it depends on the administrative interface used, which points to an inconsistency of implementation across Microsoft 365. Table 1 shows the results of some tests I did to see if I could create groups with reserved aliases.

Admin endpointGroup TypeCreation with reserved alias possible?
Microsoft 365 admin centerMail-enabled security groupNo
 Security groupYes
 Distribution listNo
 Microsoft 365 groupYes
Exchange admin centerDistribution listNo
 Mail-enabled security groupNo
 Dynamic distribution listYes
Azure AD admin centerMicrosoft 365 groupYes
PowerShell
New-AzureADGroup
Security groupYes
New-UnifiedGroupMicrosoft 365 groupYes
New-DistributionGroupDistribution listNo
Table 1: Tracking the ability to create groups with reserved aliases

I am a global tenant administrator, so finding five administrative endpoints where it’s possible to create a mail-enabled group with a reserved alias confirms that the documentation’s assertion that global administrators can create these groups is correct. A more exhaustive test might find more, especially in PowerShell cmdlets.

However, the problem is the five places where a global administrator couldn’t create groups with reserved aliases. Three of these are distribution lists, the others are mail-enabled security groups. That’s where the inconsistency exists. Microsoft’s documentation mentions “groups,” a term which covers a spectrum of different types of group objects and doesn’t focus on any specific kind of group. This raises the question of why are distribution lists and mail-enabled security groups treated differently?

Testing is simple. Select an administrative interface and see if you can create a group with a reserved alias (Figure 1).

Creating a new group with a reserved alias in the Microsoft 365 admin center

Reserved email alias
Figure 1: Creating a new group with a reserved alias in the Microsoft 365 admin center

When the Microsoft 365 admin center or Exchange Online admin center detect a problem creating a group with a reserved alias, it flags the error. The error text is by no means perfect. It starts off by pointing to a synchronization issue between Azure AD and Exchange Online before saying that the value for the alias is incorrect as it contains a blocked word.

Error when creating a new group with a reserved alias
Figure 2: Error when creating a new group with a reserved alias

Pointing to synchronization between Azure AD and Exchange Online is misleading. The two workloads use a dual-write process to make sure that the creation or update of a mail-enabled object occurs in both directories or not at all. Microsoft introduced the double-write some years ago to avoid synchronization issues between Azure AD (the directory of record for Microsoft 365) and Exchange Online (which has its own directory for mail-enabled objects). Reading the text, I assume that Exchange Online rejected the attempt to create the new group because of the reserved alias and Azure AD then declined the write.

PowerShell Inconsistencies Too

Other administrative interfaces give different errors. For instance, here we create a new security group with Azure AD PowerShell. Azure AD accepts the reserved alias because this group is not mail-enabled. If we try to mail-enable the group, we get an error.

New-AzureADGroup -Description "Abuse Group" -DisplayName "Abuse Group" -MailNickName Abuse -MailEnabled $False -SecurityEnabled $True

ObjectId                             DisplayName Description
--------                             ----------- -----------
d347eec5-62f1-4436-af41-e53fa18090be Abuse Group Abuse Group

Set-AzureADGroup -ObjectId d347eec5-62f1-4436-af41-e53fa18090be -MailEnabled $True
Set-AzureADGroup : Error occurred while executing SetGroup
Code: Request_BadRequest
Message: The service does not currently support writes of mail-enabled groups. Please ensure that the mail-enablement
property is unset and the security-enablement property is set.     

The Exchange Online cmdlets to work with Microsoft 365 groups are happy to accept reserved aliases:

New-UnifiedGroup -DisplayName "Majordomo Group" -Alias Majordomo -Members Tony.Redmond@office365itpros.com -Owner Tony.Redmond@office365itpros.com
Set-UnifiedGroup -Identity Majordomo -PrimarySmtpAddress Majordomo@office365itpros.com
Get-UnifiedGroup -Identity Majordomo | fl EmailAddresses

EmailAddresses : {SPO:SPO_720c5dd5-e387-4c93-836d-899466759f64@SPO_b662313f-14fc-43a2-9a7a-d2e27f4f3478, smtp:majordomo@office365itpros.onmicrosoft.com, SMTP:majordomo@office365itpros.com}

But the Exchange Online cmdlets for distribution lists are not so content to assign reserved aliases:

New-DistributionGroup -DisplayName "Secure" -Alias "Secure" -PrimarySmtpAddress Secure@Office365itpros.com -Name "Secure Group"
An Azure Active Directory call was made to keep object in sync between Azure Active Directory and Exchange Online.
However, it failed. Detailed error message:
        The value specified for property Alias is incorrect. Reason: ContainsBlockedWord RequestId :
514d14b4-cc5f-4581-b97a-9930dff98542
The issue may be transient and please retry a couple of minutes later. If issue persists, please see exception members
for more information.
    + CategoryInfo          : NotSpecified: (:) [New-DistributionGroup], UnableToWriteToAadException
    + FullyQualifiedErrorId : [Server=DB9PR04MB8445,RequestId=9f8a149c-dc90-4196-9274-7625148d6280,TimeStamp=25/08/202
   1 12:05:53] [FailureCategory=Cmdlet-UnableToWriteToAadException] AEC31773,Microsoft.Exchange.Management.RecipientTasks.NewDistributionGroup
    + PSComputerName        : outlook.office365.com

Moving along, we can assign a reserved alias to a dynamic distribution list.

Get-DynamicDistributionGroup "Secure DDL" | fl primarysmtpaddress

PrimarySmtpAddress : Secure@office365itpros.com

The Need for Consistency

One way of looking at this is to say that so many ways exist to create new mail-enabled groups within Microsoft 365 that it’s inevitable that some inconsistencies will creep in. However, the current situation shows all the signs of poor attention to detail. Global administrators usually know what they’re doing when they create groups. Users can create distribution lists and Microsoft 365 groups/teams (if allowed by policy), so user-driven creation is where an absolute block should exist on reserved aliases.

It would be nice if Microsoft either lived up to its assertion that global administrators aren’t subject to the block on using reserved aliases or documented exactly where they can and cannot create groups with reserved aliases. Knowing what to expect and where to do it is so much better than probing holes in documentation.


Learn how to exploit the Office 365 data available to tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2021/08/30/reserved-aliases/feed/ 1 51279
How to Control the Creation of Microsoft 365 Groups (and Teams) in a Tenant https://office365itpros.com/2021/08/11/creation-microsoft-365-groups/?utm_source=rss&utm_medium=rss&utm_campaign=creation-microsoft-365-groups https://office365itpros.com/2021/08/11/creation-microsoft-365-groups/#comments Wed, 11 Aug 2021 02:03:00 +0000 https://office365itpros.com/?p=51014

Azure AD – then Microsoft 365 – And Maybe OWA

My recent note about the changes Microsoft made to the Azure AD admin center to control the creation of groups created some additional questions about the overall governance of group creation, specifically for Microsoft 365 Groups. It’s a topic that Microsoft has been accused of over-complicating in the past, but it seems reasonably straightforward now.

The Central Role of Azure AD

Azure AD exerts governance over Microsoft 365 Groups at tenant level. This is easy to understand because Microsoft 365 Groups are a form of Azure AD groups. Every Microsoft 365 group is an Azure AD group, and the group membership and ownership are in Azure AD. Because Microsoft 365 Groups are mail-enabled objects, Exchange Online stores some additional properties for the groups (like proxy addresses, membership counts, and SharePoint Online URLs). However, Azure AD is the directory of record and manages the foundational elements of a group.

In late 2019, Microsoft introduced a dual write mechanism to ensure that any changes made by clients to a Microsoft 365 group must update both the Exchange Online Directory and Azure AD to succeed. This change avoids the synchronization glitches which sometimes interfered with object consistency across the two directories.

Configuring Azure AD for Group Creation

Because Azure AD “owns” groups at the tenant level, it therefore follows that the Azure AD control for group creation (Figure 1) must be switched on before anyone can create Microsoft 365 Groups through any app or administrative interface.

Turning on Microsoft 365 Groups in the Azure AD admin center
Figure 1: Turning on Groups in the Azure AD admin center

The Azure AD control lets users create Microsoft 365 Groups using the Azure portal, API, or PowerShell. By API, it means the Microsoft Graph Groups API, which is how the Microsoft 365 admin center, Teams admin center, and new Exchange admin center create groups. It also covers creation in group-enabled apps like Teams, Planner, and Outlook. PowerShell covers creation using cmdlets like New-UnifiedGroup and New-Team.

Controlling Group Creation by Apps

The next level down is to control group creation by apps. Microsoft 365 uses an Azure AD directory setting policy for this purpose. If the policy doesn’t exist, apps allow anyone to create new Microsoft 365 Groups. If the policy exists, apps use the defined policy settings.

Be aware that using the Azure AD policy to control group creation is an Azure AD premium feature. Administrators and the accounts who are members of the group used to control group creation need an Azure AD Premium P1 license (included in several Microsoft 365 plans and the Enterprise Mobility and Security suite). In the education sector, Azure AD Basic EDU licenses are sufficient. It’s also important to realize that members of the nominated group receive the right to create new groups. It’s not enough to be the owner of the group: if you want to be able to create new groups, you’ve got to be a member.

As discussed in the previous article, some of the settings in the Azure AD policy are accessible through the Azure AD admin center. The settings controlling group creation are not, so PowerShell is needed. The necessary cmdlets are in the Azure AD Preview module. A bunch of PowerShell examples are available to help people understand how to update the settings for group creation, most of which seem to be based on Microsoft’s version. I have my own version of a script to update the group control settings (downloadable from GitHub), which is parameter driven and has more error checking and validation. You won’t run the script very often, but it’s nice to have a version that does things like report back on what it’s done.

The PowerShell commands used to configure group control are simple and relatively straightforward. The basic approach is:

  • Use the Get-AzureADDirectorySetting cmdlet to check whether the tenant has customized the Azure AD policy for groups. If no, create a new policy using the New-AzureADDirectorySetting cmdlet.
  • Fetch the existing settings using the Get-AzureADDirectorySetting cmdlet and update the two settings in the policy used to control group creation.
    • EnableGroupCreation is $True if anyone can create new Microsoft 365 Groups or $False if creation is restricted.
    • GroupCreationAllowedGroupId contains the Azure AD object identifier (GUID) for a group (security group or Microsoft 365) holding the set of users allowed to create new Microsoft 365 Groups. If this property is not set, then no one except administrators can create new Microsoft groups.
  • Update the Azure AD policy for Groups using the Set-AzureADDirectorySetting cmdlet.

Example commands to enable group creation control are shown below. The variables used to hold the group object identifier and the True/False setting for EnableGroupCreation are set beforehand.

$PolicySettingsId = (Get-AzureADDirectorySetting | ? {$_.DisplayName -eq "Group.Unified"}).Id
If (!$PolicySettingsId) { # No policy settings found for the tenant, so create it and extract the identifier
  $PolicyTemplate = Get-AzureADDirectorySettingTemplate | ? {$_.DisplayName -eq "Group.Unified"}
  $PolicySettings = $PolicyTemplate.CreateDirectorySetting()
  New-AzureADDirectorySetting -DirectorySetting $PolicySettings
  $PolicySettingsId = (Get-AzureADDirectorySetting | ? {$_.DisplayName -eq "Group.Unified"}).Id
} # End If

$PolicySettings = Get-AzureADDirectorySetting -Id $PolicySettingsId
$PolicySettings["EnableGroupCreation"] = $OnOffSwitch
$PolicySettings["GroupCreationAllowedGroupId"] = $GroupId
Set-AzureADDirectorySetting -Id $PolicySettingsId -DirectorySetting $PolicySettings

Once you’ve updated the Azure AD policy for Groups, it takes a little while for apps (like Teams and Planner) which support the policy to pick up the new settings. It’s worth mentioning that apps which don’t include code to check the Azure AD policy won’t respect the settings.

OWA Mailbox Policy

When Microsoft launched Office 365 Groups (now Microsoft 365 Groups) in November 2014, OWA was the initial client. At the time, it made sense to have a setting in the OWA mailbox policy to control who could create new groups. Today, the separate OWA setting is an anachronism that should be replaced by the Azure AD policy.

In any case, the GroupCreationEnabled setting controls whether Outlook users can create new Microsoft 365 groups. Anyone assigned an OWA mailbox policy with GroupCreationEnabled set to True can go ahead – that is, if they’re also allowed to do so by the Azure AD policy.

To help understand the situation in a tenant, this code reports the set of OWA mailbox policies which allow group creation, and the set of mailboxes assigned those policies:

[array]$OWAPolicies = Get-OWAMailboxPolicy | ? {$_.GroupCreationEnabled -eq $True} | Select -ExpandProperty Identity
Write-Host ""
Write-Host "OWA Mailbox policies allowing group creation:"
Write-Host ""
$OWAPolicies
[array]$Mailboxes = Get-CasMailbox | ? {$_.OWAMailboxPolicy -in $OWAPolicies } | Select DisplayName, OWAMailboxPolicy
Write-Host ""
Write-Host "The OWA Mailbox policy assigned to these mailboxes allows them to create Microsoft 365 Groups:"
$Mailboxes

Changing the assigned OWA mailbox policy for an account can take several hours to take effect. You’ll know when the change is effective when OWA no longer offers the option to create a new group.

Managing Group Creation

Managing the creation of Microsoft 365 Groups isn’t difficult. Make sure that Azure AD allows their creation and then decide if everyone or a restricted set can create new groups. Adjust the OWA mailbox policy as required. The need for Azure AD Premium P1 licenses to use the Azure AD policy for groups to control creation is a barrier for some, but probably not in the large enterprise deployments which benefit most from the capability. And if you’re feeling brave, you can create your own approval workflow using Power Apps to allow users to request a new group/team (here’s a useful article to start with).


Learn more about how Office 365 really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2021/08/11/creation-microsoft-365-groups/feed/ 3 51014
Updates to Group Creation Settings in Azure AD Admin Center https://office365itpros.com/2021/08/09/updates-group-creation-settings-azure-ad-admin-center/?utm_source=rss&utm_medium=rss&utm_campaign=updates-group-creation-settings-azure-ad-admin-center https://office365itpros.com/2021/08/09/updates-group-creation-settings-azure-ad-admin-center/#comments Mon, 09 Aug 2021 00:23:00 +0000 https://office365itpros.com/?p=50983

Changes to Close Off Creation Gaps in Administrative Interfaces

Message center notification MC275349 (August 3) informs tenant administrators of the need to check the group creation settings in the Azure AD admin center. Previously, these settings governed the ability of users to create new security groups and Microsoft 365 groups through the Azure AD admin center. However, the setting did not govern other administrative interfaces such as PowerShell or the Microsoft Graph Groups API. The new settings (Figure 1) cover all administrative interfaces and are now in place.

 The Groups creation settings in the Azure AD admin center
Figure 1: The Groups creation settings in the Azure AD admin center

Microsoft advises checking your tenant settings to make sure that the change has not affected the way your organization manages group creation. I haven’t seen any issue in any tenant I use, but it’s good to be sure.

Groups-Specific Controls

Azure AD settings apply to administrative interfaces. Other controls exist at an application level. The best example of this is Microsoft 365 Groups, which use an Azure AD directory policy to store settings used to control different aspects of groups. The default value for the EnableGroupCreation setting is True, meaning that any user can create a new Microsoft 365 group. If False, the GroupCreationAllowedGroupId setting comes into play. This defines the GUID of a group whose members are allowed to create new groups.

Controlling group creation in this way requires Azure AD P1 Premium licenses. Many large organizations have Microsoft 365 plans which include these licenses so the requirement not usually an issue.

What’s a little more problematic for some is the lack of a GUI to control most of the policy settings (the naming policy and blocked words settings are available in the Azure AD admin center). Six years or thereabouts since the introduction of the directory policy for Teams, the lack of a complete GUI means that administrators must use PowerShell to access and update the other policy settings, including group creation.

For instance, to find out the set of users allowed to create groups and the name of the group defined in the policy, we can use this code:

$Values = Get-AzureADDirectorySetting | ?{$_.DisplayName -eq "Group.Unified"}
$GroupId = $Values.Values |?{$_.Name -eq "GroupCreationAllowedGroupId" } | Select -ExpandProperty Value
Write-Host ("The name of the group defined by policy to control group creation is {0} and its object identifier is {1}" -f (Get-AzureADGroup -ObjectId $GroupId).DisplayName, $GroupId)
Get-AzureADGroupMember -ObjectId $GroupId

The name of the group defined by policy to control group creation is GroupCreationControl and its object identifier is 12cb915b-2365-4bed-baf6-6257b3543273

ObjectId                             DisplayName                 UserPrincipalName                     UserType
--------                             -----------                 -----------------                     --------
bff4cd58-1bb8-4898-94de-795f656b4a18 Tony Redmond                Tony.Redmond@office365itpros.com      Member
edc6b121-44b8-4261-9ca7-3603a16caa3e Andy Ruth (Director)        Andy.Ruth@office365itpros.com         Member
43d08764-07d4-418c-8203-a737a8fac7b3 Global Tenant Administrator GblAdmin@office365itpros.com          Member

To modify the group used to control group creation, we must update the directory policy. For example, this code retrieves values for the group we want to use and the current settings and uses the values with the Set-AzureADDirectorySetting cmdlet to update the directory policy.

$ObjectId = (Get-AzureADGroup -SearchString "Group Creation Allowed").ObjectId
$Settings = Get-AzureADDirectorySetting | ? {$_.DisplayName -eq "Group.Unified"}
$Settings[“GroupCreationAllowedGroupId”] = $ObjectId
Set-AzureADDirectorySetting -Id $Settings.Id -DirectorySetting $Settings

Outlook Creation

The GroupCreationEnabled setting in the OWA mailbox policy assigned to mailboxes was the original control mechanism for group creation (OWA was the first client to support Office 365 Groups, as they were named in 2014). This setting persists today and must be True to allow users to create new groups with an Outlook client.

This article contains a more comprehensive treatment of the steps to control the creation of Microsoft 365 Groups.

Not Much Impact?

I suspect that the changes being made won’t affect many of the tenants who control group creation. Tenants that allow users to create groups and teams as they wish probably won’t be affected either, but they have other issues to cope with like a higher proportion of aging and obsolete groups. In any case, the change is a reasonable one to introduce, even if I wish Microsoft would spend some time on other obvious deficiencies, like the lack of a GUI for the Groups directory policy.


Learn how to exploit the Office 365 data available to tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2021/08/09/updates-group-creation-settings-azure-ad-admin-center/feed/ 3 50983
How to Upgrade Office 365 PowerShell Scripts to Use the Graph API https://office365itpros.com/2021/07/26/microsoft-graph-powershell-upgrade/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-graph-powershell-upgrade https://office365itpros.com/2021/07/26/microsoft-graph-powershell-upgrade/#comments Mon, 26 Jul 2021 01:34:00 +0000 https://office365itpros.com/?p=50813

Microsoft Graph PowerShell is Quicker

Without a doubt, using Graph API calls is much faster to retrieve Office 365 than PowerShell cmdlets are. It’s more obvious in complex scripts like the Groups and Teams activity script, which is much faster in its Graph variant (5.1) than its counterpart (4.8) due to Graph API calls replacing cmdlets from the Exchange Online and SharePoint Online modules. Speed matters, especially when a tenant supports thousands of groups, and the Graph API version of the script can process large quantities of groups where the pure PowerShell version struggles to cope.

Seeking an Example to Convert

Good as it is to have a speedier script, the complexity of the activity report is possibly not a good test case to illustrate the decision process that you should go through to decide if it’s a good idea to upgrade a script to use the Graph API, and then measure the improvement. Simplicity is better, so let’s explore what needs to be done to upgrade the Microsoft 365 Groups Membership report script. This script uses the following cmdlets:

  • Get-AzureADUser: Fetch the list of Azure AD accounts in the tenant. Microsoft announced their intention to retire the Azure AD module in June 2021, so this is a good example of the kind of script update needed to replace Azure AD calls with Graph API calls.
  • Get-UnifiedGroup: Fetch the list of Teams in the tenant.
  • Get-Recipient: Fetch the list of Groups a user account belongs to.

Two cmdlets are from the Exchange Online Management module, one is from the Azure AD module. A bunch of other processing is done to filter, sort, and process the data fetched by these cmdlets, but essentially the conversation involves replacing these cmdlets with Graph API calls. Sounds easy.

Using the Graph API

Before any script can use the Graph API, it needs to use an Azure AD registered app. The app serves as the holder for permissions to allow the script to access data. Every registered app has a unique identifier. When you create a registered app, you can generate an app secret. When running the app, we need to know the secret to prove we have permission to use the app.

Before making any calls, you need to know your tenant identifier. This is the GUID for a tenant as returned by the Get-AzureADTenantDetail cmdlet. The Connect-MicrosoftTeams cmdlet also returns this information. Bringing everything together, before we can use an app to access Graph APIs, we need to know the app identifier, app secret, and tenant identifier. You’ll often see code like this in scripts:

$AppId = "a09cf913-5ff9-48a2-8015-f28f2854df26"
$AppSecret = "u6X7_i8K-yhh-b4-z5FEmj_wH_M~nIOz4n"
$TenantId = "22e90715-3da6-4a78-9ec6-b3282389492b"

It’s an important part of working with Graph API apps to understand how permissions work and to ensure that apps receive only the permissions necessary to work with the data they process. In the case of our app, we need:

  • Group.Read.All: to read information about Microsoft 365 Groups in the tenant.
  • GroupMember.Read.All: to read information about the membership of Microsoft 365 Groups.
  • User.Read.All: to read information about Azure AD accounts in the tenant.

Like all programming tasks, it soon becomes second nature to assign the correct permissions, sometimes after browsing Microsoft’s documentation to find the correct permission.

An administrator gives consent to allow the app to use its assigned permissions. Hackers can use OAuth consents as a method to gain permissions to access data, so it’s wise to keep an eye on consents given within an organization, with or without Microsoft’s new App governance add-on for MCAS.

Access Token

Equipped with a suitably permissioned app, app secret, and tenant identifier, the app can request an access token by posting a request to the token endpoint. For Graph API calls to Office 365 data, that’s going to be something like:

https://login.microsoftonline.com/tenant-identifier/oauth2/v2.0/token

The bearer token issued in response confirms that the app has the necessary permissions to access data like Users, Groups, and Sites and whether access is read-only or read-write. The token is included in the authorization header of the requests made to the Graph APIs. Access tokens expire after an hour, so long-running programs need to renew their token to continue processing.

All of this sounds complicated, but once you do it for one script, it becomes second nature to acquire an access token and be ready to start using Graph API calls.

Replacing PowerShell Cmdlets

Like anything else, it takes a little while to become used to fetching data using Graph API calls. You must pay attention to the data that’s fetched. First, to limit the demand on resources, the Graph fetches limited data at one time and you must iterate until no more data is available (a process called pagination). Second, in some cases, the property names used by cmdlets vary to what’s used by the Graph API. For example, the Get-UnifiedGroup cmdlet returns the description of a group in the Notes property whereas the Groups API uses description.

The Graph Explorer is an online Microsoft tool to help developers become accustomed to Graph API syntax and data. You should use the Explorer to test calls before including them in a script. Debugging calls using the Graph Explorer saves a lot of time and heartache. Figure 1 shows the Graph Explorer being used to examine the transitive set of groups returned for a user.

Using the Graph Explorer to test Graph API Calls

Microsoft Graph PowerShell
Figure 1: Using the Graph Explorer to test Graph API Calls

The Graph Explorer is a Graph app. Like any other app, it needs permissions to access data. If a call fails, the Explorer tells you which permissions are missing and you can then consent to the assignment.

Fetching Azure AD Accounts

The first cmdlet we need to replace in the script is the one to fetch the list of Azure AD users in the tenant. In PowerShell, this is:

$Users = Get-AzureADUser -All:$true

The Graph Users API fetches the same data. Unlike the Get-AzureADUser cmdlet, a default set of properties is returned and we must be specific if we want other properties. Here’s the call used.

$Uri = https://graph.microsoft.com/v1.0/users?&`$select=displayName,usertype,assignedlicenses,id,mail,userprincipalname
$Users = Get-GraphData -AccessToken $Token -Uri $Uri

Get-GraphData is a wrapper function to take care of pagination and extraction of data returned by Graph API calls in a form like the PowerShell objects returned by cmdlets. You can make your life easier by including a function like this in any script which interacts with Graph API calls. To see an example of the function I use, download the Graph version of the group membership report script from GitHub.

Fetching Teams

The second call fetches the list of team-enabled groups. The script then creates a hash table to store the teams so that it can be used as a lookup to see if a group is team-enabled when reporting its properties. The PowerShell code uses a server-side filter with the Get-UnifiedGroup cmdlet to return the teams.

$Teams = Get-UnifiedGroup -Filter {ResourceProvisioningOptions -eq "Team"} -ResultSize Unlimited | Select ExternalDirectoryObjectId, DisplayName

The Graph Groups API equivalent uses the same kind of filter:

$Uri = "https://graph.microsoft.com/beta/groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')"
$Teams = Get-GraphData -AccessToken $Token -Uri $Uri

Fetching the Groups a User Belongs to

The last call we make is to find the set of groups a user account is a member of. The Get-Recipient cmdlet is very fast at returning the list of groups based on the distinguished name of an account. The user data returned by Get-AzureADUser doesn’t give us the distinguished name (it is an Exchange Online property), so we must run Get-Recipient twice: once to get the distinguished name, and then use the distinguished name to find the groups.

$DN = (Get-Recipient -Identity $User.UserPrincipalName).DistinguishedName
$Groups = (Get-Recipient -ResultSize Unlimited -RecipientTypeDetails GroupMailbox -Filter "Members -eq '$DN'" | Select DisplayName, Notes, ExternalDirectoryObjectId, ManagedBy, PrimarySmtpAddress)

The Graph Users API can resolve a transitive lookup against groups to find membership information for an account. We can therefor use a call like this:

$Uri = "https://graph.microsoft.com/v1.0/users/" + $user.id +"/transitiveMemberOf"
$Groups = Get-GraphData -AccessToken $Token -Uri $Uri

That’s it. All the other command in the script process data fetched from Azure AD or Exchange Online. Apart from the changes detailed above, the same code is used for both the PowerShell and Graph versions of the script.

Microsoft Graph PowerShell Delivers a Result

Your mileage may vary depending on the backend server you connect to, the state of load on the service, and other factors. My tests, which are surely as reliable as an EPA mileage figure, revealed that the PowerShell version processed accounts at a rate of about 0.6/second each. The Graph version reduced the time to about 0.4/second. In other words, a 50% improvement.

Not every script will benefit from such a speed boost and other scripts will need more work to install Graph turbocharging. But the point is that Graph-powered PowerShell is much faster at processing Office 365 data than pure PowerShell is. Keep that fact in mind the next time you consider how to approach building a PowerShell-based solution for Office 365.


Learn more about how Office 365 really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2021/07/26/microsoft-graph-powershell-upgrade/feed/ 17 50813
How to Hide Teams-Enabled Groups from Exchange Online https://office365itpros.com/2021/07/08/hide-teams-from-exchange/?utm_source=rss&utm_medium=rss&utm_campaign=hide-teams-from-exchange https://office365itpros.com/2021/07/08/hide-teams-from-exchange/#comments Thu, 08 Jul 2021 01:00:00 +0000 https://office365itpros.com/?p=50632

Hide Teams from Exchange to Clean Up a Potential Mess

Updated 17 October 2023

In mid-2018. Microsoft updated Teams so that the Microsoft 365 Groups created for new teams were hidden from Exchange clients (like OWA) and Exchange address lists (like the GAL). This was accomplished by setting the HiddenFromExchangeClientsEnabled and HiddenFromAddressListsEnabled properties of the groups to True. The idea is that there’s no point in revealing team-enabled groups to Exchange when communications for those groups is centered around Teams messaging and meetings.

Groups Created Using Admin Interfaces

Unfortunately, the change only applied to teams created with the Teams clients (desktop, browser, and mobile) and the New-Team cmdlet from the Microsoft Teams PowerShell module. The groups for teams created using the Teams admin center (Figure 1), Azure AD admin center, Microsoft 365 admin center, New-UnifiedGroup PowerShell cmdlet, or the Microsoft Graph are not hidden from Exchange clients or address lists, with the result being that an organization can end up with some teams being visible and others not.

New teams created in the Teams admin center are visible to Exchange clients

Hide teams from Exchange
Figure 1: New teams created in the Teams admin center are visible to Exchange clients

The logic here is that when an administrator creates a new team or group, it is assumed that they can make whatever decisions are necessary about the settings for the new group. This position is undermined by the fact that there’s no way to update the settings to hide groups available in the Teams admin center or Microsoft 365 admin center, so any adjustments must be done using PowerShell or the Graph.

The PowerShell Solution to Hide Teams from Exchange

Fortunately, the solution is reasonably easy to code in PowerShell. The steps are:

Here’s some code to do the job:

$HiddenGroups = 0
Write-Host "Finding team-enabled Microsoft 365 Groups and checking for any which are visible to Exchange clients"
[array]$Groups = Get-UnifiedGroup -Filter {ResourceProvisioningOptions -eq "Team"} -ResultSize Unlimited 
# Reduce to the set visible to Exchange clients
[array]$Groups = $Groups | Where-Object {$_.HiddenFromExchangeClientsEnabled -eq $False}

# Process the remaining groups and hide them from Exchange
If ($Groups.Count -ne 0) {
  ForEach ($Group in $Groups) { 
     Write-Host "Hiding" $Group.DisplayName
     $HiddenGroups++
     Set-UnifiedGroup -Identity $Group.ExternalDirectoryObjectId -HiddenFromExchangeClientsEnabled:$True -HiddenFromAddressListsEnabled:$True
  }
}
Else { Write-Host "No team-enabled Microsoft 365 Groups are visible to Exchange clients and address lists" }

Write-Host ("All done. {0} team-enabled groups hidden from Exchange clients" -f $HiddenGroups)

You can download the script from the Office 365 for IT Pros GitHub repository.

The Graph Method to Hide Teams from Exchange

Update: Since the original post, Microsoft updated the Graph APIs to allow the retrieval and updating of the settings controlling the display of groups to Outlook clients and visibility to Exchange Online address lists. This code is an example of how to use a combination of Microsoft Graph PowerShell SDK cmdlets and Graph API requests to find teams, check each, and update the settings if necessary.

[array]$Teams = Get-MgGroup -Filter "resourceProvisioningOptions/any(x:x eq 'Team')" | Sort-Object DisplayName
If ($Teams) {
   Write-Host ("Processing {0} teams..." -f $Teams.count)
}
# Parameters if we need to update a group
$Parameters = @{
 "HidefromOutlookClients" = "true" 
 "HidefromAddressLists" = "true" }
[int]$i = 0; [int]$Problems = 0
# Process each team, check if it's visible to Outlook clients and if so, hide it
ForEach ($Team in $Teams) {
  $Uri = ("https://graph.microsoft.com/v1.0/groups/{0}?`$select=id,displayName,description,hideFromOutlookClients,hideFromAddressLists" -f $Team.Id)
  $Data = Invoke-MgGraphRequest -Uri $Uri -Method Get
  If ($Data.hideFromOutlookClients -eq $True) {
     Write-Host ("Team {0} is hidden from Outlook clients" -f $Team.displayName)
  } Else {
     Write-Host ("Hiding Team {0} from Outlook clients and Exchange address lists" -f $Team.displayName) -foregroundcolor Red
     $Uri = ("https://graph.microsoft.com/v1.0/groups/{0}" -f $Team.id)
     $Status = Invoke-MgGraphRequest -Uri $Uri -Method Patch -Body $Parameters
     If ($Status) {
       $i++
     } Else {
       $Problems++
     }
  }
}

Write-Host ("All done. {0} groups hidden successfuily. {1} problems." -f $i, $Problems)

Obviously, this check must occur periodically to process newly-created team-enabled groups to hide teams from Exchange Online clients and in address lists. Something like a scheduled job executing an Azure Automation runbook would do the job.


Learn how to exploit the Office 365 data available to tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2021/07/08/hide-teams-from-exchange/feed/ 11 50632
Speeding Up the Groups and Teams Activity Report by Replacing PowerShell with Graph API Calls https://office365itpros.com/2021/06/09/speed-access-large-group-sets-graph/?utm_source=rss&utm_medium=rss&utm_campaign=speed-access-large-group-sets-graph https://office365itpros.com/2021/06/09/speed-access-large-group-sets-graph/#comments Wed, 09 Jun 2021 02:31:00 +0000 https://office365itpros.com/?p=50174

Grappling with PowerShell

Sometimes I hate PowerShell. Not the language itself, just my ineptitude, or my inability to remember how to do things, or the speed of some cmdlets which deal with objects like mailboxes and groups. It’s not that the cmdlets are inefficient. They do a lot of work to retrieve information about objects, so they are slow.

This is fine for ad-hoc queries or where you only need to process a couple of hundred mailboxes or groups. The problem is accentuated as numbers grow, and once the need exists to process thousands of objects, some significant time is spent waiting for cmdlets to complete, meaning that scripts can take hours to run.

Microsoft has made significant progress in the Exchange Online PowerShell module to introduce faster cmdlets like Get-ExoMailbox and Get-ExoMailboxStatistics. These REST-based cmdlets are faster and more robust than their remote PowerShell cousins and these improvements are ample justification for the work needed to revisit and upgrade scripts. The module also supports automatic renewal of sessions to Exchange Online and the Security and Compliance endpoints, so it’s all good.

The Sloth of Get-UnifiedGroup

Things aren’t so impressive with Get-UnifiedGroup, which retrieves details about Microsoft 365 Groups. Reflecting the use of Microsoft 365 groups, Get-UnifiedGroup is a complex cmdlet which assembles details from Azure AD, Exchange Online, and SharePoint Online to give a full picture of group settings. Running Get-UnifiedGroup to fetch details of 200 groups is a slow business; running the cmdlet to fetch details of 10,000 groups is a day-long task. The Get-Team cmdlet is no speedster either. In their defense, Microsoft designed these cmdlets for general-purpose interaction with Groups and Teams and not to be the foundation for reporting thousands of objects over a short period.

If you only need a list of Microsoft 365 Groups, it’s also possible to create the list using the Get-Recipient cmdlet.

Get-Recipient -RecipientTypeDetails GroupMailbox -ResultSize Unlimited

Creating a list of groups with Get-Recipient is usually much faster than creating it with Get-UnifiedGroup. However, although you end up with a list of groups, Get-Recipient doesn’t return any group-related properties, so you usually end up running Get-UnifiedGroup to retrieve settings for an individual group before you can process it. Still, that overhead can be spread out over the processing of a script and might only be needed for some but not all groups.

The Graph is Quicker

Which brings me to the Microsoft Graph API for Groups. As I’ve pointed out for some years, using Graph APIs with PowerShell is a nice way to leverage the approachability of PowerShell and the power of the Graph. The script to create a user activity report from Graph data covering Exchange, SharePoint, OneDrive, Teams, and Yammer is a good example of how accessible the Graph is when you get over the initial learning curve.

Three years ago, I wrote about a script to find obsolete Teams and Groups based on the amount of activity observed in a group across Exchange Online, SharePoint Online, and Teams. In turn, that script was based on an earlier script which processed only Office 365 Groups. Since then, I have tweaked the script in response to comments and feedback and everything worked well. Except that is, once the script ran in large environments supporting thousands of groups. The code worked, but it was slow, and prone to time-outs and failures.

Speeding Up the Report

The solution was to dump as many PowerShell cmdlets as possible and replace them with Graph calls. The script (downloadable from GitHub) now uses the Graph to retrieve:

  • SharePoint Online site usage data for the last 90 days.
  • A list of Microsoft 365 Groups and a list of team-enabled groups.
  • The owners of a group.
  • The display name of the owners (because the first call returns their Azure AD identifier).
  • Some extended properties of a group not fetched when the group list is returned.
  • Counts for group members and guests (easier to do since Microsoft added advanced queries for directory objects in October 2020).
  • Archived status for teams.

The result is that the script is much faster than before and can deal with thousands of groups in a reasonable period. Fetching the group list still takes time as does fetching all the bits that Get-UnifiedGroup returns automatically. On a good day when the service is lightly loaded, the script takes about six seconds per group. On a bad day, it could be eight seconds. Even so, the report (Figure 1) is generated about three times faster.

Results - Teams and Microsoft 365 Groups Activity Report V5.1
--------------------------------------------------------------   
Number of Microsoft 365 Groups scanned                          : 199    
Potentially obsolete groups (based on document library activity): 121    
Potentially obsolete groups (based on conversation activity)    : 130      
Number of Teams-enabled groups                                  : 72    
Percentage of Teams-enabled groups                              : 36.18%

Total Elapsed time:  1257.03 seconds
Summary report in c:\temp\GroupsActivityReport.html and CSV in c:\temp\GroupsActivityReport.csv
Sample output from the Teams and Groups activity report
Figure 1: Sample output from the Teams and Groups activity report

The only remaining use of an “expensive” cmdlet in the script is when Get-ExoMailboxFolderStatistics fetches information about compliance items for Teams stored in Exchange Online mailboxes. The need for this call might disappear soon when Microsoft eventually ships the Teams usage report described in message center notification MC234381 (no sign so far despite a promised delivery of late February). Hopefully, that report will include an update to the Teams usage report API to allow fetching of team activity data like the number of conversations over a period. If this happens, I can eliminate calling Get-ExoMailboxFolderStatistics and gain a further speed boost.

Some Extra Work but Not Onerous

The downsides of using the Graph with PowerShell are that you need to register an app in Azure Active Directory and make sure that the app has the required permissions to access the data. This soon becomes second nature, and once done, being able to process data faster than is possible using the general-purpose Get-UnifiedGroup and Get-Team cmdlets is a big benefit when the time comes to process more than a few groups at one time.

]]>
https://office365itpros.com/2021/06/09/speed-access-large-group-sets-graph/feed/ 9 50174
How to Find Guest Accounts Blocked by the Azure AD B2B Collaboration Policy https://office365itpros.com/2021/05/17/azure-ad-b2b-collaboration-policy/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-b2b-collaboration-policy https://office365itpros.com/2021/05/17/azure-ad-b2b-collaboration-policy/#comments Mon, 17 May 2021 01:58:00 +0000 https://office365itpros.com/?p=49689

Blocking Guests We Don’t Want to Collaborate With

Updated 12 June 2023

In an earlier post, I cover the basics of updating the Azure AD B2B collaboration settings for a Microsoft 365 tenant. Azure AD B2B collaboration external settings allow the tenant to define a deny list of domains they do not want guest accounts to come from or an allow list to define a restrictive set of domains they’re willing to accept guests from. In my experience, most tenants use a deny list. Once implemented, any attempt to add a new guest account from one of the blocked domains will fail. This happens for applications like Teams and Outlook, and administrative interfaces like the Azure AD admin center (Figure 1).

The Azure AD admin center stops an administrator adding a new guest from a blocked domain
Figure 1: The Azure AD admin center stops an administrator adding a new guest from a blocked domain

Azure B2B Collaboration settings work well and no new guest users from domains featuring on its blacklist can be added. However, it does nothing to stop existing guests from those domains continuing to be members of groups and teams within your tenant. Microsoft doesn’t have a facility to detect and remove problem guest users, but it’s relatively easy to do with PowerShell.

The Find Bad Guests Script

We’ve posted a new script called FindBadGuestsFromBlockedDomains.PS1 in the Office 365 for IT Pros GitHub repository. The script works as follows:

  • Read the Azure AD B2B Collaboration settings to find if a blacklist of banned domains exists. If some domains are on the blacklist, we continue.
  • Find the set of Microsoft 365 Groups (including Teams) with guest members.
  • Examine the membership of each group to check if any of the guests come from banned domains.
  • Report the results.

When the script finishes processing the set of groups, it generates some basic statistics (Figure 2) and a CSV file.

Results of scanning for guests from domains blocked by Azure AD B2B Collaboration settings
Figure 2: Results of scanning for guests from blocked domains

Cleaning Up Banned Guests

The CSV file (Figure 3) contains the Azure AD object identifier for each guest account found from a banned domain. This is important because you can use this to drive a removal process if necessary.

Contents of the CSV file detailing guests from blocked domains
Figure 3: Contents of the CSV file detailing guests from blocked domains

Before removing a guest account, remember what it will do:

  • Remove the guest from memberships of all groups/teams they belong to.
  • Remove access to any documents, folders, or lists shared using the guest account.

Before deleting anything, you should review the contents of the CSV file carefully to check that each account really should be deleted. Any guest account that you want to keep should be removed from the file. The updated file can then act as the input for a removal process. For instance, this PowerShell code reads the CSV file and removes the accounts included in the file.

$BadAccounts = Import-Csv c:\temp\BadGuestAccounts.CSV
ForEach ($Account in $BadAccounts) {
   Write-Host "Removing" $Account."Guest Email"
   Remove-AzureADUser -ObjectId $Account.ObjectId }

After removing problem accounts, the remaining guest accounts in the tenant comply with the Azure AD B2B collaboration settings. If you decide to remove guest accounts, it’s probably a good idea to email the group/team owners to let them know what you plan to do, just in case a guest account is required.

Like any of our scripts, the code is written to explain a principal and demonstrate how to construct a solution to a problem. I’m sure the code can be improved, notably by adding better error handling. But it does work (at least in our tenant).


The Office 365 for IT Pros eBook has lots of intensely practical advice to help administrators run tenants. Subscribe to make sure that you benefit from our knowledge.

]]>
https://office365itpros.com/2021/05/17/azure-ad-b2b-collaboration-policy/feed/ 3 49689
SharePoint Online Teamification Can Expose Site Resources as Channel Tabs https://office365itpros.com/2021/04/23/sharepoint-teamification-expose-resources-channel-tabs/?utm_source=rss&utm_medium=rss&utm_campaign=sharepoint-teamification-expose-resources-channel-tabs https://office365itpros.com/2021/04/23/sharepoint-teamification-expose-resources-channel-tabs/#comments Fri, 23 Apr 2021 03:56:00 +0000 https://office365itpros.com/?p=49409

Teamify: An Offer Hard to Refuse

If you’re the owner of a SharePoint Online team site which is connected to a Microsoft 365 group, you might have seen the prompts (subtle harassment) to “teamify” the site. The prompt (Figure 1) promises that you can “collaborate in real-time and share resources across Microsoft 365 with your team.” Sounds good, unless you created the site to use with email-based Microsoft 365 groups (aka Outlook groups) and plan to use email as the collaboration mechanism.

Who could fail to accept an offer to teamify a site?
Figure 1: Who could fail to accept an offer to teamify a site?

Email-Based Microsoft 365 Groups

I use Outlook-based groups quite often to work with companies who don’t use Teams or might not use Microsoft 365. In 2016, Microsoft added support for Azure B2B collaboration to Outlook groups to enable collaboration with external users. Tenant users can post to Outlook groups through Outlook (desktop, mobile, or OWA) but guests rely on email to receive copies of posted messages. In many places, Outlook groups are more than sufficient to work with third parties. But email will always get the message through (within reason) and access to the group’s SharePoint sites makes it easy to work on shared documents.

Driving Teams Adoption

But Teams is where the action is, and Microsoft certainly knows how to create many different touch points to drive user awareness and usage. Teams also uses Microsoft 365 Groups, and it’s more than possible to team-enable Outlook Groups. In technical terms, it’s a matter of creating a team and setting some values for the group, like the resource provisioning option. After creating the team, the team picks up the existing group membership and resources.

Creating Tabs in the General Channel for Site Resources

It’s long been possible to link a team to a group-connected site (the so-called teamify process). Recently, Microsoft added the ability to create channel tabs for site resources when setting up the new team. Figure 2 shows the dialog used to collect information about resources like Microsoft Lists and individual pages.

Selecting SharePoint site resources to create channel tabs in the new team
Figure 2: Selecting SharePoint site resources to create channel tabs in the new team

In Figure 2, two resources are selected to become channel tabs. Figure 3 shows the result with two tabs created for the home page for the site and a Microsoft list hosted in the site.

The SharePoint site resources show up as tabs in the General channel
Figure 3: The SharePoint site resources show up as tabs in the General channel

After creating the team, the process creates tabs in the General channel for the selected resources. You can’t create new channels when you teamify an existing SharePoint site, so the resources must go into tabs in the General channel. After creating the team and building out its channel structure, you can remove resources from the General channel into other channels. Unfortunately, you need to recreate the tabs as there is no way to move a tab from one channel to another.

Hiding the New Team

A small, but potentially important point, is that team-enabling a SharePoint site does not update the properties which hide the group from Exchange clients (Outlook, OWA, and Outlook mobile). If you look at the properties with PowerShell, you’ll see that the group remains visible to Exchange clients and the GAL.

Get-UnifiedGroup -identity "Project Haycock"| fl hiddenfrom*

HiddenFromExchangeClientsEnabled : False
HiddenFromAddressListsEnabled    : False

Is this important? In April 2018, Microsoft decided to hide team-enabled groups from Exchange clients with the logic being that once a group has a team, collaboration flows through Teams channels and chats rather than email. It doesn’t make sense to expose teams to email clients, hence the hiding. You can make up your own mind if you need to run a PowerShell script to find and update team-enabled groups which are still visible.


Need to know more about how Office 365 and its apps really work? Subscribe to the Office 365 for IT Pros eBook and learn on an ongoing basis.

]]>
https://office365itpros.com/2021/04/23/sharepoint-teamification-expose-resources-channel-tabs/feed/ 1 49409
How to Control Updates for User Photos in Microsoft 365 Apps https://office365itpros.com/2021/04/14/control-updates-user-photos-microsoft-365-apps/?utm_source=rss&utm_medium=rss&utm_campaign=control-updates-user-photos-microsoft-365-apps https://office365itpros.com/2021/04/14/control-updates-user-photos-microsoft-365-apps/#comments Wed, 14 Apr 2021 01:00:00 +0000 https://office365itpros.com/?p=49131

Putting the Best Face on Every User

Updated 3 October 2023

Update: Microsoft announced (MC678855) the deprecation of the Exchange Online management cmdlets used to manage user photos (Set-UserPhoto, etc.). These cmdlets will be removed from use on 30 November 2023. You should upgrade scripts to use the cmdlets from the Microsoft Graph PowerShell SDK instead.

In April 2020, Microsoft introduced a policy to stop users being able to update their photo through the Teams client. More accurately, Teams adopted the SetPhotoEnabled setting in the Exchange Online OWA mailbox policy to control if a user can update their photo. Since then, I have noticed a flood of questions (or complaints) from people asking why their attempts to upload a photo is “blocked by policy.” Of course, the answer is that it is, and they should talk to their tenant administrator to have their photo updated, but that’s seldom a welcome response.

Given that user photos show up in places as diverse as the GAL, the Microsoft 365 user profile card, and avatars in applications like SharePoint Online and Teams, it’s a good idea to make sure that appropriate photos are available for users. For example, if a user photo is available, Teams meetings show the photo on a user’s attendee card when their video feed is turned off instead of the more generic “two-initials in a circle” card (Figure 1).

The difference a user photo makes to an attendee card in a Teams meeting
Figure 1: The difference a user photo makes to an attendee card in a Teams meeting

Two Strategies

Organizations usually consider two approaches before deciding on a strategy for user photo management.

  • User-driven. While this strategy involves less work for administrators, it exposes the danger that some users might make less than suitable photo choices. It’s a poor choice for schools and other educational establishments.
  • Organization-driven. This strategy usually means that some tool updates user photos based on a repository such as HR data. The upside of the strategy is the high standard of user photos. The downside is the need to either write a tool or find one to do the job (like Code Two Software’s Photos for Office 365).

Of course, given that control is exerted by OWA mailbox policies, you can run a hybrid strategy where some users can update their photos, and some cannot through the simple step of deploying multiple OWA mailbox policies, some of which enable photo updates and the others which don’t.

The Role Played by Exchange Online

Exchange Online plays a key role in user photo management for other Microsoft 365 applications. The SetPhotoEnabled setting in the Exchange Online OWA mailbox policy assigned to the mailbox controls the ability for users to update their photo. By default, this setting is $False, meaning that users are unable to upload a photo from apps and their Office profile. Users barred by policy see a message such as “picture options are disabled by policy” if they try to change their photo. To allow users to upload and update their photos, either:

  • Update the OWA mailbox policies so that SetPhotoEnabled is $True in all policies, or:
  • Create or update an OWA mailbox policy with SetPhotoEnabled set to $True and assign this policy to the mailboxes of accounts you want to allow to upload photos.

For example, to update an OWA mailbox policy, run the Set-OWAMailboxPolicy cmdlet:

Set-OWAMailboxPolicy -Identity OWAFullAccess -SetPhotoEnabled $True

To assign an OWA mailbox policy to a mailbox, use the Set-CASMailbox cmdlet:

Set-CASMailbox -Identity Chris.Bishop -OWAMailboxPolicy OWAFullAccess

Changes to an OWA mailbox policy take up to 30 minutes before they are effective.

OWA mailbox policies in Exchange Online obviously don’t affect users with an on-premises Exchange mailbox. These users are therefore able to update their photos in apps like Teams.

Updating User Photos Programmatically

Several PowerShell cmdlets are available to administrators to update user photos.

  • The Exchange Online Set-UserPhoto cmdlet updates the photo data in a mailbox. Set-UserPhoto can also update a photo for a group mailbox (be sure to specify the GroupMailbox switch). You cannot use Set-UserPhoto to update other mail-enabled objects, like distribution lists or mail contacts. Photos loaded into Exchange Online are synchronized to other workloads, including SharePoint Online and Teams.
  • The Teams Set-TeamPicture cmdlet updates the image for a team. This is analogous to running Set-UserPhoto to update the photo for a group mailbox. In most cases, it’s best to use Set-UserPhoto to avoid the need to load another module. It’s a good idea to highlight important teams with an appropriate image which conveys the purpose of the team.
  • The Azure AD Set-AzureADUserThumbnailPhoto cmdlet writes photo data to an Azure AD user account. Use this cmdlet when you wish to update photo data for an Azure AD account which doesn’t have an Exchange Online mailbox, like guest accounts. As the cmdlet name suggests, the cmdlet processes thumbnail (small) photos. It does not generate the larger size photos which look better in Teams meetings. For this reason, always use Set-UserPhoto to upload photos for tenant accounts.

Update: With the deprecation of the Azure AD PowerShell module, you should upgrade scripts to use the Set-MgUserPhotoContent cmdlet from the Microsoft Graph PowerShell SDK to update photos for guest accounts.

Exchange Online and Azure AD synchronize photo data to make sure that user accounts have the latest picture. After a short delay to allow the apps to refresh their caches, an updated photo will be active across the ecosystem.

Teams owners can change the picture for a team by clicking the existing picture and uploading a new file (Figure 2). Group owners can do the same for Microsoft 365 groups by editing group properties in OWA’s Manage groups section. In both cases, the picture data is in the group mailbox and will synchronize to other apps.

Updating the photo for a team
Figure 2: Updating the photo for a team

Image files for user photos can be JPEG or PNG format and should be:

  • Resolution: 648 x 648 pixels. This is the largest resolution supported. Behind the scenes, Exchange Online generates smaller 64 x 64 and 96 x 96-pixel thumbnails for apps to use when small thumbnails are appropriate. Most digital photos are much larger (in pixels) so some resizing is needed. Square photos are best as they won’t be cropped. Usually, best results are obtained when the user faces directly into the camera.
  • Size: Less than 500 KB.

Although it can take 30 seconds or more to update a picture for a mailbox, running Set-UserPhoto is simple:

Set-UserPhoto -Identity Chris.Bishop@office365itpros.com -PictureData ([System.IO.File]::ReadAllBytes("c:\Temp\ChrisBishop.jpg")) -Confirm:$False

If you want to check if a mailbox already has a picture (to avoid overwriting it), use the Get-UserPhoto cmdlet. This cmdlet returns $Null if the mailbox has no photo. Remember to include the GroupMailbox switch if checking a group mailbox (including team-enabled groups).

If (Get-UserPhoto -Identity Chris.Bishop@Office365Itpros.com) {Write-Host "Chris has a photo"}

If you make a mistake and upload the wrong image, you can restart by removing the image with the Remove-UserPhoto cmdlet:

Remove-UserPhoto -Identity Chris.Bishop@office365itpros.com -Confirm:$False

An example of how to scan user mailboxes to update photos if none are found can be downloaded from GitHub.

The Personal Side of Users

User photos are extremely personal, and it should come as no surprise that people should be upset when they cannot change their image. If you decide to clamp down on user-initiated photo updates, perhaps it might be a good idea to create a process to allow users to request photo changes. It might just keep people happier.

]]>
https://office365itpros.com/2021/04/14/control-updates-user-photos-microsoft-365-apps/feed/ 10 49131
Why the Group Membership UI in the Microsoft 365 Admin Center Sucks Dirty Canal Water https://office365itpros.com/2021/04/02/group-membership-management/?utm_source=rss&utm_medium=rss&utm_campaign=group-membership-management https://office365itpros.com/2021/04/02/group-membership-management/#comments Fri, 02 Apr 2021 01:13:00 +0000 https://office365itpros.com/?p=49057

Poor UI Design, Especially for Large Groups

Vasil Michev, the technical editor of the Office 365 for IT Pros eBook, is quick to spot a problem that we should pay attention to, which is why he highlighted a concern about the Microsoft 365 admin center UI used for group membership maintenance. In a nutshell, George McDonald, the author of the Microsoft Technical Community post, thinks the UI sucks dirty canal water, saying:

The Members Lists are no longer displayed in a drop-down list, with check boxes preceding each entry, instead now members are clumped together in what can be best described as a single text box, further compounded by the fact that this clump of members is NOT in Alphabetical Order. So if you have anything more than 20 users in the clump, your eyes have to scan every entry carefully to find the required user to remove, then hit an X mark suffix to their name to remove membership. Ridiculous and irritating user experience is putting it politely, make no mistake.

Figure 1 shows the problem. In this case, the group has 57 members and is just about manageable in the format chosen by Microsoft to display group members (as reported below, this UI is now replaced).

Displaying the membership of a Microsoft 365 Group in the Microsoft 365 admin center
Figure 1: Displaying the membership of a Microsoft 365 Group in the Microsoft 365 admin center

With a larger group, the problems in the UI would soon become apparent because:

  • The group is sorted in the order in which members are added. In other words, the first member added to the group is at the top and the most recent member is at the bottom. This doesn’t make sense. Alphabetical sorting would be better, including the ability to sort both A-Z and Z-A. It would also be good to be able to filter members based on properties like location in an advanced search.
  • The list shows the display names of the members. This is fine and people can be identified if duplicate display names don’t exist. For example, what happens if seven people named John Smith or Jane Ng are members? You can’t assume that organizations will apply suffixes to differentiate group members.
  • The initials in circles might look pretty, especially in multiple colors, but they don’t help. In this example, user photos are available for many members, but even if the list included the photo instead of user initials, it probably would not help because the images would be too small.
  • You can search for people to add to the group, but you can’t search the existing members to find someone. The membership limit for Teams is now 25,000. Imagine scanning such a list to find and remove a member.
  • The default display looks as if all members are selected and that any action, like a removal, will be applied to all.

Microsoft Updates the Group Management UI

As proven in Sod’s law, as soon as something is published about a cloud detail, Microsoft ships an update. In this case, they refreshed the group membership management UI (Figure 2) to address some of the more grievous sins evident in the previous UI. The new approach is clearer and better, but it still suffers from some issues.

The new layout for group membership management
Figure 2: The new layout for group membership management
  • There’s still no ability to sort or filter.
  • Limited information is shown about each user. Again, this is fine for a small organization but less good once the number of accounts increases.
  • User photos are not shown (if available).

Another example of poor “fit and finish” is when displaying the members of a team-enabled group. Thoughtfully, Microsoft displays an icon to show that a group member is “Teams enabled” (Figure 3). But what does this mean? After all, the members are in a team, so by definition they are Teams-enabled (I guess they could be new members waiting to receive a Teams license through an auto-claim policy). But what then for guest users? These are Teams enabled because they are a member of a team, but they don’t need licenses to access Teams in another tenant. It’s all very confusing.

What does Teams enabled mean in this context?
Figure 3: What does Teams enabled mean in this context?

I also hate the way that the Add members screen insists on loading and displaying mail contacts first. Although it’s possible that an administrator will want to add a mail contact to a group, it’s more likely that they will want to add a tenant account. Some control should be given to allow administrators to choose the order in which mail recipients load when adding new members to a group.

In summary, the Microsoft 365 admin center UI needs a makeover to improve its ability to handle the membership of large groups, provide better search features, and show more information about each member to differentiate individuals.

OWA Group Management is Better

It can be argued that the UI of the Microsoft 365 admin center is intended to help inexperienced administrators and that experienced people will use other tools, like PowerShell (which I would use to remove members from large groups). However, Microsoft uses an inconsistent array of UI designs for group membership management, some of which exhibit characteristics the admin center could adopt. For example, OWA’s UI for managing a group displays fewer members (initially), but more information is available about each member (including their photo), and you can search for members (Figure 4). There’s no filtering or ability to sort, but OWA’s is a better UI.

The Manage group membership UI in OWA
Figure 4: The Manage group membership UI in OWA

The Art of the UI

I’m sure Microsoft has heaps of UI experts on staff. Sometimes they get it right, like the recent change to the SharePoint Online sharing control, and sometimes they get it wrong, like the group management UI described above. Perhaps some knowledgeable eyes could look at the different ways to manage group membership surfaced across Microsoft 365 apps with a view to coming up with a common approach that works well for both small and large groups. Wouldn’t that be nice?

]]>
https://office365itpros.com/2021/04/02/group-membership-management/feed/ 1 49057
How to Create a Report About the Membership of Microsoft 365 Groups (and Teams) https://office365itpros.com/2021/02/23/report-teams-membership/?utm_source=rss&utm_medium=rss&utm_campaign=report-teams-membership https://office365itpros.com/2021/02/23/report-teams-membership/#comments Tue, 23 Feb 2021 01:45:00 +0000 https://office365itpros.com/?p=48387

Writing a Script to Report Microsoft 365 Group Memberships

Updated: 19 January 2023 – See this article for a new version of the script based on the Microsoft Graph PowerShell SDK.

Hot on the heels of the discussion about how to create a printable report listing the membership of a Microsoft 365 group (or team), the question is: “How can I create a report listing the members of all groups in my tenant?” Given the widespread use of Teams, the request is often to report teams membership.

A HTML report of all members in all Microsoft 365 Groups in a tenant

Report Teams membership
Figure 1: A HTML report detailing all members for all the Microsoft 365 Groups in a tenant

The Usual Approach Works, but It’s Slow

It’s a good question, and it’s one that is answered elsewhere, such as Steve Goodman’s take on the topic. However, all the approaches I have seen to date have attacked the problem as follows:

  • Run the Get-UnifiedGroup cmdlet to create a list of groups.
  • For each group, use the Get-UnifiedGroupLinks cmdlet to fetch the membership (and owners).
  • Export the results in a CSV file.

Apart from its slowness, there’s nothing wrong with this approach. The Get-UnifiedGroup cmdlet is a “fat” cmdlet. It fetches a lot of information to deliver the set of properties for each group. And the Get-UnifiedGroupLinks cmdlet is also pretty heavy. Put the two together, and things will be slow. This is fine if you have only a couple of hundred groups to process. It’s not so good when you have thousands.

Process Users, Not Groups

I decided to take a different tack. Instead of processing one group at a time, the script should process users. Basically:

  • Get a list of users from Azure Active Directory. This includes tenant and guest accounts.
  • Drop the accounts that belong to the tenant but aren’t licensed. These include accounts used for room and resource mailboxes, shared mailboxes, and service accounts, none of which will be in group membership.
  • Use Get-UnifiedGroup to return a list of team-enabled groups. Although expensive, at least the call uses a server-side filter, so Exchange Online does the processing to return the set. The set of teams is put into a hash table for quick lookup when we need to know if a group is team-enabled. As noted below, a Graph query is faster at retrieving groups and teams.
  • For each user, use the Get-Recipient cmdlet to fetch a list of Microsoft 365 groups the user belongs to. This sounds as if it should take a lot of processing, but it doesn’t because we can use a server-side filter based on the user’s distinguished name.
  • If some groups are returned, extract information for each group, check if it is team-enabled, and update a PowerShell list with the details.
  • Update another list with summary information about the user, such as how many groups they belong to and a list of the display names for those groups.
  • If the user doesn’t belong to any groups (many guest accounts are in this category), only output the summary data.
  • After processing all users, generate some statistics and create three output files:
    • A HTML report in two sections. First, a listing of group membership with a single line for each group a user belongs to. Second, a one-line summary for each user reporting how many groups they are in and the display names of those groups.
    • A CSV file of the group membership data.
    • A CSV file of the user membership summary data.

The script can be downloaded from GitHub. In testing, it took around a half-second per account (Figure 2), which isn’t too bad considering the amount of processing done.

Creating a Microsoft 365 Groups membership report with PowerShell
Figure 2: Creating a Microsoft 365 Groups membership report with PowerShell

Groups with no members are ignored by the script. These groups might have owners, but the lack of members mean that they are not picked up when checking group membership on a per-user basis.

Searching for Speed in a Teams Membership Report

A script powered by the Graph API will deliver faster results in places like fetching a list of team-enabled groups (using the list groups API). You can also use the Get-MgGroup cmdlet from the Microsoft Graph PowerShell SDK to return the list of team-enabled groups. The set of groups a user belongs to is found using the list user transitive member of API. For example, a call like https://graph.microsoft.com/v1.0/users/{GUID}/transitiveMemberOf returns the set of groups that the account with the object identifier (GUID) is a member of. Using the Microsoft Graph PowerShell SDK, the code to return the set of groups a user belongs to would be something like this:

$User = Get-MgUser -UserId James.Ryan@office365itpros.com
$Uri = "https://graph.microsoft.com/v1.0/users/" + $User.Id + "/transitiveMemberOf "
[array]$UserGroups = Invoke-MgGraphRequest -Uri $Uri -Method Get

I also wrote a Graph version of the script, which you can also find on GitHub. Remember that you must register an app in Azure AD, assign the app the necessary permissions, and create an app secret (or other credentials) before you can use this version. On the upside, the Graph version is faster and scales better for large tenants.


Learn much more about interacting with Microsoft 365 Groups and Teams through PowerShell by subscribing to the Office 365 for IT Pros eBook. Monthly updates keep you up to date with what’s happening across the Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2021/02/23/report-teams-membership/feed/ 7 48387
Blocking Download Permission for Teams Meeting Recordings https://office365itpros.com/2021/02/22/blocking-download-permission-teams-meeting-recordings/?utm_source=rss&utm_medium=rss&utm_campaign=blocking-download-permission-teams-meeting-recordings https://office365itpros.com/2021/02/22/blocking-download-permission-teams-meeting-recordings/#comments Mon, 22 Feb 2021 00:35:00 +0000 https://office365itpros.com/?p=48429

Switching Storage from Stream to ODSP

Following on from the change in timing for the general switchover of Teams meeting recordings from Stream Classic to OneDrive for Business and SharePoint Online (ODSP for short) to July 2021, Microsoft is leveraging SharePoint permissions to have better control access to recordings. This wasn’t possible in Stream Classic, but it is now that Teams is adopting SharePoint-based sharing.

After you switch Teams meeting recordings to ODSP, new meeting recordings are not stored in Stream. Instead:

  • Recordings for personal and group chats and personal (private) meetings are stored in the OneDrive for Business account of the user who starts the recording. This user is the owner of the recording.
  • Recordings for channel meetings are stored in the channel folder of the document library in the SharePoint team site owned by the team.

In both cases, the MP4 files for the recordings are in the Recordings folder.

No Downloads Please

In message center MC230505 (updated February 18), Microsoft makes the important clarification that the only person allowed to download a recording for a personal chat or meeting is the owner. Everyone else is assigned view-only permission to the file.

Permissions for a Teams meeting recording in OneDrive for Business
Figure 1: Permissions for a Teams meeting recording in OneDrive for Business

A change due to roll out in early April and finish in June will block users with view-only permission from downloading the file. Only those with edit access to recordings can change the permissions to allow others to download the files. The change is described in Microsoft 365 roadmap 70543. Organizations cannot override the assignment of permissions to meeting recordings or the way the permissions work.

Channel Meetings are Different

Channel meetings are treated differently. Once someone uses the Teams calendar app or the channel calendar app to create a channel meeting, the team which owns the channel becomes the owner of the event. The person who schedules the meeting can still update meeting settings, but they are not the owner.

This is important because the Microsoft 365 Groups access model which underpins Teams dictates that team members have equal access to group resources. The simplicity of the Groups membership model makes it easy to understand, but sometimes its lack of granularity is regrettable and forces change, such as the introduction of private channels in Teams to support confidential access to resources for a subset of team members. Because team members enjoy the same level of access to group resources, they have edit permission for meeting recordings stored in the document library of the SharePoint site owned by the team.

Don’t Discuss Sensitive Information in Channel Meetings

The devil is always in the detail. In this case, Microsoft recommends that organizations do not use channel meetings to discuss confidential or sensitive information. The reason why is simple: if you do, any team member can access files shared in the meeting or download the meeting recording, which is probably not what you want to happen with sensitive material.

Instead, use private meetings when you need control over who can join the meeting and who will be able to access information shared in the meeting. Recent changes to meeting settings allow precise control over who can join a meeting automatically, meaning that you can be sure that someone can’t sneak in using a meeting link shared by another participant.


So much change, so much detail. Stay abreast of developments by subscribing to the Office 365 for IT Pros eBook and receive monthly updates (a completely new book). It’s the best value in IT!

]]>
https://office365itpros.com/2021/02/22/blocking-download-permission-teams-meeting-recordings/feed/ 1 48429
Printing a Report of Microsoft 365 Group (Team) Membership https://office365itpros.com/2021/02/19/printing-microsoft365-group-membership/?utm_source=rss&utm_medium=rss&utm_campaign=printing-microsoft365-group-membership https://office365itpros.com/2021/02/19/printing-microsoft365-group-membership/#comments Fri, 19 Feb 2021 08:15:00 +0000 https://office365itpros.com/?p=48368

The Sad Decline of Printing

One of the most common requests I get is “how can I print off the membership of a group or team?” On one level, it’s a rather old-fashioned request. Much to the chagrin of printing companies, people don’t print as often as they used to. When I worked at HP, I sat through many presentations about the wonders of printer technology and how ink jets work, but all that business about super-heated injection through microscopic holes of ink configured to an exact chemical specification isn’t quite as popular (or exciting) anymore.

How Apps List Group Membership

Applications list the membership of a Microsoft 365 group in different ways. OWA has quite a nice way of displaying group membership (Figure 1) but misses out that all-important print option.

How OWA lists the membership of a Microsoft 365 Group
Figure 1: How OWA lists the membership of a Microsoft 365 Group

Teams takes much the same approach in its Manage team option (Figure 2) and also neglects to include a printing feature. It’s a little surprising that Teams doesn’t support printing off membership information seeing that so many education establishments use Teams for classes. Then again, Teams pretty well eschews printing everywhere (clearly a member of the anti-ink faction) and leaves it to other apps to print off files like meeting attendance reports.

How Teams lists the membership of a Microsoft 365 Group
Figure 2: How Teams lists the membership of a Microsoft 365 Group

Neither OWA nor Teams reveal much about the members. Some details are exposed, like title and location (Teams), but not a lot.

Code Needed

The solution is to write some code to create a report about group membership. You could do this using PowerShell or the Graph API. There are many examples of PowerShell scripts to attack the problem that can be found on the internet (like this mega-script by Vasil Michev), but there’s always room for another.

The things to remember about Microsoft 365 Groups are:

  • An owner must be a member.
  • Owners and members are stored in two lists. Each list is a set of links back to individual accounts. Dynamic Groups also have two lists; the sole difference is that Azure AD periodically calculates the membership of each list by querying the directory.
  • Microsoft 365 Groups support only tenant accounts and guest accounts as members. No nesting is allowed, probably because of the complexities it might introduce for applications which use Groups as a membership and identity service.

The setup of Microsoft 365 Groups mean that they are much easier to process than Exchange distribution groups. We need to:

  • Fetch all the members and fetch the properties of each member that we are interested in.
  • Do the same for the owners.
  • Generate a report.

In my case, I decided to generate a HTML format report (Figure 3) and a CSV file. The report is for printing, the CSV file is to analyze membership as needed. Because we’re using PowerShell, it’s easy to include or remove properties for each member. I chose to include name, user principal name, email, title, department, office, city, post code, country, type (tenant or guest account), and member type (owner or member). The bottom of the report contains some summary detail such as the number of members, owners, and guests, the description of the group, and when it was created.

HTML membership report for a Microsoft 365 group
Figure 3: HTML membership report for a Microsoft 365 group

You can download the script from GitHub. The code is very straightforward. Feel free to improve it as you wish. And if you want a report detailing all the Microsoft 365 Groups and Teams in your tenant, check out the Groups and Teams Activity Report.

And if you’d like to create a report of the membership of every group/team in your tenant, check out this article.


Want to understand how to use PowerShell with Microsoft 365 Groups and Teams? Read Chapter 13 of the Office 365 for IT Pros eBook!

]]>
https://office365itpros.com/2021/02/19/printing-microsoft365-group-membership/feed/ 2 48368
New URL Format for Incoming Webhook Used by Teams and Groups https://office365itpros.com/2021/02/03/incoming-webhook-connector-format/?utm_source=rss&utm_medium=rss&utm_campaign=incoming-webhook-connector-format https://office365itpros.com/2021/02/03/incoming-webhook-connector-format/#comments Wed, 03 Feb 2021 02:12:00 +0000 https://office365itpros.com/?p=41450

Connectors Must be Updated by April 11, 2021

Office 365 Notification MC234048 published on January 12 announces that Microsoft is improving the security of the “Microsoft Teams connector apps webhook.” That mouthful refers to the incoming webhook connector which can be attached to Microsoft 365 Groups or Teams channels to allow the posting of cards generated from information in network sources.

The webhook is a unique URL which applications use to address the channel where they wish to publish information. The change being made adds the tenant name to the webhook URL to make it more apparent than previous. According to Microsoft, security is improved through the presence of the tenant name because organizations can then use the information to filter traffic logs. Quite how this improves security is beyond me because the tenant identifier (a GUID) has always been present in the webhook URL.

The changeover to the new format started on January 11, 2021. Old format webhooks will continue working for three months. The webhooks must be updated to the new format by April 11, 2021 to ensure that information continues to flow.

Interpreting the Webhook URL

The new format webhook looks like this:

https://office365itpros.webhook.office.com/webhookb2/7aa49aa6-7840-443d-806c-08ebe8f59966@c662313f-14fc-43a2-9a7a-d2e27f4f3478/IncomingWebhook/8592f62b50cf41b9b93ba0c0a00a0b88/eff4cd58-1bb8-4899-94de-795f656b4a18

The component parts are of the webhook are:

  • Tenant name (taken from Azure AD): Office365itpros. This is the new piece added to the webhook URL.
  • Webhook root: webhook.office.com/webhookb2. Old webhook URLs use office.com/webhook.
  • Group identifier: The Azure AD identifier for the Microsoft 365 group. In the example, this is 7aa49aa6-7840-443d-806c-08ebe8f59966.
  • Tenant identifier: The Azure AD identifier for the tenant. In the example, this is c662313f-14fc-43a2-9a7a-d2e27f4f3478. The group and tenant identifiers are separated by an at sign.
  • Provider name: This is always “IncomingWebhook.”
  • Alternate identifier: Another GUID to make the URL unique because a group or channel can support multiple webhooks. In this case, it’s 8592f62b50cf41b9b93ba0c0a00a0b88.
  • Group owner: The Azure AD identifier (GUID) for the group owner. Or rather, the account of the owner who adds the webhook to the group.

The same connector is used to bring data in from external sources to both Teams (data is posted in a channel) and Microsoft 365 Groups (data is posted as topics in email conversations).

Updating a Connector

Updating a connector to use the new format webhook isn’t hard. The trick is to know where the connectors are currently configured. When you know that, a group owner can access the set of connectors and check the incoming webhook connector. If “Attention required” appears for the connector, the webhook must be updated (Figure 1). If not, the connector is using the new format.

Attention required for an incoming webhook connector in a Teams channel
Figure 1: Attention required for an incoming webhook connector in a Teams channel

Clicking Manage brings you to the connector settings (Figure 2). Click Update URL to generate a new format webhook URL and then Save to update the connector. Before you save, make sure that you copy the webhook URL as you’ll need this to update the applications which send data to Microsoft 365 Groups or Teams through the connector.

Updating the webhook URL for a connector
Figure 2: Updating the webhook URL for a connector

The applications might be as simple as a PowerShell script (here’s an example of posting Microsoft 365 roadmap items to Teams and another to post notifications about inactive mailboxes). In other cases, the webhook URL might be used to post information coming from an application.

Finding Teams Using the Incoming Webhook

If you have only a few teams configured with the incoming webhook it won’t be hard to find and update the URL. Things are a little more complex in large organizations where many webhooks might be in use for both Teams and Microsoft 365 Groups. In these circumstances, some help might be needed to find all the connectors.

Some time ago, I wrote a PowerShell script to show how to report the channels and tabs connected to Teams. The point of that post was to demonstrate the retrieval of large amounts of data from Teams. As each team has standard apps (like Calls)), can have up to 200 channels, and each channel can have multiple tabs (including standard tabs), the script collects a bunch of information. In this instance, we can use it to find which teams have connectors configured with the incoming webhook connector.

After running the script, we can query the output report to find instances of teams with the connector:

$Report | ?{$_.App -eq "incoming webhook"} | Format-Table Team

Team
----
Technology News and Views
Engineering Colleagues
Human Resources Group
PL Test
Industry News

The report doesn’t tell us for which channel the connector is configured, but that should be easily found.

In passing, if you want to get an insight into the number of standard apps installed by Teams, run this command to group the results in the report:

$Report | Group App | Sort Count -Descending | Format-Table Name, Count

As they say, the output is “interesting”!


Interpreting the real meaning of a Microsoft announcement takes experience and background knowledge. Learn from the best by subscribing to the Office 365 for IT Pros eBook. No bumpf, just knowledge.

]]>
https://office365itpros.com/2021/02/03/incoming-webhook-connector-format/feed/ 5 41450
Use the Graph API to Work with Azure AD Access Reviews https://office365itpros.com/2021/01/26/graph-api-azure-ad-access-reviews/?utm_source=rss&utm_medium=rss&utm_campaign=graph-api-azure-ad-access-reviews https://office365itpros.com/2021/01/26/graph-api-azure-ad-access-reviews/#comments Tue, 26 Jan 2021 01:19:00 +0000 https://office365itpros.com/?p=40660

Check that Guests are Really Wanted

Microsoft is previewing the ability to create an Azure AD Access Review to cover guest access to every group (and team) in a tenant. The idea is that group owners are asked to approve or deny the access granted to guest users to their groups. With the caveat that all previews come with rough edges, the review works well enough for organizations to assess if the feature is valuable for them.

A challenge facing every GUI is how to achieve the balance of usability for both large and small organizations. Getting an oversight of an access review for guests in 27 teams in a small tenant makes certain demands on the GUI to make the data comprehensible and enable administrators to figure out where the overall review is at. Doing the same for a large tenant where reviews might be ongoing for thousands of teams poses a different test. However, the Identity Governance section in the Azure AD admin center has just one interface to manage access reviews (Figure 1).

Details of an Azure AD Access Review for all groups in a tenant
Figure 1: Details of an Azure AD Access Review for all groups in a tenant

When an access review is viewed through the Azure AD admin center, you see10 groups at a time (it’s a preview), but even if the admin center showed a hundred groups, paging through large numbers of groups to find what’s happening in an individual review can be painful.

Using the Graph API for Access Reviews

Which brings us to the Graph API for Azure AD Access Reviews, the basis for DIY management of access reviews. To test how the API worked, I wrote a PowerShell script to find the review for all groups and create a report of the review decisions made to date.

The steps taken in the script are:

  • As usual when using PowerShell to interact with the Graph, create a registered app in Azure AD. Note the app identifier, tenant identifier, and app secret. The app must be assigned the AccessReview.ReadWrite.All permission. I also assigned the Group.ReadApp permission to allow the app to retrieve details of Azure AD groups.
  • Use the app identifier, tenant identifier, and app secret to get an access token.
  • Read the set of access reviews known in the tenant and find the one used for guest access to all groups. This will result in a review instance.
  • Find the groups within the scope of the review. These are called instances. In other words, an instance of the overall review applied to a specific group.
  • For each instance, retrieve the decisions made for the guests in the group. It’s here that I retrieved the group display name to make it easier to understand the output.
  • Store details of each decision (verdict). The verdict will be Deny, Approve, or NotReviewed. A verdict comes with the name of the person who decided and when they decided. If the policy dictates, a justification is also present.
  • Capture the details of the verdict out in a PowerShell list.
  • After processing all instances, write the verdict data out from the list to a CSV file and give an overall report on screen.

Figure 2 shows the output at the end of the script.

Figure 2: Overall statistics for an Azure AD Access Review for guest users

The script generates a CSV file to allow the decision data to be analyzed in whatever way you wish. Piping the data to the Out-GridView cmdlet is a good way to get a quick overview of the current state of reviews across all groups (Figure 3).

Figure 3: Reviewing the decision status for access reviews

Download the Script

The sample script can be downloaded from GitHub. It doesn’t exercise all the functionality available in the API. For example, to accelerate the process of completing the review, you could look for outstanding reviews of guests in groups and call the acceptRecommendations API to accept the automatic recommendations as made by Azure AD. However, as I explain here, accepting automatic recommendations is not always the wisest thing to do, especially when Azure AD makes decisions based on limited data.


You’ll find full details about Azure B2B collaboration (the basis of guest access to Teams and Groups) plus a ton of insight about how guest access works in the Office 365 for IT Pros eBook. And because we keep the book updated, new developments like the Azure AD Access Review for all guests in a tenant are mentioned there too.

]]>
https://office365itpros.com/2021/01/26/graph-api-azure-ad-access-reviews/feed/ 2 40660
How to Use Microsoft 365 Priority Accounts for Access Checks https://office365itpros.com/2021/01/15/use-microsoft-365-priority-accounts-access-checks/?utm_source=rss&utm_medium=rss&utm_campaign=use-microsoft-365-priority-accounts-access-checks https://office365itpros.com/2021/01/15/use-microsoft-365-priority-accounts-access-checks/#comments Fri, 15 Jan 2021 02:02:00 +0000 https://office365itpros.com/?p=38770

Underwhelming Reaction to Microsoft 365 Priority Accounts

In a recent Petri.com article, I review how to create Microsoft 365 priority accounts and how Microsoft sees these accounts being used. Essentially, Microsoft plans to roll out features which apply to priority accounts. The first two features are premium mail flow monitoring and account protection (alerts for which are shown in Figure 1).

Priority accounts show up in Microsoft 365 alerts
Figure 1: Priority accounts show up in Microsoft 365 alerts

The reaction from readers wasn’t positive. Not really negative, but more of a “hummh… how uninteresting.” Some said that they already knew their priority accounts and already managed the service delivered to these accounts, some were underwhelmed by the features Microsoft have available to leverage priority accounts (more are planned in the future).

These are valid reactions. Depending on available resources, it can be easier to manage services in a small organization and larger organizations often invest in third-party monitoring or mail hygiene services to provide additional protection. Few will be convinced to drop these services to depend solely on Microsoft.

We’ll have to see what Microsoft delivers in the future to underline the value of priority accounts, but as any Microsoft 365 tenant can nominate priority accounts, they can be used to identify a set of accounts for your own purposes.

Checking Accounts

It’s common to mark certain accounts for specific processing or to allow access to an application or other resource. Out-of-the-box methods to store a set of accounts include:

  • An email distribution list.
  • A Microsoft 365 group.
  • A security group.
  • A custom mailbox attribute.

For instance, to check if someone is a member of a distribution list, we pass their mailbox alias to the Get-DistributionGroupMember cmdlet to see if a match exists:

$UserAlias = (Get-ExoMailbox -Identity Ben.Owens@Office365itpros.com).Alias
If ((Get-DistributionGroupMember -Identity VIPUsers) -Match $UserAlias) {Write-Host "User is authorized"}

The same kind of check works against the membership of a Microsoft 365 Group:

If ((Get-UnifiedGroupLinks -Identity "Privacy Advocates" -LinkType Member) -Match $UserAlias) {Write-Host "User is authorized"}

While a check against a custom mailbox attribute could be something like:

If ((Get-ExoMailbox -Identity $UserAlias -Properties CustomAttribute1).CustomAttribute1 -eq "IT") {Write-Host "User is authorized"}

Given that priority users are marked with an (invisible) attribute returned by the Get-User cmdlet, we can perform a similar access check with:

If ((Get-User -IsVIP) -Match $UserAlias) {Write-Host "User is authorized"}

Using groups or custom attributes allows more flexibility in marking accounts than adding them to the priority list does. However, I can see the value in using priority accounts as a form of access check if an organization already uses them for other purposes. Where there’s a will, there’s a way… or in the case of Office 365, where you need to do something, there’s probably multiple ways to do it.


Office 365 is the gift that keeps on giving to writers. Keep focused and up to date with the most important developments by subscribing to the Office 365 for IT Pros eBook. The not-so-important stuff that doesn’t get into the book ends up here!

]]>
https://office365itpros.com/2021/01/15/use-microsoft-365-priority-accounts-access-checks/feed/ 2 38770
Hitting the Million Messages Limit in an Aggregate Group Mailbox https://office365itpros.com/2020/12/23/million-messages-aggregate-group-mailbox/?utm_source=rss&utm_medium=rss&utm_campaign=million-messages-aggregate-group-mailbox https://office365itpros.com/2020/12/23/million-messages-aggregate-group-mailbox/#respond Wed, 23 Dec 2020 06:48:55 +0000 https://office365itpros.com/?p=36447

A post to the Office 365 Technical Discussions Facebook group reported the strange case where a mailbox encountered the ItemsInFolder threshold. This happens when the number of items in a mailbox folder reaches one million, the maximum supported number for an Exchange Online folder.

Curious NDR

The problem became apparent when a user sent a message to a Microsoft 365 group and received a non-delivery report (NDR) with a 554 4.3.2 status. The natural assumption was that the problem was with the group, but when administrators checked the inbox in the group mailbox, they discovered that the number of items was around six thousand, far removed from the million-item limit. Further investigation of the NDR revealed that the problem occurred with a mailbox called AggregateGroupMailbox.A.201708112321@contoso.onmicrosoft.com.

When this mailbox was checked, it was found that a million items were in its inbox.

The aggregate group mailbox is an arbitration (system) mailbox used by Exchange Online to hold copies of messages sent to Microsoft 365 groups. According to Microsoft, the items in the mailbox are used to support searches against groups. Bringing copies of messages from multiple groups together in a single mailbox makes it easier to search across those mailboxes at the risk of blowing mailbox limits. A background assistant purges items from the arbitration mailboxes periodically, but when group mailboxes are used heavily, it’s possible to exceed the million-item limit. Microsoft has modified the way search works and the arbitration mailboxes are no longer used for searching.

Solution – Drop Messages Using a Mail Flow Rule

Until Microsoft removes the arbitration mailboxes, the workaround is to block them from receiving any further messages. The easiest way to do this is to create a mail flow (transport) rule to drop messages sent to the arbitration mailboxes. For example, this PowerShell command creates a mail flow rule to drop messages sent to a specific arbitration mailbox using an address found in the NDR.

New-TransportRule -SentTo @("AggregateGroupMailbox.A.201708181918@contoso.onmicrosoft.com") -DeleteMessage:$true -Name 'Block Messages Copies to the Aggregate Group Mailbox' -StopRuleProcessing:$false -Mode 'Enforce' -Comments 'A transport rule to drop messages copied to the Aggregate Group Mailbox' -RuleErrorAction 'Ignore' -SenderAddressLocation 'Header'

This problem should go away during 2021 when Microsoft completes the work to stop messages being copied to the aggregate group mailboxes and removes these mailboxes from Exchange Online. In the interim, organizations don’t need to do anything unless they encounter NDRs for a mailbox they probably never knew to exist.

Microsoft’s support article on the topic is available here.

]]>
https://office365itpros.com/2020/12/23/million-messages-aggregate-group-mailbox/feed/ 0 36447
Change in Guest Access for Teams: No Effect on Tenants Already Using Teams https://office365itpros.com/2020/12/08/teams-default-guest-access-setting-changing/?utm_source=rss&utm_medium=rss&utm_campaign=teams-default-guest-access-setting-changing https://office365itpros.com/2020/12/08/teams-default-guest-access-setting-changing/#comments Tue, 08 Dec 2020 03:19:00 +0000 https://office365itpros.com/?p=35484

Service Default Changes on February 8

In Office 365 notification MC228482 posted on December 3, Microsoft gives early warning of a change in the default tenant configuration for Teams. Up to now, the “service default” for guest access to Teams is Off, meaning that Teams doesn’t allow guest access unless an administrator updates the value to On. From February 8, 2021, the service default changes to On. In effect, Microsoft will then assume that tenants want to allow guest access to Teams.

Tenant control over guest access is set through the Org-wide settings section of the Teams admin center. Here you can define if guest access is allowed or not. As you can see in Figure 1, the option is set to On in my tenant.

Setting the Guest Access control in the Teams Admin Center
Figure 1: Setting the Guest Access control in the Teams Admin Center

The change in service default won’t affect tenants who have already opted to allow guest access to Teams, which is probably most of the tenants which now support over 115 million daily Teams users. It also won’t affect organizations which choose to disable guest access for Teams. However, organizations that have not yet started to use Teams should review if they wish to use guest access and if not, set the option to Off.

Teams depends on the Azure B2B Collaboration integration for Microsoft 365 Groups. Turning guest access on for Teams as the default doesn’t remove the need to enable the guest settings for Microsoft 365 Groups in the Org settings section of the Microsoft 365 admin center.

Limiting Guest Access at a Granular Level

Before disabling guest access, remember that other controls exist to limit guest access on a more granular level.

First, you can use sensitivity labels to control guest access for individual teams. If the container setting for the sensitivity label assigned to a team blocks guest access, team owners won’t be able to add new guests. However, existing guests in the team membership are not removed and tenant administrators can always add guests to team membership if necessary. The script described in this post creates a report of guests belonging to Microsoft 365 groups assigned a specific sensitivity label.

Second, you can block guest access from specific domains using an Azure B2B collaboration policy. For instance, you could include the domains for competitor companies in a blocklist to prevent team owners adding people from those domains as guests. Again, existing guests are not affected.

Tracking Down Unwanted Guests

If you need to scan the entire tenant for the presence of unwanted guest accounts, you can use the PowerShell script described in this post to create a report of guests in a tenant and the Microsoft 365 groups they belong to. The script can be adjusted to report guests based on the number of days since their account was created, so you can focus on all guests or guests created since a specific point in time.

Some guest accounts might have been created for a long-gone purpose. It’s a good idea to review guest accounts from time to time to figure out if any are no longer required and can be removed. This script helps by creating a report of guest user activity.

Teams Owners Can Restrict Guests Too

Within a team, you can restrict guest access by creating a private channel and limiting its membership to tenant accounts. This is a good way to create a barrier within a team for information which should remain confidential. If you want to be even more secure, apply a sensitivity label with encryption to any documents stored in the private channel and make sure that the label settings restrict access to tenant accounts.


The ins and outs of Azure B2B collaboration and guest account access to resources is explained in depth in the Office 365 for IT Pros eBook. Subscribe today to keep abreast of changes as they appear inside Microsoft 365.

]]>
https://office365itpros.com/2020/12/08/teams-default-guest-access-setting-changing/feed/ 1 35484
Outlook’s Groups Menu Bar Now Includes Teams https://office365itpros.com/2020/11/20/outlooks-groups-menu-bar/?utm_source=rss&utm_medium=rss&utm_campaign=outlooks-groups-menu-bar https://office365itpros.com/2020/11/20/outlooks-groups-menu-bar/#comments Fri, 20 Nov 2020 08:53:49 +0000 https://office365itpros.com/?p=34889

Introducing the Teams Button

Today’s topic is an unannounced update that’s just turned up in Outlook for Windows version 2011 (click to run build 13426.20184). At least, I’ve just noticed the change, which adds a Teams button to the Groups menu bar displayed when a team-enabled Microsoft 365 group (aka an Office 365 group or even Outlook group) is accessed (Figure 1). The button is hidden when you open a Microsoft 365 group that doesn’t have an associated team.

The Teams button in Outlook's Groups menu bar
Figure 1: The Teams button in Outlook’s Groups menu bar

Clicking the Teams button opens the Teams client positioned in the General channel of the team. It can’t open any other channel.

I’m uncertain what value is delivered by the Teams button. If you use Outlook to open Microsoft 365 Groups, you’re likely using it to have email-based conversations instead of Teams chat-based conversations. It seems unreasonable to assume that you would want to switch between the two modalities in the same group. After all, Microsoft doesn’t support the Share to Teams functionality for group conversations that’s available for regular email. Apart from a manual cut and paste, the only way to get a group conversation from Outlook (or OWA) to Teams is to forward the message to the email address of a team channel.

Moving Easily Between Outlook and Teams

It could be argued that adding the Teams button is simply a case of Microsoft making it easier for customers to move between Outlook and Teams. It could be the case that the team has integrated apps that aren’t available to Outlook, like Planner, some SharePoint pages, and a couple of third-party apps. In that respect, it makes sense to have an easy way to jump from Outlook to Teams.

It seems more likely that the Teams button is Microsoft’s subtle way to convince people to move their conversations from Outlook to Teams. There’s logic underpinning that transition because Teams is a better place to hold many conversations, especially those involving multiple back-and-forth responses.

On the other hand, if email-based conversations are your thing and your group involves many external people (guests and non-guests), an Outlook-based group is a good way to get work done. Microsoft recently updated Outlook for Windows to make the unread count work like regular folders, so work is still being done to improve and smoothen Outlook groups. And that’s the way it should be. Although Teams has 115 million daily active users, a lot of email is still sent inside and out of Office 365.


We cover both Teams and Outlook Groups in the Office 365 for IT Pros eBook. And we use both to get real work done.

]]>
https://office365itpros.com/2020/11/20/outlooks-groups-menu-bar/feed/ 4 34889
Millions of Microsoft 365 Groups Fail Auto-Renewal Annually https://office365itpros.com/2020/10/16/microsoft-365-groups-statistics/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-groups-statistics https://office365itpros.com/2020/10/16/microsoft-365-groups-statistics/#comments Fri, 16 Oct 2020 01:00:49 +0000 https://office365itpros.com/?p=31069
Microsoft 365 Groups by the Numbers (source: Microsoft)
Microsoft 365 Groups by the Numbers (source: Microsoft)

Like many others, I’ve been catching up on sessions delivered at the Microsoft Ignite 2020 virtual conference. The Microsoft 365 Groups developers delivered a good set of sessions, including a refreshed version of How Microsoft manages Microsoft 365 Groups that’s well worth watching.

In the Groups roadmap session, an interesting statistic was reported by Venkat Ayyadevara, who said that 79% of Groups managed by an expiration policy are auto-renewed. This sounds good, but then my mind turns to why over a fifth of all groups are not renewed.

Figuring Out Groups Numbers

No one outside Microsoft knows how many Microsoft 365 Groups are in use today, so we need to do some inspired guesswork. Given that:

  • Groups are used by many applications to control membership, including Teams, Planner, Power BI, and SharePoint Online.
  • There are 258 million paid Office 365 seats and 75 million active Teams users (both Microsoft figures from April 2020). The fast adoption of Teams by Office 365 tenants is a significant driver for the number of groups in use.
  • Microsoft’s implementation spans over 350,000 groups alone. That’s approximately one for every user account. This is in line with the experience of many large on-premises Exchange deployments where the number of distribution lists often approached the number of mailboxes.
  • The Enterprise Mobility and Security suite has 147 million users, implying that most Office 365 enterprise users possess the necessary Azure AD Premium licenses needed for expiration policies.

If the number of enterprise Office 365 seats is around 175 million, we could guess that the number of Microsoft 365 groups is close to that number. And if 80% of enterprise users have EMS licenses, the number of groups which might be eligible for coverage by an expiration policy could be around 120 million. Not all tenants use expiration policies and not all tenants which do use expiration policies set them up to cover all groups, so let’s say that half of the groups (60 million) are covered by expiration policies.

The Success of Auto-renewal

Microsoft tells us that 79% of the groups auto-renew, or 47.4 million (goodness because administrators are saved processing this number of renewal requests). On the downside, 21% of groups are ineligible for auto-renewal, meaning that 12.6 million groups do not come up to the low bar of activity across group-connected workloads set for auto-renewal. One can surmise that these groups are created and quickly become stagnant and inactive. Email conversations don’t happen, documents aren’t uploaded to the group’s SharePoint Online document library, and if the group is team-enabled, it fails to attract any chat activity.

Should We Care?

At this point you could ask “who cares?” Apart from a small amount of SharePoint storage quota, the unrenewed groups don’t occupy any resources that a tenant might be charged for. There’s a minor annoyance that some disused groups might clutter up the GAL and slow down administrative processing such as reporting on Teams and Groups activity, but no more than that.

In the cloud era, you’re right. Microsoft pays whatever the cost is to keep the stagnant groups around until they are aged out by the expiration policy. We should only care if we reflect on why these groups are created in the first place. Were they created as the result of a managed approval process or in organizations where users have free rein over group creation? In either case, the 21% expiration figure is sufficiently high for tenant administrators to ask if users receive enough guidance about their creation and use. That’s something worth thinking about.


The Office 365 for IT Pros eBook covers Microsoft 365 Groups in detail from concepts to automation with PowerShell. Like all our content, it’s based on hard experience and it’s kept updated as knowledge develops.

]]>
https://office365itpros.com/2020/10/16/microsoft-365-groups-statistics/feed/ 1 31069
Change to Outlook Groups Displays High Unread Counts https://office365itpros.com/2020/09/24/change-outlook-groups-displays-high-unread-counts/?utm_source=rss&utm_medium=rss&utm_campaign=change-outlook-groups-displays-high-unread-counts https://office365itpros.com/2020/09/24/change-outlook-groups-displays-high-unread-counts/#comments Thu, 24 Sep 2020 01:00:00 +0000 https://office365itpros.com/?p=28377

Wow! Where Did All Those Unread Items Come From?

Last Tuesday, I checked for updates for the Microsoft 365 apps for enterprise (Office click to run) and duly downloaded the available update to upgrade to version 2009 (build 13231.20200). Nothing strange happened and the upgrade proceeded without any issues. I was a happy camper.

That is, until I noticed that the unread count for my Outlook Groups suddenly displayed much higher numbers (Figure 1). Usually these groups have a very low number of unread items, especially those marked as favorites because I check them at least once daily.

Outlook for Windows displays some high unread counts for Groups
Figure 1: Outlook for Windows displays some high unread counts for Groups

The History of Groups

The reason why this happens is clouded in history. When Microsoft introduced Office 365 Groups (now Microsoft 365 Groups) in November 2014, they were characterized as a new way for email-centric collaboration. Teams didn’t exist at that point and although Microsoft’s marketing muscle was pushing Yammer (bought in June 2012) as the future for collaboration and a replacement for email (that strategy really worked out), the bulk of interpersonal electronic collaboration occurred over email.

In the on-premises world, many Exchange organizations combined distribution lists with public folders to give people an archive for discussions. Groups introduced a group mailbox to host discussions and a shared calendar and came with a SharePoint Online team site for document storage, including a shared group OneNote notebook. Given that the bulk of work that had been migrated to Office 365 at that point was email, Groups looked pretty good. In April 2017, Groups (now called Groups in Outlook) had 10 million active users, or roughly 10% of the Office 365 user count at the time. The latest figure for Office 365 is 258 paid seats (April 2020). It’s unlikely that Outlook Groups have kept pace and now has 25 million active users, but it’s possible.

The collaboration landscape within Office 365 changed upon the general availability of Teams in March 2017. Since then, Teams has taken the lead and Groups have concentrated on a new mission of delivering a membership and access service to applications like Teams. Usage of Outlook Groups as a fulcrum for email-based collaboration is much less important to Microsoft now, but Groups are still actively used in this way in many Office 365 tenants.

Choosing a Simpler Unread Count Model for Groups

When Groups were added to Outlook in 2015, the developers decided not to use the standard item read/unread model as used in other mailbox folders like the Inbox. This model depends on the unread status of items and operates on a per-user basis. In other words, in a shared resource like a group inbox or public folder, each user has a separate unread count generated by the number of items they have not read in the folder.

Instead, the group developers chose a “more simple triage model for the groups conversations list, where all the conversations would be marked as seen as you moved away from the group.” Apparently, the decision was based on user feedback that many groups contain conversations unimportant to some members, so you couldn’t expect them to read everything. As implemented in Outlook, the group seen/unseen model allowed users to scan a group for new items and then set the unread count to zero once the user moves from the group. The new item count for a group then becomes the number of items delivered to the group since the last access by the user.

By comparison, new messages delivered to an inbox are personal and the mailbox owner is expected to deal with them. The new item count for the inbox is therefore very important for the mailbox owner and is adjusted up and down as the unread status for messages change (you can mark a read item as unread).

OWA and Outlook Mobile Use Normal Unread Counts

At the time, the developers accepted that the difference in how folders reported unread counts caused user confusion and said that they were working on implementing an item read/unread model for Groups. That model was implemented by OWA in early 2019 and is in use today (Figure 2).

OWA has used the read/unread model since 2019
Figure 2: OWA has used the read/unread model since 2019

For whatever reason (prioritization, lack of resources, more pressing features, etc.), Outlook desktop is a long way behind OWA in moving to the item read/unread model. The latest builds of Outlook have switched to the item read/unread model, which is the reason why the unread counts for my groups suddenly exploded from their normal low levels. Outlook Mobile has also used item unread counts since early 2019.

Resetting the Unread Count for an Outlook Group

Another piece of good news is that the Outlook developers have included a Mark All as Read option to reset the unread count for a group. Select the group you want to reset, right-click, and select the option. Processing to reset the unread status for items occurs in a background thread, so it doesn’t stop you working while the unread count is reset. Depending on the number of unread items in the group, the option can take a little while to complete.

Outlook's Mark All as Read option
Figure 3: Outlook’s Mark All as Read option

Unhappily, Outlook’s Mark All as Read option might not be able to update the status for all unread items. At least, it didn’t for me. My solution was to open the group with OWA and use its version of Mark All as Read, which worked flawlessly.

The good news is that as you open unread items in in a group using one client, the read status for the item and unread count for the group is updated and shown correctly across all Outlook clients.

Hindsight Always Best

The benefit of hindsight tells us that the decision of the Groups developers to go with the simpler read/unread model for their Outlook implementation was flawed. The change made in the other clients in 2019 is now showing up in Outlook desktop. A little preparation and user communication should be enough to get everyone over the shock of seeing elevated unread counts for their groups.


This one-time change will probably warrant a line or two in the Office 365 for IT Pros eBook. It’s an example of a small change that’s important for some users for a period. Once the change is done, it’s done. But change persists inside Office 365, which is why we keep updating the book.

]]>
https://office365itpros.com/2020/09/24/change-outlook-groups-displays-high-unread-counts/feed/ 3 28377
How to Archive Microsoft 365 Groups and Teams with PowerShell https://office365itpros.com/2020/07/03/archiving-microsoft-365-groups-teams-powershell/?utm_source=rss&utm_medium=rss&utm_campaign=archiving-microsoft-365-groups-teams-powershell https://office365itpros.com/2020/07/03/archiving-microsoft-365-groups-teams-powershell/#comments Fri, 03 Jul 2020 10:03:54 +0000 https://office365itpros.com/?p=9944

Keep Groups and Teams Online, But Stop User Access

Chapter 13 of Office 365 for IT Pros is where we tackle the subject of how to use PowerShell to manage Microsoft 365 Groups and Teams. For the last two versions, we’ve included a script to demonstrate how to archive a group at the end of its useful lifetime, such as when a project finishes.

The script works by removing all the current owners and members and replacing them with a single owner/member, which we’ll call the compliance manager. The script also removes the group from address lists and hides it from Exchange clients to make sure that users can’t browse to find it and updates the SMTP address and display name to show that the group is archived.

The idea is to keep the group and its resources online in a state where eDiscovery can still find information easily and the group can be resuscitated quickly if necessary. Teams has a menu option to archive a team by making it read-only. This is essentially the same, but the approach works for all groups.

Archiving Groups at the End of The Academic Year

We received a request through the Microsoft Technical Community for a script that could archive 600 teams at the end of an academic year. When you think about it, archiving the teams (groups) used for classes is something that schools and universities probably must do each year.

Everyone loves the chance to revisit old code (don’t they?). In response, we’ve updated the script (you can download a copy from GitHub) and will include it in the August update for the Office 365 for IT Pros (2021 edition) eBook.

Script Steps

The updated script:

  • Makes a collection of groups to be archived. The script searches for groups marked with “Archive” in the CustomAttribute1 property. This code could be replaced by reading in a set of groups from a CSV file using the Import-CSV cmdlet.
  • Replaces the current members and owners with a user identified in the script in the $AdminAccount variable. This might be an account created specially to manage archived groups.
  • Creates a new primary SMTP address of the group in the form OldAddress_archived@domain. Replacing the address stops email arriving to the group.
  • If your tenant uses Sensitivity Labels to control group settings, you need to define a sensitivity label to assign to archived groups. This should be a label that marks the group as private and restricts guests.
  • The group settings are updated to make it inaccessible (not discoverable) through Exchange clients and Teams.

Everything that the script does is logged and output to a CSV file (Figure 1 shows the output as piped to the Out-GridView cmdlet).

Details of archived Microsoft 365 Groups
Figure 1: Details of archived Microsoft 365 Groups

The script uses the Exchange Online PowerShell module and you must connect with a tenant admin account to be able to update group settings.

Extract, Review, and Process

An approach which might be easier to take when processing hundreds of groups (as in at the end of a school term or academic year) is to:

  • Extract details of groups and write them to a CSV file.
  • Review the CSV file in Excel and remove groups that you want to keep (not archive).
  • Use the updated CSV file as input to the script.

For example, this code finds groups and creates a CSV file for review:

# Find details of all groups in the tenant and export to CSV
$Groups = Get-UnifiedGroup -ResultSize Unlimited | Select DisplayName, Notes, DistinguishedName, Alias, PrimarySmtpAddress, SensitivityLabel
$Groups | Sort DisplayName | Export-CSV -NoTypeInformation c:\temp\GroupsForReview.CSV
Reviewing Groups and Teams in Excel
Figure 2: Reviewing Groups and Teams in Excel

After editing the CSV file (Figure 2) to remove the groups you don’t want to archive and saving the file, we can then replace the call to Get-UnifiedGroup in the script with:

$ArchiveGroups = Import-CSV c:\temp\GroupsForReview.CSV

The rest of the code in the script is unaltered.

Finding Inactive Groups and Teams for Archiving

If you’re not in an education environment, you could use the same approach to archiving Groups and Teams. In this case, you could run the Groups and Teams Activity Report to find underused or inactive groups to archive.

As with everything in PowerShell, you can update the code as you like to fit the circumstances of your tenant. Have fun!

]]>
https://office365itpros.com/2020/07/03/archiving-microsoft-365-groups-teams-powershell/feed/ 9 9944
Updates for Groups Management in Microsoft 365 Admin Center https://office365itpros.com/2020/06/19/groups-updates-microsoft365-admin/?utm_source=rss&utm_medium=rss&utm_campaign=groups-updates-microsoft365-admin https://office365itpros.com/2020/06/19/groups-updates-microsoft365-admin/#comments Fri, 19 Jun 2020 07:57:41 +0000 https://office365itpros.com/?p=9661

Changes Slip Through When You’re Not Watching

Microsoft recently updated the Microsoft 365 admin center with several useful changes to improve the Groups section of the portal. Most of the changes relate to Office 365 Groups (sorry, now Microsoft 365 Groups). It’s entirely possible that these changes have escaped your attention, so let’s cover them briefly.

Restore Deleted Groups

Given the popularity of Microsoft 365 Groups as the membership service for applications like Teams, Yammer, and Planner, it’s inevitable that some mistakes will be made when removing groups or that an important group will be allowed to expire. The ability to restore deleted Microsoft 365 groups was first introduced through PowerShell cmdlets in early 2017. Soon afterwards, the feature appeared in the Exchange admin center.

Now you can restore Microsoft 365 groups in the Microsoft 365 admin center. Go to Groups and open the Deleted groups section. Any groups that have not exceeded their 30-day soft-deleted retention period are listed (Figure 1). Deleted groups of other types, like distribution groups, don’t show up here and can’t be recovered using this method.

Listing deleted groups in the Microsoft 365 admin center
Figure 1: Listing deleted groups in the Microsoft 365 admin center

To restore a group, select it and either:

  • Use the Restore group option in the group header.
  • Click the group name to bring up the details pane, which has some basic information about the (display name, description, and email addresses) and click the Restore group button.

Both options perform the same processing to restore the deleted group. After a short delay, the group object is restored in Azure AD and begins the process of notifying the associated workloads to reconnect. It takes a little while for resources like the SharePoint site, a plan, or a team to be reconnected, but eventually everything comes together.

Selecting Licensed Teams Owners

Restoring deleted groups is a relatively big feature. A smaller, but still nice, feature is the way that the Add group wizard checks and displays if assigned groups owners have Teams licenses (Figure 2). Why is this important? Well, if you add someone who isn’t licensed for Teams as a group owner and then team-enable the group, that owner won’t be able to manage the team. And ownerless teams are bad.

Making sure that assigned group owners can use Teams
Figure 2: Making sure that assigned group owners can use Teams

The nagging doubt in my mind is that this feature might not work so well in very large tenants when many accounts can be nominated as group owners, but I’m sure this has been tested.

Improvements in Groups Section

The Groups section in the Microsoft 365 admin center has been nicely refreshed. Some of the changes have been around for a while, but I’ll note them here. In Figure 3 we see:

  • The ability to edit the name and description of a selected group. This is a shortcut to calling the group details pane where you make the changes.
  • Edit email addresses. Those of us who like PowerShell would run Set-UnifiedGroup to do this, but normal people will find it much easier to change the primary SMTP address of a group or add new proxy email addresses here.
  • See the set of Teams-enabled groups. The Teams icon tells all.
  • Filters to show different kinds of groups. We sometimes forget the humble distribution list, but these objects are managed here too.
  • The sync status property tells you where a group is homed. In this case they’re all in the cloud.
Improved UI in the Groups section of the Microsoft 365 admin center
Figure 3: Improved UI in the Groups section of the Microsoft 365 admin center

Usefully, you can export the filtered set of groups to a CSV file. This kills off many PowerShell scripts written to do the same job (using cmdlets or Graph calls), but there’s nothing wrong with that.

There’s nothing earth shattering in anything that Microsoft has done and it’s likely that the changes help them reduce the number of support calls that flow in to ask how to do these operations. Changes that help both Microsoft and tenants are a good thing and collectively the changes make group management just that bit easier for those who don’t manage these objects very often. Best of all, Restore Deleted Groups is useful for even hard-bitten professionals. I’ll let you decide if you fall into that category.


Changes like those described in this post add value but they can slip by without you noticing. This is why we monitor what’s going on inside Office 365 and update the Office 365 for IT Pros eBook to make sure that our subscribers are always in the know.

]]>
https://office365itpros.com/2020/06/19/groups-updates-microsoft365-admin/feed/ 1 9661
How to Find Guests in Microsoft 365 Groups and Teams Where Guests are Prohibited https://office365itpros.com/2020/06/09/finding-guests-office-365-groups-prohibited/?utm_source=rss&utm_medium=rss&utm_campaign=finding-guests-office-365-groups-prohibited https://office365itpros.com/2020/06/09/finding-guests-office-365-groups-prohibited/#comments Tue, 09 Jun 2020 13:45:53 +0000 https://office365itpros.com/?p=8378

Newly Applied Label Doesn’t Remove Existing Guests

Let’s assume that you’ve decided to replace the text-only classifications defined in the Azure Active Directory policy for Groups with Office 365 Sensitivity Labels. All is well, and you might even have used the PowerShell code explained in this article to do the job and all your teams, groups, and sites are now labelled properly.

You then consider an issue that will be dealt with differently from tenant to tenant: Assigning a label to a container that limits guest members does not affect access for existing guests. In other words, assigning a label to block guest access to a team, group, or site does precisely zero to remove any existing guests. They remain in the group membership and their access to group resources continues unimpeded.

Checking for Existing Guests

If this is a concern and you want to be sure that containers marked with a high degree of sensitivity do not have guest members, you should check the membership of these groups and remove any guests. This is simple to do with PowerShell. In this example, we find the groups stamped with a specific sensitivity label that have guest members and report who those guests are.

The code is straightforward.

  • Fetch all Microsoft 365 Groups stamped with a label that prohibits guest access.
  • Check the membership of each group to see if any guests are present.
  • Report any guests that are found.
CLS; Write-Host "Finding confidential Microsoft 365 Groups..."
$Groups = Get-UnifiedGroup | ? {$_.SensitivityLabel -eq "1b070e6f-4b3c-4534-95c4-08335a5ca610" -and $_.GroupExternalMemberCount -gt 0} 
If (!$Groups.Count) { Write-Host "No Microsoft 365 Groups found with that label"}
  Else {
     $Report = [System.Collections.Generic.List[Object]]::new(); $NumberGuests = 0
     Write-Host "Now examining the membership of" $Groups.Count "groups to find guests..." 
     ForEach ($Group in $Groups) {
       Write-Host "Processing" $Group.DisplayName
       $Users = Get-UnifiedGroupLinks -Identity $Group.Alias -LinkType Members
       ForEach ($U in $Users) {
         If ($U.Name -Match "#EXT#" -and $U.Name -NotLike "*teams.ms*") {
## Remember to edit the string to make sure it’s your tenant name…
            $CheckName = $U.Name + "@EditMeTenantName.onmicrosoft.com"
            $User = (Get-AzureADUser -ObjectId $CheckName).DisplayName 
            $ReportLine = [PSCustomObject]@{
               Email           = $U.Name
               User            = $User
               Group           = $Group.DisplayName
               Site            = $Group.SharePointSiteURL }
            $Report.Add($ReportLine)
            $NumberGuests++ }         
}}}
Write-Host "All done." $NumberGuests "guests found in" $Groups.Count "groups"

$Report | Sort Email | Out-GridView

The output is in a PowerShell list that we can review through the Out-GridView cmdlet (Figure 1) or by writing to a CSV file. After finding guests in groups where they are now prohibited, you can make the decision to leave them in place or remove them from the membership.

Guest users found in groups assigned a sensitivity label that blocks guest access
Figure 1: Guest users found in groups assigned a sensitivity label that blocks guest access

A more developed version of the script would first figure out which labels block guest access and then loop through all groups with these labels to create a report for all such labels. We explain how in the Office 365 for IT Pros eBook.

It’s worth noting that, if necessary, a global administrator can add a guest to a group even when blocked by policy.


Thinking about problems like this is what drives the Office 365 for IT Pros writing team to continually improve and refine our text about different aspects of Office 365. It’s why we issue a completely new book to our subscribers every month. Join us by taking out a subscription.

]]>
https://office365itpros.com/2020/06/09/finding-guests-office-365-groups-prohibited/feed/ 2 8378
How to Report Microsoft 365 Groups Deletions Using the Audit Log https://office365itpros.com/2020/01/30/microsoft-365-groups-deletions/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-groups-deletions https://office365itpros.com/2020/01/30/microsoft-365-groups-deletions/#comments Thu, 30 Jan 2020 09:15:21 +0000 https://office365itpros.com/?p=7050

Deletions for Microsoft 365 Groups reported by PowerShell

Soft and Hard Deletions for Microsoft 365 Groups

Yesterday’s post addressed the topic of how to report the removals of Teams from an Office 365 tenant. This led to the logical question of how to know when Microsoft 365 Groups (and Teams) are removed because they expire due to the settings in the tenant group expiration policy.

The simple answer is that the Office 365 audit log captures details for all group deletions, including when the group expiration policy determines that a group is expired and puts the group into a soft-deleted state. Thirty days later, a background process permanently removes the soft-deleted group unless an administrator restores it beforehand. As is often the case with Office 365 technology, the simple answer hides some complexity, which we’ll dive into now.

Understanding Group Deletion Records

When you examine the records for group deletions stored in the Office 365 audit log, you find three conditions to handle:

  1. A user deletes a Microsoft 365 group, team, or team-enabled site.
  2. Microsoft background processes examine groups that come within the scope of the expiry policy and remove expired groups where no activity has occurred to force automatic renewal. These events are logged with a user identifier like ServicePrincipal_1342cefb-7a89-4ee2-af90-c8443053e1e8.
  3. Both 1 and 2 put groups into a soft-deleted state. After 30 days, another background process called the Microsoft Online services Garbage Collector permanently removes these groups and all attached resources.

With these conditions in mind, we can create a PowerShell script to extract records from the Office 365 audit log and parse the records to extract some useful information. Here’s the script I created. It looks similar to the one discussed yesterday with some extra processing to handle the three conditions.

CLS; Write-Host "Searching Office 365 Audit Records to find auto-expired group deletions"
$StartDate = (Get-Date).AddDays(-90); $EndDate = (Get-Date) 
$PolicySoftDeletes = 0; $HardDeletes = 0; $UserSoftDeletes = 0
$Records = (Search-UnifiedAuditLog -Operations "Delete Group" -StartDate $StartDate -EndDate $EndDate -ResultSize 1000)
If ($Records.Count -eq 0) {
    Write-Host "No audit records for group deletions found." }
Else {
    Write-Host "Processing" $Records.Count "team deletion audit records..."
    $Report = [System.Collections.Generic.List[Object]]::new() # Create output file 
    # Scan each audit record to extract information
    ForEach ($Rec in $Records) {
      $AuditData = ConvertFrom-Json $Rec.Auditdata
      $User = $AuditData.UserId.Split("_")[0]    
      $GroupName = $Auditdata.Target | ? {$_.Type -eq 1} | Select -ExpandProperty Id
          Switch ($User)
          {
            "Certificate"  { # Hard delete of a group 
                 $HardDeletes++ 
                 $Reason = "Group permanently removed" 
                 $User = $User + " (System Process)" }
            "ServicePrincipal" { #Soft delete - expiration policy 
                 $PolicySoftDeletes++
                 $Reason = "Group removed by expiration policy"
                 $User = $User + " (System Process)" }
            default { #Regular delete by a user 
                 $UserSoftDeletes++ 
                 $Reason = "User deleted group" }
          }       
          $ReportLine = [PSCustomObject] @{
           TimeStamp = Get-Date($AuditData.CreationTime) -format g
           User      = $User
           Group     = $GroupName 
           Reason    = $Reason
           Action    = $AuditData.Operation
           Status    = $AuditData.ResultStatus }        
      $Report.Add($ReportLine) }
}
Cls
Write-Host "All done - Group deletion records for the last 90 days"
Write-Host "User deletions:"     $UserSoftDeletes
Write-Host "Policy deletions:"   $PolicySoftDeletes
Write-Host "Group hard deletes:" $HardDeletes
Write-Host "----------------------"
$Report | Sort Group, Reason -Unique | Select-Object Timestamp, Group, Reason, User | Out-GridView

If you examine the results as piped through the Out-GridView cmdlet (Figure 1), you’ll see examples where a record captures a soft-delete by user or policy followed 30 days later by a permanent removal.

Deletion records for Microsoft 365 Groups extracted from the audit log
Figure 1: Out-GridView shows group deletion records

Once you’re happy with the data generated, it’s easy to include an extra line of code to output a CSV file using the Export-CSVFile cmdlet or use the ImportExcel module to export the data to an Excel worksheet. Once again, access to this kind of information proves that PowerShell and the audit log are a very flexible tool to understand what happens behind the scenes in Microsoft 365.


Need more insight into mining the valuable information stored the Office 365 audit log? We’ve got a complete chapter covering auditing and reporting in the Office 365 for IT Pros eBook.

]]>
https://office365itpros.com/2020/01/30/microsoft-365-groups-deletions/feed/ 1 7050
How to Generate an Activity Report for Microsoft 365 Groups and Teams https://office365itpros.com/2020/01/14/microsoft-365-groups-teams-report/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-groups-teams-report https://office365itpros.com/2020/01/14/microsoft-365-groups-teams-report/#comments Tue, 14 Jan 2020 09:54:37 +0000 https://office365itpros.com/?p=6653

Find Obsolete Microsoft 365 Groups and Teams

May 11: Microsoft is deprecating the TechNet Gallery in June 2020. The script can now be downloaded from GitHub.

Oct 14: Microsoft updated the location of the Teams compliance records used to check how active a team is. Version 4.7 or later of the script handles this issue.

I wrote the first version of a script to analyze the activity in Microsoft 365 Groups (then Office 365 Groups) and Teams to identify obsolete groups in 2017. The script is described in this Petri.com article and is reasonably popular. I keep an eye on the feedback from people who run the script and update the script as time goes by. You can download the latest version from GitHub. The latest version is V4.8 dated 16 December 2020.

Update: A Graph-based version of the script is available in GitHub (5.4). This version is much faster at processing Microsoft 365 Groups and Teams and is now the recommended code and the base for future development. See this post for details.

The basic idea is to analyze the Microsoft 365 Groups in a tenant to find underused groups or simply understand the level of activity across groups. The script looks at the level of activity in:

  • Conversations stored in the Inbox of group mailboxes (for Outlook Groups).
  • Documents (in SharePoint document libraries belonging to the groups)
  • Chats (for Teams-enabled Groups). In fact, the script checks the compliance records logged in the group mailbox for conversations in channels belonging to the Teams.

Checking different aspects of individual groups isn’t fast. One tenant tells me that it takes 17 hours to process 5,300 groups… but this isn’t a report that you’ll run daily. It’s more like a monthly or quarterly activity.

Script Outputs

The script records the data for each group in a PowerShell list. Eventually, after processing all the groups, the script outputs an HTML report and a CSV file. The script also assigns a status of Pass, Warning, or Fail to each group depending on the level of detected activity. The determination of the status is entirely arbitrary and should be adjusted to meet the needs of your organization.

The output from the Microsoft 365 Groups and Teams activity report
Figure 1: The output from the Microsoft 365 Groups and Teams activity report

At the bottom of the report you’ll see a summary like this:

Report created for: tenant.onmicrosoft.com
Number of groups scanned: 182
Number of potentially obsolete groups (based on document library activity): 120
Number of potentially obsolete groups (based on conversation activity): 129
Number of Teams-enabled groups : 65
Percentage of Teams-enabled groups: 35.71%

----------------------------------------------------------------------------

Reviewing the report should help you find the Microsoft 365 Groups and Teams that are not being used. These groups are candidates for removal or archival.

You can view a screen capture video showing how to run the script here.

Recent Improvements

Scripts that evolve over time can often do with a rewrite from scratch. However, I don’t have the time for that. but I do make changes that I think are useful. Here are some recent changes.

  • New tests to see if the SharePoint Online and Teams modules are loaded. In particular, the Teams check took far too long because the cmdlets in this module are slow. In fact, the Get-Team module is so slow that we don’t use it from V4.3 onwards. Using Get-UnifiedGroup with a filter is about twice as fast. We might revisit this point as new versions of the Teams PowerShell module appear. See note above about the Graph-based version of the script.
  • Use a PowerShell list object to store the report data. This is much faster than an array, especially when you might want to store data for thousands of groups. This was one of the performance tips received after publishing a post about how we write PowerShell and it makes a real difference.
  • Use Get-Recipient instead of Get-UnifiedGroup to create a set of group objects to process. Get-Recipient is much faster than Get-UnifiedGroup when you need to create a list of mail-enabled objects like Office 365 Groups. V5.0 and later replaces these calls with Graph API commands.
  • Output the storage consumed by the SharePoint site belonging to each group.
  • Handle groups that have no conversations in the group mailbox more elegantly. Groups used by Teams are often in this situation.

Test Before Deployment

As always, test any PowerShell code downloaded from the web, even from sources like GitHub, before introducing it to a production system. The code as written needs some extra error handling to make it as robust as it could be, but I’ve left that to the professionals as people tend to have their own way of approaching this issue.


The Office 365 for IT Pros eBook includes many valuable tips for writing PowerShell scripts to interact with Office 365 Groups and Teams. Subscribe to gain benefit from all that knowledge!

]]>
https://office365itpros.com/2020/01/14/microsoft-365-groups-teams-report/feed/ 140 6653
How to Report Old Guest Accounts and Their Membership of Microsoft 365 Groups (and Teams) https://office365itpros.com/2019/10/15/report-old-guest-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=report-old-guest-accounts https://office365itpros.com/2019/10/15/report-old-guest-accounts/#comments Tue, 15 Oct 2019 08:20:05 +0000 https://office365itpros.com/?p=5185

A Proliferation of Guest User Accounts Within Tenants

Updated July 19. 2023

Azure Active Directory Guest User Accounts are terrifically useful in terms of allowing people outside your Microsoft 365 tenant to access resources inside the tenant. Applications like Teams, SharePoint Online, Planner, and Outlook Groups use the Azure B2B Collaboration framework to create new guest accounts to share information, whether it’s the membership of a team or access to a shared document or folder.

Guest accounts might be needed for to share just one document. Old guest accounts can accumulate over time. A regular spring cleaning is needed to ensure that you detect old guest accounts that are possibly no longer needed.

The Problem of Deciding When to Leave

As always, the problem is to decide when a guest account should be removed. Left by themselves, guest accounts will remain in the tenant directory because neither Office 365 nor Azure Active Directory include an automated method to clean up guests past their best-by date. One approach is to review guest accounts that are older than a certain age and look for evidence to indicate if they should be removed.

For example, you might decide that membership of multiple Microsoft 365 groups (aka Office 365 groups) is sufficient reason to keep guest accounts. The logic here is that these memberships give people access to Teams (conversations), Outlook Groups (conversations delivered via email), and Planner (group tasks). Therefore, if we write a script to scan for guest accounts older than x days and then check if these accounts are members of groups, we should have some evidence upon which to base a decision to remove or keep.

PowerShell Script to Highlight Old Guest Accounts and their Group Membership

The script below does the following:

  • Connects to the Graph with the Connect-MgGraph cmdlet.
  • Finds all guest accounts in the tenant using the Get-MgUser cmdlet.
  • Checks each guest to discover its age using the account creation date.
  • If the guest account is older than 365 days, we look for its group membership by running the Get-MgUserMemberOf cmdlet and report the display names of any groups found.
  • Checks the last sign-in activity for the account. This could be an important indicator that the account is active.
  • Writes the discovered information out to an array.
  • After all guest accounts are processed, the script writes the contents of the array containing information about old guest accounts to a CSV file.

Some example code is shown below. The latest version of the script is available on GitHub and is the version which you should download and use. The latest version runs with the Microsoft Graph PowerShell V2 cmdlets.

Remember that you might want to update the code to add error handling and do whatever testing is necessary before running the script against your production tenant.

# Script to find Old Guest Accounts in an Office 365 Tenant that are older than 365 days and the groups they belong to
# Find guest accounts

Write-Host "Finding Guest Accounts..."
[Array]$GuestUsers = Get-MgUser -Filter "userType eq 'Guest'" -All -Property Id, displayName, userPrincipalName, createdDateTime, signInActivity `
    | Sort-Object displayName
$i = 0; $Report = [System.Collections.Generic.List[Object]]::new()
# Loop through the guest accounts looking for old accounts 
CLS
ForEach ($Guest in $GuestUsers) {
# Check the age of the guest account, and if it's over the threshold for days, report it
   $AccountAge = ($Guest.CreatedDateTime | New-TimeSpan).Days
   $i++
   If ($AccountAge -gt $AgeThreshold) {
      $ProgressBar = "Processing Guest " + $Guest.DisplayName + " " + $AccountAge + " days old " +  " (" + $i + " of " + $GuestUsers.Count + ")"
      Write-Progress -Activity "Checking Guest Account Information" -Status $ProgressBar -PercentComplete ($i/$GuestUsers.Count*100)
      $StaleGuests++
      $GroupNames = $Null
      # Find what Microsoft 365 Groups the guest belongs to... if any
     [array]$GuestGroups = (Get-MgUserMemberOf -UserId $Guest.Id).additionalProperties.displayName
     If ($GuestGroups) {
        $GroupNames = $GuestGroups -Join ", " 
     } Else {
        $GroupNames = "None"
     }
  
#    Find the last sign-in date for the guest account, which might indicate how active the account is
     $UserLastLogonDate = $Null
     $UserLastLogonDate = $Guest.SignInActivity.LastSignInDateTime
     If ($Null -ne $UserLastLogonDate) {
        $UserLastLogonDate = Get-Date ($UserLastLogonDate) -format g
     } Else {
        $UserLastLogonDate = "No recent sign in records found" 
     }

     $ReportLine = [PSCustomObject][Ordered]@{
           UPN               = $Guest.UserPrincipalName
           Name              = $Guest.DisplayName
           Age               = $AccountAge
           "Account created" = $Guest.createdDateTime 
           "Last sign in"    = $UserLastLogonDate 
           Groups            = $GroupNames }     
     $Report.Add($ReportLine) }
} # End Foreach Guest

Sample Report File

The output CSV file is shown (somewhat obscured to protect the names of the guilty) in Figure 1. Any guest that isn’t a member of at least one Microsoft 365 group is a potential delete target. As you can see from the created column, it’s easy for old and stale guest accounts to linger on unless you clean them up from time to time.

 Reporting old guest user accounts and their group membership
Figure 1: Reporting old guest user accounts and their group membership

Details of a more comprehensive report of membership of Microsoft 365 Groups including both tenant and guest members and summary details are explained in this article.


We have lots of other PowerShell examples of how to manage Azure Active Directory guest users and other Office 365 objects in the Office 365 for IT Pros eBook.

]]>
https://office365itpros.com/2019/10/15/report-old-guest-accounts/feed/ 30 5185
How to Report the SharePoint URLs for Teams https://office365itpros.com/2019/03/27/finding-sharepoint-urls-teams/?utm_source=rss&utm_medium=rss&utm_campaign=finding-sharepoint-urls-teams https://office365itpros.com/2019/03/27/finding-sharepoint-urls-teams/#comments Wed, 27 Mar 2019 12:09:50 +0000 https://office365itpros.com/?p=2224

Every Team has a SharePoint Site (Collection)

Updated: 15 February 2021

As you probably all know, Office 365 provisions every the group created for each team with a SharePoint site collection. Or rather site, because Microsoft seems to be moving away from referring to collections, possibly because the vast majority of collections created today come from Microsoft 365 Groups and Teams and therefore hold just one site.

In any case, a question posed by Syskit asked how to retrieve the associated SharePoint URLs for teams-enabled Microsoft 365 Groups. The article offered the suggestion that you could run the Get-UnifiedGroup cmdlet as follows:

Get-UnifiedGroup | Select DisplayName, SharePointSiteUrl

The big downside with this approach is that a) Get-UnifiedGroup is an “expensive” (slow) cmdlet and b) you return all Microsoft 365 Groups and not the ones enabled for Teams.

Using Get-Team to Find Teams

To be fair, the article was written in November 2018 and doesn’t reflect the state of the art ever since Microsoft delivered Version 0.9.5 of the Teams PowerShell module (the latest version is 1.1.16). The Get-Team cmdlet is the way to return the set of known teams in a tenant. Here’s what we can do:

$Teams = (Get-Team |Select GroupId, DisplayName, Alias)
ForEach ($T in $Teams) {
   $SPOURl = (Get-UnifiedGroup -Identity $T.GroupId | Select -ExpandProperty SharePointSiteURL)
   Write-Host "URL for the" $T.DisplayName "team is" $SPOURL "and the group mailbox alias is" $T.Alias "with email address" $T.PrimarySmtpAddress }

The code is simple. Create a set of teams and loop through the set to retrieve the SharePointSiteURL for each team. In fact, Get-UnifiedGroup returns three URLs for SharePoint:

  • SharePointSiteURL: The root of the site.
  • SharePointDocumentsURL: The URL for the default document library created in the site. Each channel in the team has a folder in this library, starting with General for the default channel.
  • SharePointNotebookURL: The URL for the shared OneNote notebook belonging to the Office 365 Group/team. Some organizations prefer to replace the Teams Wiki with OneNote.

Even Faster Code

Time moves on and we have better ways of reporting the information. As described in this article, here’s how we could do the job by using a single call to the Get-UnifiedGroup cmdlet.

$Groups = Get-UnifiedGroup -Filter {ResourceProvisioningOptions -eq "Team"} -ResultSize Unlimited | Select ExternalDirectoryObjectId, DisplayName, SharePointSiteURL, Alias, PrimarySmtpAddress
ForEach ($T in $Groups) {
  Write-Host "URL for the" $T.DisplayName "team is" $T.SharePointSiteURL "group mailbox alias is" $T.Alias "and email address" $T.PrimarySmtpAddress }

Why Not Use the New SharePoint Admin Center?

Some have suggested that you can use the new SharePoint Admin Center to find the URLs. Well, the Admin Center certainly displays the URLs, but it doesn’t distinguish between team sites that belong to Microsoft Teams (team-enabled) or those used by an Office 365 Group that isn’t team-enabled (an Outlook or Yammer group). Although you can certainly generate and download a CSV file from the Admin Center containing the site URL (along with other details) for all the active sites in the tenant, you still must isolate which sites belong to Teams and which don’t. Using the technique above starts with a list of Teams and doesn’t go near the non-team enabled sites.


The set of PowerShell modules used with Office 365 change all the time. That’s a good reason to keep up with change by subscribing to the Office 365 for IT Pros eBook. Chapter 13 tells all about how to manage Office 365 Groups and Teams with PowerShell.

]]>
https://office365itpros.com/2019/03/27/finding-sharepoint-urls-teams/feed/ 8 2224
How to Post Information to Microsoft 365 Groups or Teams Channels https://office365itpros.com/2019/01/14/inbound-webhook-connector/?utm_source=rss&utm_medium=rss&utm_campaign=inbound-webhook-connector https://office365itpros.com/2019/01/14/inbound-webhook-connector/#comments Mon, 14 Jan 2019 10:22:30 +0000 https://office365itpros.com/?p=1382

Use Inbound WebHook Connector to Fetch and Post Service Information

Chapter 14 of the Office 365 for IT Pros eBook describes an example of how to post messages to an Office 365 group or a team channel using the inbound webhook connector. The inbound webhook connector is one of the standard connectors supported by Office 365. In this context, you can think of the webhook as a URI that Office 365 can use to find a target destination into which it posts messages. The posted messages are in JSON format and are called cards.

The process to connect a webhook to a team channel or Office 365 group is similar. Select the inbound webhook from the set of connectors available, give it a name and a replacement icon (if you want), and Office 365 responds with a webhook address. You can then copy that address and use it in scripts. The address will be something like this (the format changed in 2021):

https://outlook.office.com/webhook/5348781d-52a8-490f-b75b-a72e702114d1@b662313f-14fc-43a3-9a7a-d2e27f4f3478/IncomingWebhook/f286f53c0d244ebdba7fea50035f35df/eff4cd58-1bb8-4899-94de-795f656b4a18

Creating an inbound webhook for a Teams channel

inbound webhook connector
Figure 1: Creating an inbound webhook connector for a team channel

Using PowerShell to Post to the Inbound Webhook Connector

In any case, it’s pretty simple to write some PowerShell to fetch some data, format it, and post a card with the data to Office 365. The example we have in the book uses information about Office 365 service incidents, but anything you can fetch with PowerShell will do. The service incidents are gathered using the Office 365 service communications module for PowerShell.

The biggest challenge isn’t usually to find data to post. Instead, the usual issue that people run into is to format the JSON payload for the post. Microsoft has a message card “playground” to help people design actionable cards. We don’t need to go quite that far for the relatively simple and static card used in the demo, but you might like to explore the possibilities in the playground and see how the JSON structure is built out.

A Reader Suggestion

A reader suggested that instead of hand-crafting the card for each post, it would be simpler to create a template post and replace fields in the template before posting to the webhook. This seemed like a good idea, so we tried. The script below creates a template, then finds some service data, and updates the template to create a new JSON card before posting it to a team channel. Exactly the same code would work with a Microsoft 365 group if you change the target to a webhook pointing to the group.

$Notification = @"
    {
        "@type": "MessageCard",
        "@context": "https://schema.org/extensions",
        "summary": "Office 365 Notification",
        "themeColor": "0072C6",
        "title": "Office 365 Service Degradation",
         "sections": [
            {
            
                "facts": [
                    {
                        "name": "Service Incident:",
                        "value": "ID"
                    },
                    {
                        "name": "Start time:",
                        "value": "DATETIME"
                    },
                    {
                        "name": "Service:",
                        "value": "SERVICENAME"
                    },
                    {
                        "name": "Description:",
                        "value": "MESSAGE"
                    }
                ],
                "text": "Office 365 Service Problem"
            }
        ]
    }
"@

$Incidents = Get-SCEvent -EventTypes Incident -PastDays 2 -SCSession $O365ServiceSession | ? {$_.Status -eq "Service degradation"} | Sort StartTme -Descending | Select-Object Id, Status, StartTime, @{n='ServiceName'; e={$_.AffectedServiceHealthStatus.servicename}},
    @{n='Message';e={$_.messages[0].messagetext}}
ForEach ($Incident in $Incidents) {
   $IncidentID = $Incident.Id
   $IncidentStartTime = Get-Date ($Incident.StartTime) -format g
   $IncidentServiceName = $Incident.ServiceName
#   $IncidentMessage = out-string -InputObject $incident.message -Width 80
   $IncidentMessage = $Incident.Message
   $NotificationBody = $Notification.Replace("ID","$IncidentId").Replace("DATETIME","$IncidentStartTime").Replace("SERVICENAME","$IncidentServiceName").Replace("MESSAGE","$IncidentMessage")
   $Command = (Invoke-RestMethod -uri $TargetChannel -Method Post -body $NotificationBody -ContentType 'application/json')
}

Here’s what the cards look like after they are posted to the target channel.

How cards created using the webhook show up in a Teams channel using the Teams webhook connector
Cards for Office 365 service incidents posted in a team channel

The template method is probably better than the one we previously used, so we’re going to update Chapter 14 to include it. As I’ve often said, one of the joys of working on an eBook is that we can integrate new material and republish as often as we want – or in the case of Office 365 for IT Pros, monthly. And like the suggestion that lead to this update, we welcome comments and hints to improve our material.

PSTeams Module

If you’re interested in exploring the possibilities that exist in posting messages to Teams via the inbound webhook connection, you might consider the PSTeams module in GitHub, which is designed to help programmers build and send cards to Teams channels.


Read Chapter 14 in Office 365 for IT Pros for more information about using PowerShell to manage Office 365 Groups and Teams.

]]>
https://office365itpros.com/2019/01/14/inbound-webhook-connector/feed/ 8 1382
How to Move a Personal Microsoft Form to Become a Group Forms https://office365itpros.com/2018/11/17/move-group-form/?utm_source=rss&utm_medium=rss&utm_campaign=move-group-form https://office365itpros.com/2018/11/17/move-group-form/#respond Sat, 17 Nov 2018 13:57:59 +0000 https://office365itpros.com/?p=991

Forms Primer

Last month, I wrote about Forms and called the application an underappreciated part of Office 365. Yesterday, Microsoft posted a note to say that they’ve added the ability to move a personal form to Group Forms.

Moving Forms

To move a form, you select a form that you own, select Move from the ellipsis menu, and then choose a target Microsoft 365 group (see below). Once the target group is selected, click Move.

MoveForm
Selecting a target Office 365 group

A few seconds later, the selected form disappears from the My forms section and shows up under Group forms (you might have to select the target group to see the form). All of the members of the owning Microsoft 365 group are now able to work with the form and view the data gathered by the form when the form is released.

Groupform
The form in the Office 365 group

As Microsoft’s post makes clear, moving is a one-way operation that can’t be reversed. You also can’t move a form from one group to another.

The move option allows someone to develop a form out of sight of a group and then share the form with the group when it’s ready for review. It’s a small but good improvement.


We cover Forms in Chapter 9 of the Companion Volume for the Office 365 for IT Pros eBook. The companion volume is bundled with the EPUB/PDF edition but must be purchased separately if you want the companion volume for Kindle.

]]>
https://office365itpros.com/2018/11/17/move-group-form/feed/ 0 991
Teams Now Supports Dynamic Microsoft 365 Groups https://office365itpros.com/2018/11/13/dynamic-teams/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-teams https://office365itpros.com/2018/11/13/dynamic-teams/#comments Tue, 13 Nov 2018 12:32:05 +0000 https://office365foritpros.com/?p=963
Dynamic Teams

Work Done. Formal Support Announced

As noted in September, the support for dynamic Teams was not fully baked, even if the functionality worked. Well, Microsoft has completed the tweaking to make sure that dynamic Azure AD groups work as expected for Teams. The November post about new functionality in Teams says:

You can now create teams backed by Office 365 groups with a dynamic membership rule for which membership is managed dynamically based on user or device properties in Azure Active Directory. If a user or device satisfies a rule for a group, they are added as a member, and when they no longer satisfy the rule, they are removed.

This helps with scenarios like:

  • A hospital can create different teams for nurses and doctors that automatically reflect current staffing.
  • A university can create a team for all faculty within a college, including an adjunct faculty that changes frequently.”

The functionality to enable a dynamic group with a team is now supported in the latest version of the Teams desktop and browser client. Teams with dynamic membership signal owners with the warning banner shown above.

All of which is nice and the details of how to create and use dynamic groups with Teams are as reported previously. The kicker comes in the statement that “Dynamic membership rules in Azure Active Directory require an Azure AD Premium P1 license for each unique user that is a member of one or more dynamic groups.”

Extra Cost to Use

To put this another way, if you create a large team, say covering all the employees in a department or country, you must make sure that each one of those accounts has an Azure AD Premium P1 license. This isn’t an issue if you license Enterprise Mobility + Security (EMS) or one of the Microsoft 365 bundles as the license is included, or if you’ve bought the add-on for another of the Groups features that need the Azure AD Premium license, like imposing a naming convention for new groups.

However, if you don’t have the licenses already, being asked to shell out an extra $6/user/month might be a significant hurdle in overcome in making the decision whether to use dynamic groups.

Org-Wide Teams Don’t Use Dynamic Groups

Org-wide teams don’t use dynamic groups, so they don’t incur any need for extra licenses. However, these teams are currently limited to 10,000 members. If you need to create a larger team for org-wide communications up to the current 25,000 member limit for a team, you can use a dynamic group.


We cover using dynamic Teams in Chapter 13 of the Office 365 for IT Pros eBook. Other information is in Chapter 12, where we discuss the Azure AD policy for Groups and the features that need to be licensed.

]]>
https://office365itpros.com/2018/11/13/dynamic-teams/feed/ 2 963
Block Guest Members for Individual Microsoft 365 Groups and Teams https://office365itpros.com/2018/11/04/block-guest-access-to-teams/?utm_source=rss&utm_medium=rss&utm_campaign=block-guest-access-to-teams https://office365itpros.com/2018/11/04/block-guest-access-to-teams/#comments Sun, 04 Nov 2018 18:40:26 +0000 https://office365foritpros.com/?p=900

Block Guest Access to Teams with Group Settings

Updated 10-Oct-2023

Block guest access to Teams

By default, Microsoft 365 tenants can add guest users (people with accounts outside your tenant) to the membership of Microsoft 365 Groups (and Teams). In this article, we’ll explore how to block guests for individual groups and teams.

Tenants control guest access through the Azure Active Directory policy for Groups, which has two relevant settings:

  • AllowToAddGuests: Controls if group (or team) owners can add guest users to membership. The default is True.
  • AllowGuestsToAccessGroups: Controls if guest accounts can access resources through Office 365 Groups. The default is True.

Settings in the Entra ID directory policy for Microsoft 365 Groups can be changed through PowerShell. For instance, to block group owners from being able to add guests, you change the value of AllowToAddGuests to False. These command fetch the current settings, update the value, and update the policy (assuming that you have already created a tenant policy):

Connect-MgGraph -Scopes Group.Read.All, Directory.ReadWrite.All

$TenantSettings = Get-MgBetaDirectorySetting | Where-Object {$_.DisplayName -eq "Group.Unified"}
$Values = $TenantSettings.Values
($Values | Where-Object Name -eq 'AllowToAddGuests').Value = "false"
Update-MgBetaDirectorySetting -DirectorySettingId $TenantSettings.Id -Values $Values

Guests who are members of groups can continue to use their membership. The block simply stops group owners adding new guests. See this article for more information about configuring and managing the settings of the Entra ID policy for Microsoft 365 groups.

Block Guest Access to Teams and Groups on an Individual Basis

The normal course of events is to allow guest users for groups and selectively block access for specific groups that hold confidential information. It’s relatively easy to find and block access to selected groups. In the following example, the code:

  • Find the group policy template object for the tenant.
  • Finds a set of Microsoft 365 groups whose classification is set to “Secret.” You could use whatever filter you like to find the set of target groups.
  • Checks if an existing custom setting exists for a group. If one isn’t present, the code applies a new setting to block guest access. If one is, the setting is updated to block guest access.
$GroupTemplate = (Get-MgBetaDirectorySettingTemplate | Where-Object {$_.DisplayName -eq "Group.Unified.Guest"})
[array]$Groups = (Get-UnifiedGroup -ResultSize Unlimited | Where-Object {$_.Classification -eq "Secret"})

ForEach ($Group in $Groups) {
    $GroupSettings = Get-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId 
    If ($GroupSettings) {
       # Policy settings already exist for the group - so update them
       $GroupSettings = Get-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId
       Update-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId -TemplateId $GroupTemplateId `
         -GroupSettingId $GroupSettings.Id -Values (@{'name'='AllowToAddGuests';'value'='false'}) | ConvertTo-Json
       Write-Host ("External Guest accounts blocked for {0}" -f $Group.DisplayName) 
    } Else {
       # Settings do not exist for the group - so create a new settings object and update
       $Status = New-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId -TemplateId $GroupTemplateId `
           -Values (@{'name'='AllowToAddGuests';'value'='false'}) | ConvertTo-Json
       Write-Host ("New settings created and guests blocked for {0}" -f $Group.DisplayName) 
    }
}

The process of updating the directory setting to block guests in teams and groups happens when you apply a sensitivity label that blocks guests in teams and group.

Block Guest Access to Teams Individually Trumps Tenant Setting

Some people would like to reverse the process and block guest access to all groups except on a selective basis. This isn’t possible because the tenant-level block trumps settings at an individual group level. Once you set AllowToAddGuests to False at the tenant level, the policy stops any group owner from adding guests to group membership. Only administrators keep the ability to add guests, and they can only do so through an admin interface like running the Add-UnifiedGroupLinks cmdlet or updating group membership in the Microsoft 365 Admin Center or Entra ID admin center.

If you want to block access for guests to all but a small set of groups, you must leave AllowToAddGuests as True at the tenant level and then block all but the set of groups you want to allow guests to join.

Block Guest Access to Teams Through Sensitivity Labels

Generally available from June 2020, if you enable sensitivity labels for use with Groups, Teams, and Sites, the container settings in the labels can be used to block guest users. For example, you can have a label called Confidential which, when applied to a group, stops new guests being added. Existing guests aren’t removed, but you can find them as described here.


This is the kind of topic we cover in Managing Groups chapter of the Office 365 for IT Pros eBook. You can find a lot more about managing Groups there.

]]>
https://office365itpros.com/2018/11/04/block-guest-access-to-teams/feed/ 3 900
How to Control the Access of Guest Users to Confidential Information in Microsoft 365 Groups and Teams https://office365itpros.com/2018/08/16/guest-member-access-data/?utm_source=rss&utm_medium=rss&utm_campaign=guest-member-access-data https://office365itpros.com/2018/08/16/guest-member-access-data/#respond Thu, 16 Aug 2018 14:23:35 +0000 https://office365foritpros.com/?p=229
Guest member or external member access to information

Keeping Confidential Information Secret

Many SharePoint Online sites belonging to Microsoft 365 Groups and Teams hold confidential information that you might not want to share with guest members. When Microsoft first supported guest users for Office 365 Groups (now Microsoft 365 Groups) through Azure B2B Collaboration, the focus was on allowing guests to collaborate with tenant users through email and shared documents. Over time, apps like Teams and Planner included support for Azure B2B Collaboration and increased the amount of data available to guests. The issue often encountered now is how to keep organizational secrets when using collaborative applications.

Controlling Guest Access

In the early days of Office 365 Groups, there wasn’t much that group members could do to protect confidential information from guests. The Groups membership model is very simple. All members enjoy equal access to group content. This led to the creation of many additional groups to segregate information which needed to stay internal with that which could be shared externally.

As time went by, Microsoft introduced functionality to help. A range of options now exist:

  • Groups and Teams blocked against guest access. By restricting membership to tenant users, you create conditions for unfettered internal discussions and sharing. The block is imposed by updating the properties of the group in Azure AD to prevent group owners adding guest members. An administrator can update the group properties manually or the group can inherit the block when a group owner or administrator assigns a sensitivity label with the appropriate restriction to the group.
  • Inside a group with guest members, sensitivity labels with encryption can stop specific members (guests and perhaps some internal users) accessing sensitive documents in the group’s document library. Access rights defined in the label control who can interact with documents, and if guests aren’t assigned rights in a label, they cannot open any document assigned that label. This method is an effective block, but it does go against the general philosophy that members share equal access to group resources. Remember that document metadata is not encrypted by sensitivity labels, so guests will be able to see document titles and authors.
  • Private channels avoid the need to create a new group by establishing barriers to sharing within teams. Private channels are restricted to a subset of team members, such as only tenant users. Anything shared in a private channel is only available to the members of the channel, including documents stored in the SharePoint Online team site for the channel.
  • Shared channels don’t use Azure B2B collaboration, so don’t use guest members to control external access. Instead, tenants agree to federate using Azure AD cross-tenant access settings to allow users to work together in shared channels, including access to the SharePoint Online team sites used by the channels (just like private channels, each shared channel has its own site). Sensitivity labels placed on confidential documents can limit access to tenant members of shared channels.

With these options in mind, the best approach might be to stop external users getting into sensitive groups in the first place. As noted above, this is possible by blocking the ability of owners to add guests to their groups and teams at a group level or (for shared channels) with cross-tenant access settings. Administrators can always add guest members to teams and groups if necessary.

Controlling Group Policy Settings

The Azure Active Directory policy for Groups holds settings for how Microsoft 365 Groups behave in a tenant. One of those settings is AllowToAddGuests, which is True if the tenant allows guests to be members of groups, and False if you want to block guests. This policy covers all groups and is managed through PowerShell. If the tenant policy allows guests users, the properties of individual groups can be amended to block access to those groups.

Use Sensitivity Labels

Today, sensitivity labels are the best method to controlling external access to confidential information. A sensitivity label can hold several container management settings, including guest access and the external sharing capability for SharePoint. Applying the label to a group forces the inheritance of the container settings, and if the settings dictate a block for guest access, the group’s AllowAddGuests property is set to #False. Sensitivity labels are available in the Office 365 E3 and E5 plans.

Using Classifications to Block Guest Access

If you choose not to use sensitivity labels, you can use group classifications to mark confidential groups and update the properties of those groups to block guest access. A classification is a text value defined in the ClassificationList setting of the Groups policy. Classifications are visual markers intended to convey to users what kind of information a group holds. They do not affect how a group or team works, nor does a classification protect content or place any restriction on how that content is used. Adding or updating a new classification or removing a classification from the list does not affect classifications placed on existing groups.

Let’s assume that you define a “Secret” classification to mark confidential groups (or teams). After classifying the secret groups (using PowerShell or client UIs), we can use PowerShell to scan for and block guest access for those groups.

The first step in the example code creates a set of groups classified as “Secret.” The code then loops through each group to discover whether group-specific policy settings are in place. If so, the code updates the settings to block guest access. Groups that don’t have a policy setting are controlled by the tenant policy, so the first step is to create policy settings for the group. We can then update the setting to block guest access.

$GroupTemplate = (Get-AzureADDirectorySettingTemplate | ? {$_.DisplayName -eq "Group.Unified.Guest"})
$Groups = (Get-UnifiedGroup -ResultSize Unlimited | Where {$_.Classification -eq "Secret"})
 
ForEach ($Group in $Groups) {
    $GroupSettings = Get-AzureADObjectSetting -TargetType Groups -TargetObjectId $Group.ExternalDirectoryObjectId 
    if($GroupSettings) {
       # Policy settings already exist for the group - so update them
       $GroupSettings["AllowToAddGuests"] = $False
       Set-AzureADObjectSetting -Id $GroupSettings.Id -DirectorySetting $GroupSettings -TargetObjectId $Group.ExternalDirectoryObjectId -TargetType Groups
       Write-Host "External Guest accounts prohibited for" $Group.DisplayName 
    }
    Else
    {
       # Settings do not exist for the group - so create a new settings object and update
       $Settings = $GroupTemplate.CreateDirectorySetting()
       $Settings["AllowToAddGuests"] = $False
       New-AzureADObjectSetting -DirectorySetting $Settings -TargetObjectId $Group.ExternalDirectoryObjectId -TargetType Groups
       Write-Host "External Guest accounts blocked for"$Group.DisplayName 
    }
}

To check that the block for guest access is in place, we can create a list of the groups blocked from having guest members. To do this, run the Get-UnifiedGroup cmdlet to check the AllowAddGuests property, which is $False if the group is blocked. For example, this command reports the display names and classification for all blocked groups. Remember that the block works for all clients that populate group membership, including Teams.

Get-UnifiedGroup -ResultSize Unlimited | ? {$_.AllowAddGuests -eq $False } | Format-Table DisplayName, Classification

It’s critical to realize that applying a block on guests to a group does nothing to remove existing guests. If you want to eject existing guests, you need to do that separately.

Multiple Secret-Keeping Techniques

Multiple approaches are available to block guests from accessing content shared in Teams and Groups. The most fundamental is to block guest access completely, but if guests are already present, consider using Private channels in Teams or limit access to confidential documents with sensitivity labels and encryption.

]]>
https://office365itpros.com/2018/08/16/guest-member-access-data/feed/ 0 229
Use the New-UnifiedGroup Cmdlet to Create Microsoft 365 Groups with Multiple Users https://office365itpros.com/2018/08/10/new-unifiedgroup-multipleiowners/?utm_source=rss&utm_medium=rss&utm_campaign=new-unifiedgroup-multipleiowners https://office365itpros.com/2018/08/10/new-unifiedgroup-multipleiowners/#comments Fri, 10 Aug 2018 21:26:53 +0000 https://office365-ebook.com/?p=149

Confusion About How to Add Multiple Users with New-UnifiedGroup

Updated 22-Dec-2023

The New-UnifiedGroup cmdlet from the Exchange Online management module creates a new Microsoft 365 Group). In the past, Microsoft’s documentation included the statement that:

You can specify multiple owners separated by commas.

Microsoft has since removed the statement from its documentation but the fact remains that people sometimes still need to nominate multiple owners when they create Microsoft 365 groups. This article covers how to accomplish that goal.

Creating Multiple Owners with New-UnifiedGroup

Many Exchange Online cmdlets accept an array of values as input for a parameter. If this was true for the New-UnifiedGroup cmdlet, it would mean that you could pass an array containing the names of multiple group owners when creating a new Microsoft 365 group. For example:

New-UnifiedGroup -Alias MyGroup -DisplayName "My Group" -Owner "Tony@Office365itpros.com", "Paul@Office365itpros.com"

But you can’t. PowerShell responds with:

New-UnifiedGroup: Cannot process argument transformation on parameter ‘Owner’. Cannot convert value “System.Collections.Generic.List1[System.String]” to type “Microsoft.Exchange.Configuration.Tasks.RecipientIdParameter”. Error: “Object of type ‘System.Collections.Generic.List1[System.String]’ cannot be converted to type ‘Microsoft.Exchange.Configuration.Tasks.RecipientIdParameter’.”

Error adding multiple owners with the New-UnifiedGroup cmdlet
Figure 1: Error adding multiple owners with the New-UnifiedGroup cmdlet

The error is unexpected, not only because it goes against the grain of “normal” Exchange Online cmdlet processing but also because you can pass the names of multiple members when creating a new Microsoft 365 group. It appears that the code used to create owners is different to that used for members. This is understandable in a way because before someone can be a group owner, they must first be added as a group member.

The ManagedBy Workaround

Fortunately, two simple workarounds exist. One is so-so, the other is much better. The first is to add multiple owners by specifying them in the ManagedBy parameter. Here’s an example:

New-UnifiedGroup -Alias "Banking.Ivestigations" -DisplayName "Banking Investigations" -Owner Tony.Redmond -ManagedBy Jessica.Chen, Tony.Redmond
Add-UnifiedGroupLinks -Identity "Banking.Ivestigations" -Links Jessica.Chen, Tony.Redmond -LinkType Members

You can see that I have specified both the Owner and ManagedBy parameter. If you don’t pass a value in Owner, Exchange Online sets the signed in user as the group owner along with the people specified in ManagedBy (a property inherited from distribution lists). The net result is that Exchange Online adds three group owners. However, Exchange Online doesn’t add the people specified in the ManagedBy property as group members, which is why I use the Add-UnifiedGroupLinks cmdlet to add those users after the creation of the new group. It is easy to forget this step and create a situation where you have multiple group owners who are not group members. This is acceptable to Entra ID and won’t cause an immediate problem, but it’s not the way that Microsoft 365 Groups are designed to work and that’s why I don’t recommend using this approach.

The Add-UnifiedGroupLinks Workaround

The second (and recommended) method is to create the new group with New-UnifiedGroup and then add as many owners as you want afterwards with Add-UnifiedGroupLinks. Just make sure that the people you add as owners are first added as members because a group owner has to be a member before they can be an owner. In this example, I declare the set of group owners in an array and then pass the array to the Add-UnifiedGroupLinks cmdlet:

[array]$Owners = "Ken.Bowers", "Michelle.Dubois", "Andy.Ruth", "Brian.Weakliam"
Add-UnifiedGroupLinks -Identity MyGroup -LinkType Member -Links $Owners
Add-UnifiedGroupLinks -Identity MyGroup -LinkType Owner -Links "$Owners

The values supplied to the Add-UnifiedGroupLinks cmdlet must be something that Exchange Online can resolve. This can be a mailbox alias, primary SMTP address, external directory object id (the identifier for the user’s Entra ID account) display name, or even a distinguished name.


Learn about using Exchange Online, Microsoft 365 Groups, PowerShell, and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2018/08/10/new-unifiedgroup-multipleiowners/feed/ 3 149