Microsoft Teams’ daily users are skyrocketing. From April 2020 to August 2021, their numbers rose more than 97 percent from 75 million to 145 million.
Suppose your company decides to adopt this software for daily internal or external communications. In that case, you might face the manual construction of public/private teams and channels, assign Office 365 users to them, and so on. This procedure might become very time-consuming as the complexity of your company increases.
To solve this problem, I have created a script that takes care of all of this for you by automating the entire process. The only thing you must do is create a JSON file as described below and pass a few parameters to the script.
Configuration file
This JSON file contains all the teams and channels that you want to create in your organization. You can also specify each object’s visibility (private or standard), the users you wish to assign to it, and their roles.
The schema of the JSON is the following,
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/object1610484731.json",
"title": "root",
"type": "object",
"required": [
"teams"
],
"properties": {
"teams": {
"$id": "#root/teams",
"title": "teams",
"type": "array",
"default": [],
"items": {
"$id": "#root/teams/items",
"title": "items",
"type": "object",
"required": [
"displayname",
"visibility",
"users",
"channels"
],
"properties": {
"displayname": {
"$id": "#root/teams/items/displayname",
"title": "displayname",
"description": "Team display name",
"type": "string",
"default": "",
"examples": [
"public team1"
],
"pattern": "^.*$"
},
"visibility": {
"$id": "#root/teams/items/visibility",
"title": "visibility",
"description": "Team visibility",
"type": "string",
"default": "public",
"enum": ["public", "private"],
"examples": [
"public",
"private"
]
},
"users": {
"$id": "#root/teams/items/users",
"title": "users",
"type": "array",
"default": [],
"items": {
"$id": "#root/teams/items/users/items",
"title": "items",
"type": "object",
"required": [
"email",
"role"
],
"properties": {
"email": {
"$id": "#root/teams/items/users/items/email",
"title": "email",
"description": "User email",
"type": "string",
"default": "",
"format": "email",
"examples": [
"user1@domain.com"
]
},
"role": {
"$id": "#root/teams/items/users/items/role",
"title": "role",
"description": "User role",
"type": "string",
"default": "owner",
"enum": ["owner", "member"],
"examples": [
"owner",
"member"
]
}
}
}
},
"channels": {
"$id": "#root/teams/items/channels",
"title": "channels",
"type": "array",
"default": [],
"items": {
"$id": "#root/teams/items/channels/items",
"title": "items",
"description": "Team users",
"type": "object",
"required": [
"displayname",
"membershiptype",
"users"
],
"properties": {
"displayname": {
"$id": "#root/teams/items/channels/items/displayname",
"title": "displayname",
"description": "Channels display name",
"type": "string",
"default": "",
"examples": [
"public channel"
],
"pattern": "^.*$"
},
"membershiptype": {
"$id": "#root/teams/items/channels/items/membershiptype",
"title": "membershiptype",
"description": "Channels membership type",
"type": "string",
"default": "standard",
"enum": ["standard", "private"],
"examples": [
"standard",
"private"
]
},
"users": {
"$id": "#root/teams/items/channels/items/users",
"title": "users",
"description": "Channels users",
"type": "array",
"default": [],
"items": {
"$id": "#root/teams/items/channels/items/users/items",
"title": "items",
"type": "object",
"required": [
"email",
"role"
],
"properties": {
"email": {
"$id": "#root/teams/items/channels/items/users/items/email",
"title": "email",
"description": "User email",
"type": "string",
"default": "",
"format": "email",
"examples": [
"user1@domain.com"
]
},
"role": {
"$id": "#root/teams/items/channels/items/users/items/role",
"title": "role",
"description": "User role",
"type": "string",
"default": "member",
"enum": ["owner", "member"],
"examples": [
"owner",
"member"
]
}
}
}
}
}
}
}
}
}
}
}
}
The following example shows how to create different teams and channels with multiple users and permissions,
{
"teams":[
{
"displayName": "Public team1",
"visibility": "Public",
"users": [
{
"email":"User1@domain.com",
"role":"Owner"
},
{
"email":"User2@domain.com",
"role":"Member"
},
{
"email":"User3@domain.com",
"role":"Member"
}
],
"channels": [
{
"displayName": "Public channel",
"membershipType": "Public",
"users":[]
},
{
"displayName": "Private channel",
"membershipType": "Private",
"users":[
{
"email":"User1@domain.com",
"role":"Owner"
},
{
"email":"User2@domain.com",
"role":"Member"
}
]
}
]
},
{
"displayName": "Public team2",
"visibility": "Public",
"users": [
{
"email":"User1@domain.com",
"role":"Owner"
},
{
"email":"User2@domain.com",
"role":"Member"
},
{
"email":"User3@domain.com",
"role":"Member"
}
],
"channels": [
{
"displayName": "Public channel",
"membershipType": "Public",
"users":[]
},
{
"displayName": "Private channel",
"membershipType": "Private",
"users":[
{
"email":"User1@domain.com",
"role":"Owner"
},
{
"email":"User2@domain.com",
"role":"Member"
}
]
}
]
}
]
}
Script
This script is based on the official Microsoft Teams PowerShell module.
If you are using a version of PowerShell prior the 1.0.18, you might face the following error,
New-TeamChannel : A parameter cannot be found that matches parameter name 'MembershipType'.
At line:1 char:101
+ ... upId -DisplayName PrivateChannelDisplayName -MembershipType Private
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [New-TeamChannel], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.TeamsCmdlets.PowerShell.Custom.NewTeamChannel
The problem is that the creation of private channels is not supported by versions prior to 1.0.18. At the time of this writing, I am using version 1.1.10-preview.
The logic of this script is straightforward. However, there are few keynotes.
Because each team and the private channel has its dedicated SharePoint communication site, channels are created asynchronously. To solve possible issues related to assigning users to an incomplete instance, the function Add-UserToPrivateChannel will perform several attempts to set users to a private channel.
If you want to set a user as the owner of a private channel, the user must first be added as a standard user and then changed to the owner.
Param(
[Parameter(Position=0)]
[string]$Office365Username,
[Parameter(Position=1)]
[string]$Office365Password,
[Parameter(Position=2)]
[string]$TeamsFilePath
)
Write-Verbose "Importing modules"
$Module = Get-Module -Name MicrosoftTeams -ListAvailable
if($Module.Count -eq 0) {
Write-Verbose "Installing MicrosoftTeams module"
Install-Module -Name MicrosoftTeams -AllowPrerelease -AllowClobber -Force
}
Function New-MicrosoftTeam([object]$Team) {
Try {
Write-Verbose "Creating $($Team.DisplayName) $($Team.Visibility) team"
$NewTeam = New-Team -DisplayName $Team.DisplayName -Visibility $Team.Visibility
Write-Verbose "Adding $($Team.Users.Length) users to $($Team.DisplayName) team"
$Team.Users | ForEach-Object -Begin {
$Index = 0
} -Process {
$Index = $Index + 1
Write-Progress -Id 1 -ParentId 0 -Activity "Add user to the team" -Status "$($Index) of $($Team.Users.Length) - User: $($_.Email), Role: $($_.Role)" -PercentComplete ($Index/$Team.Users.Length*100)
Write-Verbose "Adding $($_.Email) to $($Team.DisplayName) teams as $($_.Role)"
Add-TeamUser -User $_.Email -Role $_.Role -GroupId $NewTeam.GroupId
} -End {
Write-Verbose "Users succesfully added to the $($Team.DisplayName) team"
}
Write-Verbose "Add $($Team.Channels.Length) channels to $($Team.DisplayName) team"
$Team.Channels | ForEach-Object -Begin {
$Index = 0
} -Process {
$Index = $Index + 1
Write-Progress -Id 2 -ParentId 0 -Activity "Creation of a new channel" -Status "$($Index) of $($Team.Channels.Length) - Display Name: $($_.DisplayName), Membership Type: $($_.MembershipType)" -PercentComplete ($index/$Team.Channels.Length*100)
New-TeamChannel -DisplayName $_.DisplayName -MembershipType $_.MembershipType -GroupId $NewTeam.GroupId
Write-Verbose "Check channel membership type"
if('Private' -eq $_.MembershipType -And $_.Users.Length -gt 0) {
Write-Verbose "Add $($_.Users.Length) users to $($_.DisplayName) private channel"
$_.Users | ForEach-Object -Begin {
$IndexUsers = 0
$UsersLength = $_.Users.Length
$DisplayName = $_.DisplayName
} -Process {
$IndexUsers = $IndexUsers + 1
Write-Progress -Id 3 -ParentId 2 -Activity "Add user to the private channel" -Status "$($IndexUsers) of $($UsersLength) - User: $($_.Email), Role: $($_.Role)" -PercentComplete ($IndexUsers/$UsersLength*100)
Write-Verbose "Adding $($_.Email) to $($DisplayName) private channel as $($_.Role)"
Add-UserToPrivateChannel -DisplayName $DisplayName -Email $_.Email -Role $_.Role -GroupId $NewTeam.groupId
} -End {
Write-Verbose "Users succesfully added to the $($_.DisplayName) channel"
}
}
} -End {
Write-Verbose "Channels succesfully created"
}
}
Catch {
Write-Error "Message: [$($_.Exception.Message)]" -ErrorId B1
}
}
Function Add-UserToPrivateChannel([string]$DisplayName, [string]$Email, [string]$Role, [string]$GroupId) {
$MaxNumberOfAttemps = 5
$Attemps = 0
do {
try {
Write-Verbose "$($Attemps) attempt/s"
Write-Verbose "Waiting $(60*$Attemps) seconds"
Start-Sleep -s (60*$Attemps)
Write-Verbose "Adding $($Email) to $($DisplayName) private channel"
Add-TeamChannelUser -DisplayName $DisplayName -User $Email -GroupId $GroupId
Write-Verbose "Check user role"
if("Owner" -eq $Role){
Write-Verbose "Set $($Email) as owner of the $($DisplayName) private channel"
Add-TeamChannelUser -DisplayName $DisplayName -User $Email -Role "Owner" -GroupId $GroupId
}
break;
} catch {
$Attemps = $Attemps + 1
if($_.Exception.ErrorCode -ne 404 -And $attemps -eq $MaxNumberOfAttemps){
throw
}
}
} while ($Attemps -lt $MaxNumberOfAttemps)
}
Write-Verbose "Generating secure password"
$SecurePassword = ConvertTo-SecureString -AsPlainText $Office365Password -Force
Write-Verbose "Generating PSCredential object"
$Credentials = New-Object -TypeName System.Management.Automation.PSCredential -Argumentlist $Office365Username, $SecurePassword;
Write-Verbose "Connecting to Microsoft Teams"
Connect-MicrosoftTeams -Credential $Credentials
Write-Verbose "Read JSON file from $($TeamsFilePath)"
$Json = Get-Content -Raw -Path $TeamsFilePath | ConvertFrom-Json
$Json.Teams | ForEach-Object -Begin {
$Index = 0
} -Process {
$Index = $Index + 1
Write-Progress -Id 0 -Activity "Creation of the teams" -Status "$($Index) of $($Json.Teams.Length) - Display Name: $($_.DisplayName), Visibility: $($_.Visibility)" -PercentComplete ($Index/$Json.Teams.Length*100)
New-MicrosoftTeam -Team $_
} -End {
Write-Host "Update completed" -ForegroundColor Green
}
Thanks to Ivan Porta for the nice idea.