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.
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 robert@domain.com |
Select-Object userprincipalname,*policy
The output should be similar to the below:
UserPrincipalName : robert@domain.com
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.
Tip
If you want to check details of any policy, use the following pattern:
powershellGet-CS<PolicyNameGoesHere> -Identity '<PolicyNameOrGlobal>' # For example - named policy Get-CsConferencingPolicy -Identity 'BposSAllModality' # and global policy Get-CsOnlineDialinConferencingPolicy -Identity 'Global'
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'}
Warning
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:
powershellPS> 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""
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!
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."
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.
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:
-eq
operator-or
operator surrounded with spacesTranslating 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.