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
Policy
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:
Get-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
# 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:
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""
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
-ne
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
The workflow would be:
-eq
-or
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
&
& "Get-Cs$policyType"
Later on, we use pipeline variable
$_
# If current item in a loop is Policy1, the value of
"$policyType -eq '$_'"
# Will be the following string
$policyType -eq 'Policy1'
Using
Get-CsOnlineUser