Chapter 1. Users and Teams Management
If you use Microsoft Teams, you may have heard this phrase a few times: Teams is the hub for your teamwork. That’s usually meant to point out the different features and functions that Teams provides to make collaboration as easy and effective as possible. In the context of this book, that phrase reinforces the idea that Teams administration and management incorporate functionalities that used to be in separated silos. Teams is deeply integrated in the Microsoft 365 platform to deliver a seamless user experience.
The logical consequence of this integration is that managing users, groups, and teams often involves administrative tasks that are not specific to Teams. The Teams Admin Center (TAC) and the Teams PowerShell module won’t be the only tools you will use to manage Teams.
Note
Throughout the book, I will use Teams (with a capital T) when I refer to the app and platform, and teams (with a lowercase t) when talking about grouping people and giving them a channel to work together.
The TAC lets you manage many aspects of Teams within the browser. Policies can be created within the TAC, assigned to users, updated, and removed. The downside, however, is that the TAC does not provide detailed feedback, information, or error handling—especially when performing batch updates. PowerShell provides more details (regular PowerShell error handling and code) and is the best choice to manage multiple tenants or to leverage functions from Microsoft 365 and Microsoft Entra ID at the same time.
With regard to user and team management in Teams, this chapter will introduce some activities that are typically required on an almost daily basis, such as assessing what kinds of licenses you have and what those licenses enable in terms of features and access to different products.
When managing your tenant, there is also a considerable amount of work required to keep Teams manageable and compliant with company standards, including managing the lifecycle of teams and channels. Compliance involves tools that span Microsoft 365. We’ll discuss some of the required activities in this chapter, and we’ll explore compliance in more detail in Chapter 16.
Finally, one more aspect to consider in the administration of Teams is the interaction with external users accessing your Teams environment from other companies. They are not your users, strictly speaking, but they impact compliance and security, among other things. Chapter 11 explores this topic in detail.
1.1 Reporting the Assigned Office 365 License
Solution
You will use the Microsoft Graph PowerShell SDK for this solution.
Note
If you have not installed the Graph PowerShell SDK, the Discussion section of this recipe gives you additional information and resources. The solution has been tested with Microsoft.Graph module version 1.21.0. You can check the installed version of the module (if any) using the following command:
Get-InstalledModule
As a first step, you must identify the required scopes (permissions) for the specific service you want to access. In this solution, the Get-MgSubscribedSku
command is used to get the list of commercial subscriptions that an organization has acquired:
Find-MgGraphCommand
-Command
Get-MgSubscribedSku
|
Select
-First
1
`
-ExpandProperty
Permissions
Figure 1-1 shows the output.
For Get-MgSubscribedSku
, the required permissions are Directory.Read.All
, Directory.ReadWrite.All
, Organization.Read.All
, and Organization.ReadWrite.All.
Note
Graph users must sign in using the Connect-MgGraph
command, and they must explicitly specify the permissions required for the API they are accessing when issuing the command. Find-MgGraphCommand
will help you understand which permissions to ask for when you invoke the Connect-MgGraph
command.
If a user fails to include required permissions when issuing the Connect-MgGraph
command, and those permissions have not already been granted to the application or to the user, then subsequent commands that access the APIs authorized via those permissions will fail.
The next step is connecting the Graph PowerShell SDK with the correct scope. There could be a multi-factor authentication (MFA) prompt and a request to assign Microsoft Graph PowerShell permissions, depending on your tenant configuration. A successful connection will display the message “Welcome to Microsoft Graph!”
You can assign the scope by running the following command:
Connect-MgGraph
-Scopes
"Directory.Read.All"
,
"Directory.ReadWrite.All"
,
`
"Organization.Read.All"
,
"Organization.ReadWrite.All"
When you use the Connect-MgGraph
cmdlet (a small, lightweight command used in PowerShell), the last tenant you signed into during a session will be used by default. If you want to force a specific tenant, the -TenantId
parameter is required, as in the following example:
Connect-MgGraph
-TenantId
"eab48e2f-746a-4346-bf7f-xxxxxxxxx"
To see all the SKUs for a company, use the following command:
Get-MgSubscribedSku
|
select
ConsumedUnits
,
SkuId
,
SkuPartNumber
Figure 1-2 shows the output.
SKU stands for stock-keeping unit, but Microsoft uses it to define a specific business product that it sells.
Within each license type, there are also service plans (apps) provided by the license. To enable or disable a service plan, you need to use the ServicePlanId
. You can view the service plans, included in an SKU, using the following commands:
$license
=
Get-MgSubscribedSku
$license
[
0
].
ServicePlans
Figure 1-3 shows the ServicePlanId
view, after running the previous commands.
To visualize a list of assigned SKUs for a single user (for example, AllanD@M365x01033383.onmicrosoft.com), use the following command:
Get-MgUserLicenseDetail
-UserId
AllanD
@M365x01033383
.
onmicrosoft
.
com
|
fl
The Get-*
cmdlets return objects, which can contain properties that are arrays of values. When you use the pipe symbol (|
) to forward those objects to the Format-List
cmdlet, PowerShell only shows you the first four, by default.
Since the results that you see in the previous command are truncated, you can use the FormatEnumerationLimit
variable to tell PowerShell how many occurrences to include in the formatted output. If you set the variable to -1, PowerShell displays all occurrences (as in the following command):
$FormatEnumerationLimit
=-
1
Discussion
If you work with the Graph PowerShell SDK often, it makes sense to have a script to automate the scope assignment for the commands you use most frequently. The following script is just an example; you can save it in a PowerShell script (PS1 file) to execute whenever you need to use the Graph PowerShell SDK:
Select-MgProfile
-Name
"beta"
$scopes
=
@(
"AuditLog.Read.All"
,
"Directory.Read.All"
,
"Directory.ReadWrite.All"
,
"Group.ReadWrite.All"
,
"GroupMember.ReadWrite.All"
,
"Organization.Read.All"
,
"Organization.ReadWrite.All"
,
"TeamsApp.ReadWrite.All"
,
"TeamsAppInstallation.ReadWriteForTeam"
,
"TeamsAppInstallation.ReadWriteSelfForTeam"
,
"TeamSettings.ReadWrite.All"
,
"TeamsTab.ReadWrite.All"
,
"TeamMember.ReadWrite.All"
,
"User.Read.All"
)
Connect-MgGraph
-Scopes
$scopes
-TenantId
"eab48e2f-746a-4346-bf7f-xxxxxxxxx"
You can save this script in a file called GraphConnect.ps1 and use it in every solution that requires connectivity to Graph.
Microsoft provides services from its cloud (such as Azure and Microsoft 365) based on the licenses that are purchased and assigned to a user. One of the routine activities for Microsoft 365 and Teams administrators is assigning and removing Microsoft licenses, especially because companies prefer to acquire (and pay for) as few licenses as possible and reusing existing ones is a common practice.
In the past, different Microsoft resources—Azure Active Directory (AD) (now Entra ID), Exchange Online (EXO), etc.—had different sets of APIs and were considered to be different endpoints. Microsoft is moving to a different approach, with the goal to connect everything. This new approach is based on Graph exposing REST APIs and client libraries to consume and manage all the different Microsoft cloud services.
Microsoft Graph offers access to:
-
Microsoft 365 core services
-
Enterprise Mobility + Security services
-
Windows services
-
Dynamics 365 Business Central services
The basic way to use Graph is via HTTP using https://graph.microsoft.com. It is possible to access the Graph REST APIs using an SDK, available in different languages, to simplify building applications that access Graph. One of the supported languages is PowerShell.
The Graph PowerShell SDK is a collection of PowerShell modules that contain commands for calling the Graph service, and it’s the one that we are going to use for our recipes.
To install and import the Graph SDK in Windows PowerShell, run the following commands:
Install-Module
Microsoft
.
Graph
-Scope
CurrentUser
Import-Module
Microsoft
.
Graph
The import command may produce the following error:
Import-Module
:
Function
"XYZ"
cannot
be
created
because
function
capacity
4096
`
has
been
exceeded
for
this
scope
.
If that happens, use the following commands to increase the function count and the variable count, and run the Import-Module
command again:
$MaximumFunctionCount
=
8192
$MaximumVariableCount
=
8192
The -Scope CurrentUser
parameter in PowerShell specifies the current user environment as the target scope for a particular command. When this parameter is specified, the command will only operate on the user environment variables, registry settings, and other elements that are specific to the current user, as opposed to the system-wide environment. This is particularly useful when you do not have full administrative rights on the machine you are using. Throughout the book, the -Scope
parameter will usually be omitted. The decision of whether to include it or not will depend on the level of access you have to the machine you are using and whether you intend to make changes on a machine-wide or user-specific level.
Prerequisites and additional details about the Graph PowerShell SDK installation are detailed in the Microsoft article “Get started with the Microsoft Graph PowerShell SDK”.
Microsoft licensing evolves with the solutions offered in Microsoft 365. Paired with the basic license (for example, an E3 or an E5), there are add-ons that may be required to access specific features, like Public Switched Telephone Network (PSTN) calling or conditional access. A clear understanding of what is included in each plan, what is required to deliver the necessary functionalities, and what you already have is extremely important.
The official Microsoft documentation provides a list of product names and service plans for licensing. The M365Maps website is also a useful (unofficial) guide to the different Microsoft licenses. You can use it as a starting point if you need an overview of the different options.
The information available from the Get-MgSubscribedSku
command includes the following:
AccountObjectId
-
The unique ID of the account this SKU belongs to
AccountSkuId
-
The unique string ID of the account/SKU combination
ActiveUnits
-
The number of active licenses
ConsumedUnits
-
The number of licenses consumed
ServiceStatus
-
The provisioning status of individual services belonging to this SKU
SkuId
-
The unique ID for the SKU
SkuPartNumber
-
The part number of this SKU
SubscriptionIds
-
A list of all subscriptions associated with this SKU (for the purposes of assigning licenses, all subscriptions with the same SKU will be grouped into a single license pool)
SuspendedUnits
-
The number of suspended licenses (these licenses are not available for assignment)
1.2 Allocating and Removing User Licenses
Solution
In this recipe, you will assign or remove Office 365 E5 licenses to/from a Microsoft 365 group named “Users with Teams Voice.”
You can leverage the SkuPartNumber
information gathered with the cmdlets used in the previous recipe to define which licenses to assign or remove. The license you want to add is ENTERPRISEPREMIUM
. The license you want to remove is VISIOCLIENT
.
You must first connect to the Graph PowerShell SDK module:
.\
GraphConnect
.
ps1
The license information needs to be saved in variables:
$ENTERPRISEPREMIUM
=
Get-MgSubscribedSku
-All
|
where
SkuPartNumber
-eq
`
"ENTERPRISEPREMIUM"
$VISIOCLIENT
=
Get-MgSubscribedSku
-All
|
where
SkuPartNumber
-eq
"VISIOCLIENT"
A quick check of one of the variables (for example, $VISIOCLIENT
) will show the information is correctly stored (see Figure 1-4).
You now need to find the GroupId
for the group “Users with Teams Voice.” The following command searches for the group using a filter (DisplayName
starting with “Users with Teams Voice”) and puts the results in a variable:
$Group
=
Get-MgGroup
-Filter
"DisplayName eq 'Users with Teams Voice'"
`
-CountVariable
CountVar
-Top
1
-Sort
"DisplayName"
-ConsistencyLevel
`
eventual
The GroupId
makes it possible to create a list of the group members. In our case, the parameter we’re interested in is the user’s email address:
Get-MgGroupMember
-GroupId
$Group
.
Id
|
select
AdditionalProperties
|
`
foreach
{
get-mguser
-userid
$_
.
AdditionalProperties
.
}
Note
In the previous command, I’m assuming that the user principal name (UPN) and email address are the same. If this is not the case, you must edit the command to match your tenant’s naming standard for users.
You can assign a license to all the users in the group with the following command:
Get-MgGroupMember
-GroupId
$Group
.
Id
|
select
AdditionalProperties
|
`
foreach
{
Set-MgUserLicense
-UserId
$_
.
AdditionalProperties
.
`
-AddLicenses
@{
SkuId
=
$ENTERPRISEPREMIUM
.
SkuId
}
-RemoveLicenses
@()}
You can use the following command to quickly check that the licenses have been assigned:
Get-MgGroupMember
-GroupId
$Group
.
Id
|
select
AdditionalProperties
|
foreach
`
{
Get-MgUserLicenseDetail
-UserId
$_
.
AdditionalProperties
.
}
The output will look like Figure 1-5.
In a similar way, you can remove licenses:
Get-MgGroupMember
-GroupId
$Group
.
Id
|
select
AdditionalProperties
|
foreach
`
{
Set-MgUserLicense
-UserId
$_
.
AdditionalProperties
.
-AddLicenses
@()
`
-RemoveLicenses
$VISIOCLIENT
.
SkuId
}
Discussion
Managing licenses and applications based on the joiners, movers, and leavers (JML) process within a company is a task that happens on an almost daily basis. To streamline this process and reduce the risk of errors, you can make use of user groups and PowerShell.
Entra ID is the underlying infrastructure that supports identity management for all Microsoft cloud services. Entra ID stores information about license assignment states for users and automatically manages license modifications when group membership changes. Some key takeaways about this problem’s solution are as follows:
-
Licenses can be assigned to any security group in Entra ID.
-
You can disable one or more service plans inside a specific SKU.
-
All Microsoft cloud services that require user-level licensing are supported.
-
Users who receive licenses from different groups they are members of will get all the products and services assigned to these different groups.
1.3 Scripting the Creation of Teams
Solution
Create a new team with the following parameters:
-
DisplayName
: “Teams Cookbook” -
Description
: “Teams Cookbook Information Sharing” -
Visibility
: Public
Then, disable Giphy usage, add a channel, and add members to the team using the “Users with Teams Voice” user group.
Note
Giphy is a third-party source. Its content is not controlled by Microsoft, and there is a risk that inappropriate content may appear. Animated GIFs also add complexity to message compliance controls.
Let’s start by connecting to Teams:
Import-Module
MicrosoftTeams
Connect-MicrosoftTeams
The following command will create the team with the required parameters:
New-Team
-DisplayName
"Teams Cookbook"
-Description
`
"Teams Cookbook Information Sharing"
-Visibility
Public
-Owner
`
admin
@M365x01033383
.
onmicrosoft
.
com
You can check the results with the following commands:
$team
=
Get-Team
-DisplayName
"Teams Cookbook"
$team
|
fl
The output is shown in Figure 1-6.
Next, disable Giphy usage inside the channel:
Set-Team
-GroupId
$team
.
GroupId
-AllowGiphy
$false
And add a channel to the team:
New-TeamChannel
-DisplayName
"Teams Cookbook Private Channel"
`
-GroupId
$team
.
GroupId
-MembershipType
Private
Connect to the Graph PowerShell SDK and get the GroupId
using a variable, as in Solution 1.2:
.\
GraphConnect
.
ps1
$Group
=
Get-MgGroup
-Filter
"startswith(displayName, `
'Users with Teams Voice')"
-CountVariable
CountVar
-Top
1
-Sort
`
"displayName"
-ConsistencyLevel
eventual
Then list the group members and export the UPN in a comma-separated values (CSV) file:
Get-MgGroupMember
-GroupId
$Group
.
Id
|
select
AdditionalProperties
|
foreach
`
{
Get-MgUser
-UserId
$_
.
AdditionalProperties
.
}
|
Export-Csv
`
c
:\
temp
\
users
.
csv
-notypeinformation
Warning
The parameter -NoTypeInformation
is required to remove the first line that is automatically created when exporting to a CSV file. In our case, that looks like #TYPE Microsoft.Graph.PowerShell.Models.MicrosoftGraphUser1
.
The result you want instead must show the properties as columns, as shown in Figure 1-7.
Finally, bulk import the users with the following command:
Import-Csv
-Path
c
:\
temp
\
users
.
csv
|
foreach
{
Add-TeamUser
-GroupId
`
$team
.
GroupId
-User
$_
.
UserPrincipalName
}
Discussion
The solutions in this book have been tested using Microsoft Teams PowerShell module version 4.9.3.
You could also use Graph to create a team, using the New-MgTeam
command. The basic format of the command requires a Template
and a DisplayName
. The IDs of the different templates are available inside the TAC (on the Teams tab, under “Team templates,” as you can see in Figure 1-8).
Each template has a template ID that is visible in the details of the template. For example, Incident Response has the following template ID:
com.microsoft.teams.template.CoordinateIncidentResponse
So to create a new team called “Incident Response Team” using the Incident Response template, you could use the following command (in a PS1 file):
Using
Namespace
Microsoft
.
Graph
.
PowerShell
.
Models
[MicrosoftGraphTeam1]
@{
Template
=
[MicrosoftGraphTeamsTemplate]
@{
Id
=
'com.microsoft.teams.template.CoordinateIncidentResponse'
}
DisplayName
=
"Incident Response Team"
Description
=
"Incident Response Team"
}
|
New-MgTeam
1.4 Teams: Creating a Team with Dynamic Membership
Solution
You can create a team that uses an Entra ID group with dynamic membership to define its own members list. The Discussion section of this recipe talks more about dynamic membership groups. Focusing on the Teams administration part of the solution, you must first log in to a Teams client with your administrative account and select “Join or create a team,” and then select “Create a team.” Click “From a group or team,” as shown in Figure 1-9.
Then, choose “Microsoft 365 group,” as shown in Figure 1-10.
In our example, the group is called “DynamicGroupTeams” (Figure 1-11).
Discussion
Microsoft Teams supports teams associated with Microsoft 365 groups with dynamic membership. Dynamic membership for Microsoft 365 groups means that the list of the users included in the group (and, as a consequence, in the team) will be created and updated based on one or more rules that check for certain user attributes in Entra ID. Users are automatically added to or removed from the correct groups as user attributes change or as users join and leave the tenant.
Let’s quickly define a group with dynamic membership. Our query filters users that have street addresses containing “205” and a state equal to “WA.”
First, sign in to the Microsoft Entra admin center with an account that has a role as Global Administrator, Intune Administrator, or User Administrator in the organization.
Search for and select Groups, as shown in Figure 1-12.
Select “All groups,” and then click “New group,” as shown in Figure 1-13.
After you select “New group,” “Group name” will be DynamicGroupTeams and “Membership type” will be Dynamic User, as shown in Figure 1-14.
At the bottom, click “Add dynamic query.”
As Figure 1-15 shows, our query will be (user.streetAddress -contains "205") and (user.state -eq "WA")
.
Select Validate Rules to test the query on a few users, as shown in Figure 1-16.
Save the query and select Create. For a team with dynamic membership, the capability to add members manually will not be available. Figure 1-17 shows the limited number of options you have for teams with dynamic membership.
When you open the Members tab (see Figure 1-18), you will see a disclaimer that the membership settings prevent you from adding or removing members.
Owners will not be able to add or remove users as members of the team, since members are defined by dynamic membership rules.
Note
The Graph PowerShell SDK offers a command that you can use to create a dynamic Microsoft 365 group: New-MgGroup
. For example, if you want to create a new dynamic group called “Dynamic_Group_Created_with_Graph” with the same membership rules that we used earlier, you can use the following command:
New-MgGroup
-DisplayName
"Dynamic_Group_Created_with_Graph"
`
-Description
"Dynamic Group Created with Graph"
`
-MailEnabled
:
$True
-SecurityEnabled
:
$True
-MailNickname
`
DynamicGroupGraph
-GroupTypes
"DynamicMembership"
,
"Unified"
`
-MembershipRule
"(user.streetAddress -contains ""205"") and `
(user.state -eq ""WA"")"
-MembershipRuleProcessingState
"On"
1.5 Managing Apps in Teams and Channels
Solution
You want to list all the apps installed in a specific team (Sales and Marketing) and gather some additional information about them.
The TAC has tools to manage the permissions related to app installation. However, we cannot see which apps are installed in a specific team. The Teams module for PowerShell does not have a cmdlet to return information about the installed apps, so the best solution is to use the Graph PowerShell SDK.
First, connect to Teams with the usual command:
Connect-MicrosoftTeams
Then get the team’s GroupId
using the following command:
Get-Team
-DisplayName
"Sales and Marketing"
Figure 1-19 shows the output.
Next, connect to the Graph PowerShell SDK module, retrieve the information, and save it in a variable (expanding the properties that we need):
.\
GraphConnect
.
ps1
$app_info
=
Get-MgTeamInstalledApp
-TeamId
`
e5ac9743
-
4586
-
426f-a36b-bbe4a72b5802
-ExpandProperty
TeamsApp
,
TeamsAppDefinition
You can export the list of installed apps in a CSV file:
$app_info
.
TeamsApp
|
Export-Csv
c
:\
temp
\
app_info
.
csv
-NoTypeInformation
The result will look like Figure 1-20.
Additional information about the apps is available if we export the app definitions using a command like this one:
$app_info
.
TeamsAppDefinition
|
Export-Csv
c
:\
temp
\
TeamsAppDefinition
.
csv
`
-NoTypeInformation
We can see an example of an export in Figure 1-21.
Discussion
The Graph PowerShell SDK is a collection of PowerShell modules that contain commands for calling the Graph service.
The Graph PowerShell SDK is organized into modules that contain related commands and functions. Each module focuses on a specific aspect of Microsoft 365 administration, such as users, groups, SharePoint, or Teams. Modularity allows administrators to load and use only the modules they need, ensuring a lighter and more customized user experience.
The Graph PowerShell SDK complies with Microsoft’s security and compliance standards. Administrators can ensure that Microsoft 365 services are secure by using built-in security features and controls. PowerShell scripts and credentials can be secured with MFA, application permissions, and best practices. This helps administrators comply with organizational requirements and maintain a robust security posture.
1.6 Creating User Reports: Active Users and Channels
Solution
The script you’ll use exports the names of all the teams, as well as the following information for each team: team object ID, team owners, team member count, list of all the team members, number of channels in the team, channel names, SharePoint site, access type, and team guests.
Connect to Teams:
Connect-MicrosoftTeams
Connect-ExchangeOnline
-UserPrincipalName
admin
@domain
.
com
Run the following script (save it in a PS1 file):
$AllTeamsInOrg
=
(
Get-Team
).
GroupID
$TeamList
=
@()
Foreach
(
$Team
in
$AllTeamsInOrg
)
{
$TeamGUID
=
$Team
.
ToString
()
$TeamGroup
=
Get-UnifiedGroup
-Identity
$Team
.
ToString
()
$TeamName
=
(
Get-Team
|
?{
$_
.
GroupID
-eq
$Team
}).
DisplayName
$TeamOwner
=
(
Get-TeamUser
-GroupId
$Team
|
?{
$_
.
Role
-eq
'Owner'
}).
User
$TeamMembers
=
(
Get-TeamUser
-GroupId
$Team
|
?{
$_
.
Role
-eq
`
'Member'
}).
User
$TeamUserCount
=
((
Get-TeamUser
-GroupId
$Team
).
UserId
).
Count
$TeamGuest
=
(
Get-UnifiedGroupLinks
-LinkType
Members
-Identity
$Team
`
|
?{
$_
.
Name
-Match
"#EXT#"
}).
Name
if
(
$TeamGuest
-eq
$null
)
{
$TeamGuest
=
"No Guests in Team"
}
$TeamChannels
=
(
Get-TeamChannel
-GroupId
$Team
).
DisplayName
$ChannelCount
=
(
Get-TeamChannel
-GroupId
$Team
).
ID
.
Count
$TeamList
=
$TeamList
+
[PSCustomObject]
@{
TeamName
=
$TeamName
;
`
TeamObjectID
=
$TeamGUID
;
TeamOwners
=
$TeamOwner
-join
', '
;
`
TeamMemberCount
=
$TeamUserCount
;
TeamMembers
=
"$TeamMembers"
;
`
NoOfChannels
=
$ChannelCount
;
ChannelNames
=
$TeamChannels
`
-join
', '
;
SharePointSite
=
$TeamGroup
.
SharePointSiteURL
;
`
AccessType
=
$TeamGroup
.
AccessType
;
TeamGuests
=
$TeamGuest
`
-join
','
}
}
$TeamList
|
Export-Csv
c
:\
temp
\
TeamsDatav2
.
csv
-NoTypeInformation
Discussion
Using the TAC to generate reports on channels in different teams, team members, team owners, and so on is complex. The Microsoft Teams PowerShell module can display and export the relevant information.
The original script for this solution was published on Microsoft’s TechCommunity website. I have modified it slightly to export a list of all members in a team.
You can add any information you want to the export by simply adding a line with the property and an additional parameter to the $TeamList
variable. For example, let’s say you want to see whether a team is archived or not. You can add a line to the following script (I added it after line 9):
$TeamArchived
=
(
Get-Team
|
?{
$_
.
GroupID
-eq
$Team
}).
Archived
Then, modify the $TeamList
variable (the modification is in bold):
$TeamList
=
$TeamList
+
[PSCustomObject]
@
{
TeamName
=
$TeamName
;
`
TeamArchived
=
$TeamArchived
;
TeamObjectID
=
$TeamGUID
;
`
TeamOwners
=
$TeamOwner
-join
', '
;
TeamMemberCount
=
$TeamUserCount
;
`
TeamMembers
=
"
$
TeamMembers
"
;
NoOfChannels
=
$ChannelCount
;
ChannelNames
=
`
$TeamChannels
-join
', '
;
SharePointSite
=
$TeamGroup
.
SharePointSiteURL
;
`
AccessType
=
$TeamGroup
.
AccessType
;
TeamGuests
=
$TeamGuest
-join
','
1.7 Reporting Teams User Policies
Solution
This script exports a report of all policies for all users who have accounts on Teams.
Start by connecting to Teams:
Connect-MicrosoftTeams
Then, run the following script (and save it in a PS1 file):
$TeamsUsers
=
Get-CsOnlineUser
$TeamsReport
=
@()
Foreach
(
$User
in
$TeamsUsers
)
{
$Info
=
""
|
Select
"DisplayName"
,
"ObjectId"
,
"UserPrincipalName"
,
`
"SipAddress"
,
"Enabled"
,
"LineURI"
,
"WindowsEmailAddress"
,
`
"HostedVoiceMail"
,
"OnPremEnterpriseVoiceEnabled"
,
"OnPremLineURI"
,
`
"SipProxyAddress"
,
"OnlineDialinConferencingPolicy"
,
`
"TeamsUpgradeEffectiveMode"
,
"TeamsUpgradePolicy"
,
`
"HostingProvider"
,
"VoicePolicy"
,
"MeetingPolicy"
,`
"TeamsMeetingPolicy"
,
"TeamsMessagingPolicy"
,
"TeamsAppSetupPolicy"
,
`
"TeamsCallingPolicy"
,
"VoicePolicySource"
,
"MeetingPolicySource"
,
`
"TeamsMeetingPolicySource"
,
"TeamsMessagingPolicySource"
,
`
"TeamsAppSetupPolicySource"
,
"TeamsCallingPolicySource"
Write-Host
"Querying policy information for"
$User
.
DisplayName
`
-ForegroundColor
Green
$UserPolicies
=
Get-CsUserPolicyAssignment
-Identity
$User
.
SipAddress
$Info
.
DisplayName
=
$User
.
DisplayName
$Info
.
ObjectId
=
$User
.
ObjectId
$Info
.
UserPrincipalName
=
$User
.
UserPrincipalName
$Info
.
SipAddress
=
$User
.
SipAddress
$Info
.
Enabled
=
$User
.
Enabled
$Info
.
LineURI
=
$User
.
LineURI
$Info
.
WindowsEmailAddress
=
$User
.
WindowsEmailAddress
$Info
.
HostedVoiceMail
=
$User
.
HostedVoiceMail
$Info
.
OnPremEnterpriseVoiceEnabled
=
$User
.
OnPremEnterpriseVoiceEnabled
$Info
.
OnPremLineURI
=
$User
.
OnPremLineURI
$Info
.
SipProxyAddress
=
$User
.
SipProxyAddress
$Info
.
OnlineDialinConferencingPolicy
=
$User
.
OnlineDialinConferencingPolicy
$Info
.
TeamsUpgradeEffectiveMode
=
$User
.
TeamsUpgradeEffectiveMode
$Info
.
TeamsUpgradePolicy
=
$User
.
TeamsUpgradePolicy
$Info
.
HostingProvider
=
$User
.
HostingProvider
$Info
.
VoicePolicy
=
(
$UserPolicies
|
Where-Object
{
$_
.
PolicyType
-eq
`
"VoicePolicy"
}).
PolicyName
$Info
.
VoicePolicy
=
((
$UserPolicies
|
Where-Object
{
$_
.
PolicyType
-eq
`
"VoicePolicy"
}).
PolicySource
).
AssignmentType
$Info
.
MeetingPolicy
=
(
$UserPolicies
|
Where-Object
{
$_
.
PolicyType
-eq
`
"MeetingPolicy"
}).
PolicyName
$Info
.
MeetingPolicySource
=
((
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"MeetingPolicy"
}).
PolicySource
).
AssignmentType
$Info
.
TeamsMeetingPolicy
=
(
$UserPolicies
|
Where-Object
{
$_
.
PolicyType
`
-eq
"TeamsMeetingPolicy"
}).
PolicyName
$Info
.
TeamsMeetingPolicySource
=
((
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsMeetingPolicy"
}).
PolicySource
).
AssignmentType
$Info
.
TeamsMessagingPolicy
=
(
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsMessagingPolicy"
}).
PolicyName
$Info
.
TeamsMessagingPolicySource
=
((
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsMessagingPolicy"
}).
PolicySource
).
AssignmentType
$Info
.
TeamsAppSetupPolicy
=
(
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsAppSetupPolicy"
}).
PolicyName
$Info
.
TeamsAppSetupPolicySource
=
((
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsAppSetupPolicy"
}).
PolicySource
).
AssignmentType
$Info
.
TeamsCallingPolicy
=
(
$UserPolicies
|
Where-Object
{
$_
.
PolicyType
`
-eq
"TeamsCallingPolicy"
}).
PolicyName
$Info
.
TeamsCallingPolicySource
=
((
$UserPolicies
|
Where-Object
`
{
$_
.
PolicyType
-eq
"TeamsCallingPolicy"
}).
PolicySource
).
AssignmentType
$TeamsReport
+=
$Info
$Info
=
$null
}
$TeamsReport
|
Export-Csv
.\
TeamsReport
.
csv
-NoTypeInformation
Discussion
The basic script (which I have slightly modified) was published on the Microsoft TechCommunity website. Removing one or more of the $info.<parameter>
lines will give you a shorter report if you are focused on a specific group of policies.
1.8 Bulk Assignment of Teams User Policies
Solution
Automating this kind of operation with PowerShell reduces administrative effort and risk of errors. We’ll assign a Teams meeting policy (RestrictedAnonymousNoRecording
) to an Entra ID group whose display name is “Design.”
Connect to Teams:
Connect-MicrosoftTeams
Then connect to the Graph PowerShell SDK and run the following command to get the group information:
.\
GraphConnect
.
ps1
$Group
=
Get-MgGroup
-Filter
"startswith(displayName, 'Design')"
Apply the Teams meeting policy to the group:
New-CsGroupPolicyAssignment
-GroupId
$group
.
id
-PolicyType
TeamsMeetingPolicy
`
-PolicyName
"RestrictedAnonymousNoRecording"
-Rank
1
Check the result for policies assigned to the group:
Get-CsGroupPolicyAssignment
-GroupId
$group
.
id
Discussion
Assigning policies to Azure AD users and groups is a common task for whoever manages Teams administration. Using Azure AD groups reduces the required maintenance and the risk of assigning incorrect policies to a user. You can also execute this operation from the TAC, but it can be time-consuming.
Additionally, you can check which groups you have assigned a specific policy:
Get-CsGroupPolicyAssignment
|
Where-Object
{
$_
.
PolicyName
-eq
`
"RestrictedAnonymousNoRecording"
}
You can also remove an assigned policy from a group:
Remove-CsGroupPolicyAssignment
-PolicyType
TeamsMeetingPolicy
-GroupId
$group
.
id
Group policy assignment supports all policy types used in Teams except:
-
Teams App Permission Policy
-
Teams Network Roaming Policy
-
Teams Emergency Call Routing Policy
-
Teams Voice Applications Policy
-
Teams Upgrade Policy
1.9 Summary
This chapter provided insight into the various tools and methods that can be used to simplify the daily administration and management of Microsoft Teams. These tools include PowerShell, the Graph PowerShell SDK, and various administrative panels. The scripts and steps discussed in various scenarios are versatile and can be easily adapted or reused to fulfill other administrative needs. In the next chapter, the focus shifts to the security of Teams, highlighting the essential tools and concepts that are necessary to ensure the safety and productivity of Teams users.
Get Microsoft Teams Administration Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.