Setting MS Teams Policies for Users using PowerShell


In my past two blogs I have shown you how to obtain all the users policies and output to a csv file, and how to create a new policy. In this blog post, I’m going to show you a couple of ways of setting users to new policies.

  • Change policy for an individual user
  • Change policy for a group of users
  • Change policies for group of users using a csv file

Change policy for an individual user

In this example I’m going to stick to just the messaging policy.

.\Set-IndivdualUserMessagingPolicy.ps1 -UserNameToSetPolicy:”Jeff.Hay@mytenant.onmicrosoft.com” -PolicyName:”NoGiphyOrStickersMessagePolicy”

param(
[Parameter(Mandatory)]
[string]
$UserNameToSetPolicy,
[Parameter(Mandatory)]
[string]
$PolicyName
)
Import-Module "C:\\Program Files\\Common Files\\Skype for Business Online\\Modules\\SkypeOnlineConnector\\SkypeOnlineConnector.psd1"
$Session = New-CsOnlineSession
Import-PSSession Session:$Session AllowClobber
if (-not $(Get-CsTeamsMessagingPolicy Identity:$PolicyName ErrorAction:SilentlyContinue)){
Write-warning "Unable to find Policy $PolicyName"
return
}
else{
Write-Information "Granting Message Policy $PolicyName for user $UserNameToSetPolicy"
Grant-CsTeamsMessagingPolicy PolicyName $PolicyName Identity $UserNameToSetPolicy
}
Remove-PSSession Session:$session
write-Information "Done"

It can take a while before the change is reflected in the Teams Administration.

Change policy for a group of users

There are a couple of ways you can do this. If you have your people data filled in correctly, such as Department, Office, City etc, you could assign all the people from one of these areas to a given policy. For example the following script grabs everyone from the sales department and assign them the sales policy for messaging:

Get-CsOnlineUser Filter {Department -eq 'sales'} | Grant-CsTeamsMessagingPolicy PolicyName "SalesPolicy"

Alternatively you could do it based on a AD Group, you first need to connect to grab the members of the given group, I would connect with AZ cli.

$GroupName = "SalesUsers"
$PolicyName = "SalesPolicy"
az login
az ad group member list group $GroupName query "[?userType == 'Member']" `
| ConvertFrom-Json `
| % { Grant-CsTeamsMessagingPolicy PolicyName $PolicyName Identity $_.userPrincipalName }

Change policies for a group of users using a csv file

In a previous blog post, I showed you how to obtain all the valid users from the tenant with their policies. We are going to use the csv it produces to change users policies.

The screen shot below shows my users with licenses, originally when I ran my script I only had DisplayName, UserPrincipalName and SipAddress showing, everything else was blank because my users were all in the global policies.

I have now filled in the CSV file with either SalesPolicy, HRPolicy, or NoGiphyOrStickersMessagePolicy. Left my account as global. These policy have already been created in my environment, help with doing that can be found in this blog post.

The following script requires your MS Teams Administrator username, and the path to the csv file. It loops through each item and then sets the polices for each user.

.\set-UserTeamPolicies.ps1 -UserName:admin@mytenant.onmicrosoft.com -Path:.\teamsuserpolicies.csv

param(
#Teams Administrator UserName
[Parameter(Mandatory)]
[string]
$Username,
#CSV File Path
[Parameter(Mandatory)]
[string]
$Path
)
$InformationPreference = 'Continue'
Write-Information MessageData "Obtaining Module, please connect when prompted…"
Import-Module "C:\\Program Files\\Common Files\\Skype for Business Online\\Modules\\SkypeOnlineConnector\\SkypeOnlineConnector.psd1"
$Session = New-CsOnlineSession UserName:$Username
Import-PSSession Session:$Session AllowClobber
@($(Import-csv Path:"$PSScriptRoot\$Path")).ForEach( {
$csv = $PSItem
$userPrincipalName = $csv.userPrincipalName
Write-Information MessageData:"Applying $($csv.DisplayName) Policies…"
#"TeamsMeetingPolicy",
Grant-CsTeamsMeetingPolicy PolicyName $($csv.TeamsMeetingPolicy) Identity $userPrincipalName
#"TeamsMessgingPolicy",
Grant-CsTeamsMessagingPolicy PolicyName $($csv.TeamsMessagingPolicy) Identity $userPrincipalName
#"TeamsMeetingBroadcastPolicy",
Grant-CsTeamsMeetingBroadcastPolicy PolicyName $($csv.TeamsMeetingBroadcastPolicy) Identity $userPrincipalName
#"TeamsAppPermissionPolicy",
Grant-CsTeamsAppPermissionPolicy PolicyName $($csv.TeamsAppPermissionPolicy) Identity $userPrincipalName
#"TeamsAppSetupPolicy",
Grant-CsTeamsAppSetupPolicy PolicyName $($csv.TeamsAppSetupPolicy) Identity $userPrincipalName
#"TeamsCallParkPolicy",
Grant-CsTeamsCallParkPolicy PolicyName $($csv.TeamsCallParkPolicy) Identity $userPrincipalName
#"TeamsCallingPolicy",
Grant-CsTeamsCallingPolicy PolicyName $($csv.TeamsCallingPolicy) Identity $userPrincipalName
#"CallerIDPolicy
Grant-CsCallingLineIdentity PolicyName $($csv.CallerIdPolicy) Identity $userPrincipalName
#"TeamsChannelsPolicy",
Grant-CsTeamsChannelsPolicy PolicyName $($csv.TeamsChannelsPolicy) Identity $userPrincipalName
#"TeamsEmergencyCallingPolicy",
Grant-CsTeamsEmergencyCallingPolicy PolicyName $($csv.TeamsEmergencyCallingPolicy) Identity $userPrincipalName
#"TeamsEmergencyCallRoutingPolicy",
Grant-CsTeamsEmergencyCallRoutingPolicy PolicyName $($csv.TeamsEmergencyCallRoutingPolicy) Identity $userPrincipalName
#"TenantDialPlan",
Grant-CsTenantDialPlan PolicyName $($csv.TenantDialPlan) Identity $userPrincipalName
#"TeamsUpgradePolicy"
Grant-CsTeamsUpgradePolicy PolicyName $($csv.TeamsUpgradePolicy) Identity $userPrincipalName
if ($Session.State -ne "Opened") {
Write-Warning "Session state closed, please reauthenticate"
Remove-PSSession Session:$Session
$Session = New-CsOnlineSession UserName:$Username
Import-PSSession Session:$Session AllowClobber
}
})
Write-Information "Complete"
Remove-PSSession Session:$Session

If you have a lot of users to update, the session might timeout. On line 52 – 57 there is a check to see if the session has timed out and then gets you to reauthenticate. If anyone else knows a better way to do this, please add a comment below, or get in touch.

Creating a new MS Teams Policy using PowerShell


In my previous blog I showed you how to obtain all the policies for users. This blog post, I’m going to show an example of how to create a new Policy for Meeting.

The script I’m providing below is a simple script that will:

  • Name the policy – Line 1
  • Import the SkypeOnlineConnector module – Line 4-7
  • Create the policy if it doesn’t exist – Lines 9 – 12
  • Set the properties of the Policy – Lines 15 – 31

This policy that is being created will prevent users of this policy creating Giphy, Memes or Stickers in their chats. (Basically, taking the fun out of teams. [insert wicked laugh])

$PolicyName = "NoGiphyOrStickersMessagePolicy"
$InformationPreference = 'Continue'
Import-Module "C:\\Program Files\\Common Files\\Skype for Business Online\\Modules\\SkypeOnlineConnector\\SkypeOnlineConnector.psd1"
$Session = New-CsOnlineSession
Import-PSSession Session:$Session AllowClobber
if (-not $(Get-CsTeamsMessagingPolicy Identity:$PolicyName ErrorAction:SilentlyContinue)) {
Write-Information MessageData:"New-CsTeamsMessagingPolicy -Identity:'$PolicyName'"
New-CsTeamsMessagingPolicy Identity:$PolicyName Description:'A policy for no Giphy Or Stickers for Messaging.'
}
Write-Information MessageData:"Set-CsTeamsMessagingPolicy -Identity:'$PolicyName'"
Set-CsTeamsMessagingPolicy Identity:$PolicyName `
AllowUrlPreviews:$true `
AllowOwnerDeleteMessage: $true `
AllowUserEditMessage: $true `
AllowUserDeleteMessage: $true `
AllowUserChat: $true `
AllowRemoveUser: $true `
AllowGiphy: $false `
GiphyRatingType: Strict `
AllowMemes: $false `
AllowImmersiveReader: $true `
AllowStickers: $false `
AllowUserTranslation: $true `
ReadReceiptsEnabledType: UserPreference `
AllowPriorityMessages: $true `
ChannelsInChatListEnabledType: DisabledUserOverride `
AudioMessageEnabledType: ChatsAndChannels `
Remove-PSSession Session:$session

As you can see from the above screen shot, I now have a policy called NoGiphyOrStickersMessagePolicy, and below is a screen shot of the settings within Teams Administration.

My previous blog showed you how to get 13 different policies for each user. Below are the links to those PowerShell Set policy commands with the different parameters.

Getting all MS Teams User Policies using PowerShell


With everyone working at home at the moment, you might need to grab a report of the User Policies for MS Teams. These few steps will show you how to grab all the users and display in a csv file.

To work with policies you first need to obtain the PowerShell module – SkypeOnlineConnector. Which you can download from this link https://www.microsoft.com/en-us/download/details.aspx?id=39366

Installing the SkypeOnlineConnector and creating a session

Once you have installed the PowerShell module you will need to import the module and create a PowerShell session.

Import-Module "C:\\Program Files\\Common Files\\Skype for Business Online\\Modules\\SkypeOnlineConnector\\SkypeOnlineConnector.psd1"
$Session = New-CsOnlineSession
Import-PSSession -Session:$Session -AllowClobber

You will be prompted for MS Teams administrator username and password, you can pass your credentials in with the -credential parameter at the end of New-CsOnlineSession, however this doesn’t work with MFA.

Once connected you will be able to grab all users using the Get-CsOnlineUser cmdlet, or grab one user by providing the users Identity.

Policies

If you call the above for a single user you will see that there are loads of policies that can be set. Not all are MS Teams related. The ones I will be focusing on are the same 12 policies you see when you view Assigned policies for a user in Teams Administration.

These policies are named slightly different in the results compared to the display name shown above.

Display Name Policy Name
Meeting policy TeamsMeetingPolicy
Messaging policy TeamsMessagingPoliy
Live events policy TeamsMeetingBroadcastPolicy
App permission policy TeamsAppPermissionPolicy
App setup policy TeamsAppSetupPolicy
Call park policy TeamsCallParkPolicy
Calling policy TeamsCallingPolicy
Caller ID policy CallerIdPolicy
Teams policy TeamsChannelsPolicy
Emergency calling policy TeamsEmergencyCallingPolicy
Emergency call routing policy TeamsEmergencyCallRoutingPolicy
Dial plan TenantDialPlan
Teams Upgrade TeamsUpgradePolicy

The following script will grab all users and their current policy for the above polices, with the provided path it will output to csv file.

Please note: Anything that is set to Global policy will be blank.

.\Get-UserTeamPolicies.ps1 -Path:.\AllTeamUsersPolicies.csv

param(
#OutPut CSV File Path
[Parameter(Mandatory)]
[string]
$Path
)
$InformationPreference = 'Continue'
Write-Information -MessageData "Obtaining Module, please connect when prompted..."
Import-Module "C:\\Program Files\\Common Files\\Skype for Business Online\\Modules\\SkypeOnlineConnector\\SkypeOnlineConnector.psd1"
$Session = New-CsOnlineSession
Import-PSSession -Session:$Session -AllowClobber
Write-Information -MessageData "Getting all enabled users"
$Users = Get-CsOnlineUser | Select-Object DisplayName, `
UserPrincipalName, `
SipAddress, `
TeamsMeetingPolicy, `
TeamsMessagingPolicy, `
TeamsMeetingBroadcastPolicy, `
TeamsAppPermissionPolicy, `
TeamsAppSetupPolicy, `
TeamsCallParkPolicy, `
TeamsCallingPolicy, `
CallerIdPolicy, `
TeamsChannelsPolicy, `
TeamsEmergencyCallingPolicy, `
TeamsEmergencyCallRoutingPolicy, `
TenantDialPlan, `
TeamsUpgradePolicy
$Users | Export-Csv -Path:$Path -NoTypeInformation
Write-Information "Complete"
Remove-PSSession -Session:$session

In a later blog, I will be using the csv file to update user policies.

Bonus: If you want to get all users for just a single policy you can perform a filter on the Get-CsOnlineUser

Write-host "Teams Meeting Policy"
Get-CsOnlineUser -Filter {TeamsMeetingPolicy -eq 'GivenPolicyNameBlankForGlobal'} | Select UserPrincipalName

How does the privacy in O365 Groups/Teams affect the SharePoint Site


When you create a O365 Group or Teams you get given the option for Privacy. Either Public or Private. When an O365 group is created a Modern SharePoint Team site is created, but how is the site affected depending on the privacy?

Private Privacy

In a “Private” group, in the SharePoint site, the members of the O365 Group get added to the SharePoint Members Group.

And the owners of the O365 Group get added to the Site Collection Administrators.

Public Privacy

In a public site, the Members and Owners of the group get added to the SharePoint Members group and Site Collection Administrators respectively. The extra difference I have noticed, is that the “Everyone except external users” group is also added to the SharePoint members group.

What does this mean?

A private site is what it says. It is private. If it’s joined to a Teams anyone who is not a member of the group, cannot see MS Teams, search for site or documents inside it, or have access to the SharePoint site.

A public site is different. If it’s joined to Teams, a person who isn’t a member to the group will not see the MS Teams, meaning they cannot see channel chats, but can search to join it.

The documents however are available to them. They can search for these documents, access the SharePoint Site contribute on the documents too if they like.

Testing this

I have created two MS Teams. The first one is called Public OTB Permissions and set the privacy to Public. I am the only member of this site. The second site is called Private OTB and set the privacy to Private. Again, I am the only member of this site. In both “General” channels I have uploaded a Word document.

Public Privacy

Private Privacy


I have a second user called Alan Brewer, and in SharePoint I’m going to search for the word “Place” as this word is in both test documents.

Alan can only see the document from the public site, and when clicked through can access the document, contribute towards it. He can even view the whole SharePoint site. This is because he is being let into the site via the “Everyone except external users” claim that has been added to the SharePoint Members group for the site.

Just to complete this, I have added another user called Adam Barr to the Private site.

If I now sign in as Adam and perform the search “Place” like before, Adam can see both documents.

What happens when I switch the privacy for the O365 group.

When you decided you want to make a “Private site” public or a “Public site” Private, there is a process that happens in the back end which will either add/remove the “Everyone Except External users” to the SharePoint members group. I’ve noticed this can take up to 15 minutes or so before it is reflected in the SharePoint site.

Should I remove/change the “Everyone Except External users” from a public SharePoint site?

You can, but I don’t recommend it. If like me, personally it might be better to have the Everyone Except External Users in the Visitors group. Allowing the site to still be public, but you must be a member to contribute or change anything. The trouble with this, the site would be set up they way you want it, but then if an Owner decides to change the O365 Groups Privacy to Private, the backend process does not remove the “Everyone Except External Users” from the SharePoint Visitors Group, leaving the documents open to all. Then if they decided to switch it back or change an O365 Group from Private to Public Privacy, the backend process would add the “Everyone Except External Users” to the SharePoint members group. Making the site now open and everyone can contribute again. You could train all owners to ensure they remove/add this group if they switch the privacy, but who will remember to do that!

You could, if you are a developer, have a webhook using MS Graph that is attached to monitor O365 Group changes. https://docs.microsoft.com/en-us/graph/webhooks. Although possible, you would need to ensure that there is a delay from capturing the O365 Group webhook privacy change, and updating SharePoint, as you need to run after the backend process has been and flipped the “Everyone Except External Users”. If switching from Public to Private policy, any process you develop, if runs too late would leave the site open for a given amount of time, or if it fails could leave the site open where the users believe it is all locked down.

Top 10 blog post for 2019 from Cann0nf0dder.wordpress.com


In the last year, I let myself down a little by not blogging as much as I wanted to. I’m hoping this year I will blog a bit more often. The issue I sometimes find is there are so many decent blogs out there, writing something that is already out there feels like I’m just copying someone else’s idea. I also don’t write a blog about really simple things, that took me a minute to work out. If it took me a moment, is it really worth blogging about? Then there is the time to write the blog, I like to provide code, screenshots and test my walkthroughs to ensure they work and make sense. (I still have one blog that I haven’t finished yet, I started in August!)

After saying in the last paragraph I don’t want to copy someone else’s idea, the idea of posting my Top 10 blog posts for 2019, I have nicked from Nate Chamberlain. https://natechamberlain.com/2020/01/01/top-10-posts-of-2019-on-natechamberlain-com. I don’t know Nate personally, but I hope he doesn’t mind I’m copying his blog post idea.

Anyway, thank you for reading my blog and here are a list of the most popular blog posts in 2019. (I’m surprised from the list that people still go to them, as they are quite old now, dating back to 2013! Only one of them was written in 2019.)

Top 10 blog post based on views for 2019

TitleNumber of Views
TypeScript error in Visual Studio – Cannot find module, problem with the tsconfig.json file6,125
SharePoint Designer 2013 Workflows and GET REST API5,002
Building SharePoint 2016 Development environment – Part 8 – Installing SQL 2016 ready for SharePoint 20164,505
Getting sound to work within your Windows Server Hyper V client4,081
Upgrading Windows 10 – Resolution stuck at 1024 x 7683,839
Unable to download the Exchange Online PowerShell Module – ‘Deployment and application do not have matching security zones3,294
Using REST to upload a File to Document library (Including different site collection)3,167
Building SharePoint 2016 development environment – Part 10 – Configuring Central Administration for SSL2,959
SharePoint Designer 2013 Workflows and POST REST API2,937
Accessing Taxonomy Term Store with JSOM2,883
Cann0f0dder’s top 10 blog posts of 2019

As the above list one has one blog post I wrote in 2019, below is a list of the top ten blog post that were written in 2019.

The Top 10 blog post based on views that were written in 2019

TitleNumber of Views
Unable to download the Exchange Online PowerShell Module – “Deployment and application do not have matching security zones”3,294
Programmatically change the New Menu in SharePoint Online using PowerShell884
SharePoint Online Custom Format View issue with @now and UK Date format808
Setting up an O365 Dev Tenant – Part 1 – Getting the Tenant795
Access denied when attempting to move SharePoint documents395
Visual Studio – NuGet – No connection could be made because the target machine actively refused it 127.0.0.1:8888263
Setting up an O365 Dev Tenant – Part 6 – Set up SharePoint Tenant188
Finding the related Site from Teams Private Channel Site181
Setting up an O365 Dev Tenant – Part 2 – Create users from CSV file168
Setting up an O365 Dev Tenant – Part 5 – Turning on O365 Auditing104
Cann0f0dder’s top 10 blog posts of 2019 written in 2019

Finding the related Site from Teams Private Channel Site


Private Channels gives the ability to restrict the membership further within a Team site. A person can create a private channel, like creating a public channel, except they can add owners/members to the channel from a subset of members from the Team site.

When a private channel is created, what is happening under the covers is a creation of another SharePoint site. A cut down version of a SharePoint site, using the Template TEAMCHANNEL#0. (ID: 69 for those that want to know)

As this is my first blog post about Private Channels, let me demonstrate quickly how to create a Private Channel.

How to add a private channel

  • From MS Teams click on the ellipse next to your Team name, and select Add Channel.
  • Give the channel a name, optional description, and select “Private – Only accessible to a specific group of people within the team”
  • Click Next. On the next page you can add people from the Team to have access to the Private Channel. They can be an Owner of the channel even if they are only a member within the Team.
  • The private channel will show up as a channel underneath your team, with a pad lock next to it, indicating that it is a private site. You will only see this channel if you are a owner/member of the channel.
  • The SharePoint site – which you can get to by clicking on files Open in SharePoint – has the URL made up of https://<tenant>.sharepoint.com/sites/<TeamName>-<ChannelName> and the home page of the site is the root of the Shared Document library.

Finding the related site

There are a couple of places I have found out where to get the related site.

Property Bag and Graph API

When I did a PNP Get-ProvisioningTemplate pointing at a private channel site, I discovered in the property bag there is a value called RelatedGroupId and it is Indexed.

  <pnp:PropertyBagEntry Key=“RelatedGroupId” Value=“d99aa865-cd55-46cc-b256-177975ad3e13” Overwrite=“false” Indexed=“true” />

With this value you can then get the SharePoint site of the MS Team using Graph API

https://graph.microsoft.com/v1.0/groups/<group-id>/sites/root?$select=webUrl

or

https://graph.microsoft.com/v1.0/groups/<group-id>/sites/root/weburl

Note: On the parent Team site, there is a property bag value called GroupId. It also has RelatedGroupId, which has the same value.

CSOM using the Site object

The related GroupID can also be obtained in CSOM via the Site object.

Site site = context.Site;
context.Load(site, s => s.RelatedGroupId);
context.ExecuteQueryRetry();

Showing all the private channels from the main SharePoint site

When I used PNP to obtain the Private Channel template and discovered the RelatedGroupId was in the property bag and that it was indexed, means that it is searchable. If you check the Manage Search Schema, you will find the managed property.

This means doing a simple search like below, will return all the private channel sites.

RelatedGroupId:<GroupID> contentclass:STS_Site

Using the Microsoft Search PnP Modern Search SPFX (https://github.com/microsoft-search/pnp-modern-search/releases/tag/3.8.0), very quickly I was able to display links.

For someone who only has access to one of the Private Channels, they will only see one in search.

Unable to see public Teams within ‘Join or create a team’ section of MS Teams


Had an issue for a while now where the public teams were no longer showing within our Microsoft Teams client. Whenever you go to ‘Join or create a team’ at the bottom of the team’s panel

You were present with a screen similar to below. Also, the search box on the right hand of the screen was missing.

To fix this you need to go into your Admin Teams setting. https://admin.teams.microsoft.com

On the left-hand side, expand Org-wide settings and select Teams Settings

At the bottom of the Teams settings, there is a Search by Name section. Ensure this is turned OFF.

Once this has been turned off, it takes about 30 minutes for the tenant to update with the changes.

If you go back to ‘Join or create a team’ you should see the search box and the public teams.

Note: If you are using the browser, you might need to do a Ctrl + F5 to clear the cache.

Note: Within the Teams app, you might need to log out and in again to clear the cache.