Teams policies assigned to user

By Robert Dyjas on . Last edit:  • Edit this post

Learn how to analyze and report Teams policies assigned to user accounts.

The longer you administer Microsoft Teams, the more policies you might need to create and assign to your users. At some point, you might not necessarily remember which policies were assigned to which users. Let's analyze the options for reporting that via PowerShell.

Finding policies assigned to a user

The cmdlet which contains all the information we need is Get-CsOnlineUser. Based on the properties ending with Policy, we'll be able to find what policy is assigned to the user:

Get-CsOnlineUser -Identity |
	Select-Object userprincipalname,*policy

The output should be similar to the below:

UserPrincipalName                      :
OnlineDialinConferencingPolicy         :
ExchangeArchivingPolicy                : Uninitialized
VoicePolicy                            :
CallerIdPolicy                         :
MobilityPolicy                         : MobilityEnableOutsideVoice
ConferencingPolicy                     : BposSAllModality
# and so on...

An empty value means that the global policy is assigned. If you see any name on the right, that means the non-default policy is assigned.


If you want to check details of any policy, use the following pattern:

Get-CS<PolicyNameGoesHere> -Identity '<PolicyNameOrGlobal>'
# For example - named policy
Get-CsConferencingPolicy -Identity 'BposSAllModality'
# and global policy
 Get-CsOnlineDialinConferencingPolicy -Identity 'Global'

Finding users with a certain policy assigned

In some cases, we're not interested in policies assigned to the user, but we rather need to find all people with a certain policy assigned. Typical scenario is when we want to delete the policy. We cannot do that until there's no account with that policy assigned.

So, how to find the account with our to-be-deleted policy still assigned? We could use -Filter parameter for that purpose.

# String syntax
Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -eq 'AllowPreview'"
# ScriptBlock syntax
Get-CsOnlineUser -Filter {TeamsUpdateManagementPolicy -eq 'AllowPreview'}


Be careful about what type of quotes you use with string syntax. For filtering, you need to use two different types. If we use the same type, the inner quote would end the outer one and we'd see the syntax error:

PS> Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -eq "AllowPreview""
Get-CsOnlineUser: Cannot bind parameter 'Filter' to the target.
Exception setting "Filter": "Syntax error: "syntax error"
query: "TeamsUpdateManagementPolicy -eq " position: "29""

Finding all users with a custom policy assigned

As an extension of the previous example, we might want to list all the accounts which have non-global policy assigned. Let's try to extend our script!

Non-working solution

For filtering, only -eq and -ne operators are supported. This is purely based on my findings, as there's no mention of that in the docs.

The above means we cannot use any of the following:

# Not working
Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -like '*'"
Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -match '*'"
Get-CsOnlineUser -Filter {TeamsUpdateManagementPolicy -in @('EnablePublicPreview')}
# Not working

All of these would result in an error like that:

Get-CsOnlineUser: Cannot bind parameter 'Filter' to the target.
Exception setting "Filter": "Query not supported for operator:
"-in" query: "TeamsUpdateManagementPolicy -in @('EnablePublicPreview')"
position: "29""

And if we try to use a non-equality operator we'd receive a different error:

# Any of these
Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -ne ''"
Get-CsOnlineUser -Filter "TeamsUpdateManagementPolicy -ne 'Global'"

# Would result in
Get-CsOnlineUser: Cannot bind parameter 'Filter' to the target.
Exception setting "Filter": "Policy "Global" is not a user policy.
You can assign only a user policy to a specific user."

Working, but slow solution

In contrary to the previous example, we can use all operators after we pull all the users into a variable and pipe into Where-Object:

$allUsers = Get-CsOnlineUser -ResultSize Unlimited
# Group by policy
$allUsers | Group-Object TeamsUpdateManagementPolicy
# Display users with non-default policy
$allUsers | Where-Object {
	$null -ne $_.TeamsUpdateManagementPolicy } |
	Select-Object userprincipalname

The biggest drawback is that pulling all the user data takes a lot of time and depend on the size of our environment, so it's not the optimal solution.

Working and optimal solution

While only equality operators are available, we could construct a query that contains queries for all the policies merged with -or operator.

The workflow would be:

  1. Get all the policies using a dedicated cmdlet
  2. Remove global policy from an array
  3. For each policy in the array, construct query using -eq operator
  4. Join all the queries using -or operator surrounded with spaces

Translating into PowerShell:

$filterQuery = (Get-CsTeamsUpdateManagementPolicy | Where-Object Identity -ne "Global"
| Select-Object -Expand identity | Foreach-Object {
	"TeamsUpdateManagementPolicy -eq '$_'"
} ) -join " -or "
Get-CsOnlineUser -Filter $filterQuery |
	Select-Object UserPrincipalName

We can also construct a universal query where we specify the policy type first and then use if for the query construction:

$policyType = 'TeamsUpdateManagementPolicy'
(& "Get-Cs$policyType" | Where-Object Identity -ne "Global"
	| Select-Object -Expand identity | Foreach-Object {
		"$policyType -eq '$_'"
	} ) -join " -or "

In the script, we use & call operator to run the cmdlet. This is because we need to calculate the cmdlet based on the variable:

& "Get-Cs$policyType"

Later on, we use pipeline variable $_ to access the value of current item from a loop:

# If current item in a loop is Policy1, the value of
"$policyType -eq '$_'"
# Will be the following string
$policyType -eq 'Policy1'


Using Get-CsOnlineUser we can find out a lot of information about the Teams policies assigned in our environment. If we dig into filtering, we will be able to optimize our queries and more easily fetch the data we need.