Create a Microsoft Team with Graph API and PowerShell

Why use an API to create a Teams Channel?

Using API’s and Automation accounts help create a continuously repeatable process whilst minimising human error and providing a consistent experience. It has many purposes, should that be to provide a script to a managed client, create a team from a form submission, the list could go on, and If you’ve found this post by organic search, then it must be at least in some way what your are looking for.


So how do we do it?

This guide will focus on using PowerShell to call the Graph API using the Microsoft.Graph module. However, the key take away is that this can be achieve via alternative API calling methods.

What will we be deploying?

We will look at deploying a Microsoft Team, with an additional channel, whist also removing Tabs from the channels and adding custom tabs for Web Links.


Pre-requisites

  • Microsoft.Graph PowerShell Module
  • Teams Administrator (or equivalent/higher) privileges

Building up the channel Object

Before we can POST anything to the Graph API, we need to start by building up our Team and channels. In the drop down below, there is a sample of the $params object which will later be used to create a Team. We will be referencing back to this throughout this section.

Team Object
$params = @{
	"[email protected]" = "https://graph.microsoft.com/beta/teamsTemplates('standard')"
	Visibility = "Private"
	DisplayName = $TeamName
	Description = "This Teams Channel will be used for collaboration."
	Channels = @(
		@{
			DisplayName = "General"
			IsFavoriteByDefault = $true
			Description = "This channel will be used for communication purposes"
			Tabs = @(
                @{
					"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('com.microsoft.teamspace.tab.web')"
					DisplayName = "Microsoft Intune"
					Configuration = @{
						ContentUrl = "https://endpoint.microsoft.com"
					}
				}
			)
		},
        @{
			DisplayName = "Service Announcements"
			IsFavoriteByDefault = $true
			Description = "This tab will be used for things like Third Party Patching and other Service Related Alerts"
		}
	)
	MemberSettings = @{
		AllowCreateUpdateChannels = $true
		AllowDeleteChannels = $true
		AllowAddRemoveApps = $true
		AllowCreateUpdateRemoveTabs = $true
		AllowCreateUpdateRemoveConnectors = $true
	}
	GuestSettings = @{
		AllowCreateUpdateChannels = $true
		AllowDeleteChannels = $true
	}
	FunSettings = @{
		AllowGiphy = $true
		AllowStickersAndMemes = $true
		AllowCustomMemes = $true
	}
	MessagingSettings = @{
		AllowUserEditMessages = $true
		AllowUserDeleteMessages = $true
		AllowOwnerDeleteMessages = $true
		AllowTeamMentions = $true
		AllowChannelMentions = $true
	}
	DiscoverySettings = @{
		ShowInTeamsSearchAndSuggestions = $true
	}
	InstalledApps = @(
		@{
			"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('com.microsoft.teamspace.tab.web')"
		}
		@{
            #The invoke webhook
			"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('203a1e2c-26cc-47ca-83ae-be98f960b6b2')"
		}
	)
}

So lets look at some of the main properties;

There are other options within the object, which are comparable to their GUI counterparts, I have left them in the object to allow the ease of updating these values if you need to change them.

Channels

Let us explore the channel array a bit further, this is where you create additional channels within the team. This is also the section you will add in any custom tabs you may want to add as demonstrated within the object.

Each channel will be an object within the channel array, and as before, there are some basic properties like DisplayName and Description, then you have the IsFavouriteByDefault property, this is what controls if the channel is displayed or hidden upon creation based on a boolean input. Then you have Tabs, where you can add apps.

You can find the Apps available to add to this array by calling the TeamsApp API. An example query would be GET https://graph.microsoft.com/beta/appCatalogs/teamsApps?$expand=appDefinitions($select=id,displayName,allowedInstallationScopes). Using this query you could find the app IDs.

My recommendation for this would be to export a template that already has the application within it and obtain the values you need to ensure you enrich the app properly with configurations, alternatively seek these configuration values from the app vendor.

All of the apps within thr array are defined as objects as with the channels. If we look at the below object as an , you can see the teams app is bound to a URL similar to the one above. Followed by a DisplayName and the Configuration for the App. In the below example I will be creating a Tab for the Microsoft Intune Console.

@{
  "[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('com.microsoft.teamspace.tab.web')"
  DisplayName = "Microsoft Intune"
  Configuration = @{
    ContentUrl = "https://endpoint.microsoft.com"
  }
}

If you prefer to use the direct api with a JSON object from Graph Explorer or PostMan you can use the following command to convert your object to JSON.

$params | ConvertTo-Json -Depth 5

NOTE: MAKE SURE YOU FILL OUT YOUR VARIABLES WHERE THEY ARE CALLED WITHIN THE OBJECT

OK, so now we’ve explored the channels, lets explore how we POST it to the Graph API with PowerShell.


Create the Team with PowerShell

One thing I found when creating the Team via the Graph API, is that you will only receive a success status code when posting the object to the Graph. This is because the API is more like an orchestrator, which means we need to do some additional bits to track the creation.

This is more of a requirement if you want to amend the team after creation, for things like removing the Wiki tab etc, which will all be described in the following sections.

As mentioned in the Pre-requisites, we will need the Microsoft.Graph PowerShell module, you can install this by running Install-Module -Name Microsoft.Graph -AllowClobber in an elevated shell, or append with -Scope CurrentUser from a non-elevated prompt.

PowerShell Script Example
[CmdletBinding()]
param (
    [Parameter(DontShow = $true)]
    [Array]
    $ModuleNames = @("Microsoft.Graph.Teams"),
    # Teams Channel Name
    [Parameter(Mandatory = $true)]
    [String]
    $TeamName
)

#TeamsAdmin and Groups admin Required

FOREACH ($Module in $ModuleNames) {
    IF (!(Get-Module -ListAvailable -Name $Module)) {
        try {
            Write-Output "Attempting to install $Module Module for the Current Device"
            Install-Module -Name $Module -Force -AllowClobber
        }
        catch {
            Write-Output "Attempting to install $Module Module for the Current User"
            Install-Module -Name $Module -Force -AllowClobber -Scope CurrentUser
        }
    }  
    Import-Module $Module
}

$params = @{
	"[email protected]" = "https://graph.microsoft.com/beta/teamsTemplates('standard')"
	Visibility = "Private"
	DisplayName = $TeamName
	Description = "This Teams Channel will be used for collaboration."
	Channels = @(
		@{
			DisplayName = "General"
			IsFavoriteByDefault = $true
			Description = "This channel will be used for communication purposes"
			Tabs = @(
                @{
					"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('com.microsoft.teamspace.tab.web')"
					DisplayName = "Microsoft Intune"
					Configuration = @{
						ContentUrl = "https://endpoint.microsoft.com"
					}
				}
			)
		},
        @{
			DisplayName = "Service Announcements"
			IsFavoriteByDefault = $true
			Description = "This tab will be used for things like Third Party Patching and other Service Related Alerts"
		}
	)
	MemberSettings = @{
		AllowCreateUpdateChannels = $true
		AllowDeleteChannels = $true
		AllowAddRemoveApps = $true
		AllowCreateUpdateRemoveTabs = $true
		AllowCreateUpdateRemoveConnectors = $true
	}
	GuestSettings = @{
		AllowCreateUpdateChannels = $true
		AllowDeleteChannels = $true
	}
	FunSettings = @{
		AllowGiphy = $true
		AllowStickersAndMemes = $true
		AllowCustomMemes = $true
	}
	MessagingSettings = @{
		AllowUserEditMessages = $true
		AllowUserDeleteMessages = $true
		AllowOwnerDeleteMessages = $true
		AllowTeamMentions = $true
		AllowChannelMentions = $true
	}
	DiscoverySettings = @{
		ShowInTeamsSearchAndSuggestions = $true
	}
	InstalledApps = @(
		@{
			"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('com.microsoft.teamspace.tab.web')"
		}
		@{
            #The invoke webhook
			"[email protected]" = "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps('203a1e2c-26cc-47ca-83ae-be98f960b6b2')"
		}
	)
}

Connect-MgGraph

$Team = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/beta/teams" -Body $params -Method POST -OutputType HttpResponseMessage

#Wait while the team is created, this below link tracks the job. 
while ((Invoke-MGGraphRequest -URI "https://graph.microsoft.com/beta$($Team.Headers.Location.OriginalString)").status -ne "succeeded") {
    Start-Sleep 60
    "Awaiting the team creation to complete..."
}


In the above drop-down you will see an example script, which contains the same object we have been working on previously in this post, so if you have started making your own object, simply replace the object in the example script.

In this section we will focus on everything after the object and then how the script can be invoked from the command line using parameters.

The first thing that we need to do is authenticate to the Microsoft Graph, we use the Connect-MgGraph command for this when using direct execution, for automation scenarios, please review the Microsoft Documentation.

Once we have authenticated, We use the Invoke-MGGraphRequest to POST the param object to the Graph API. In this example, we assign this call to the $Team variable so we can then track the team creation.

After the initial POST to the API, the example then use a while loop to track the creation of the team. As mentioned in the tip at the start of this section, the API call to create the team is more of an orchestration API which is the reason we need to go to the additional effort to track the progress of creation.

#Wait while the team is created, this below link tracks the job. 
while ((Invoke-MGGraphRequest -URI "https://graph.microsoft.com/beta$($Team.Headers.Location.OriginalString)").status -ne "succeeded") {
    Start-Sleep 60
    "Awaiting the team creation to complete..."
}

Once the operation has succeeded, you can then layer on additional customisations, such as removing the Wiki tab as shown in the example below.

#Get the Teams ID from the Output of the header
$TeamID = (Select-String -Pattern "\'([^\']*)\'" -InputObject $Team.Content.Headers.ContentLocation.OriginalString).Matches.Groups[1].Value
#Get the Teams Channels for the new Team
$TeamChannels = Get-MgTeamChannel -TeamId $TeamID

#For Each of the Channels, remove the Wiki Tab and ensure they are all set to show by default
ForEach ($Channel in $TeamChannels) {
    $wikiTab = (Get-MgTeamChannelTab -ChannelId $Channel.id -TeamId $TeamID | Where-Object {$_.DisplayName -eq "Wiki"}).id

    Remove-MGTeamChannelTab -TeamId $TeamID -ChannelID $Channel.id -TeamsTabId $wikiTab

    Update-MGTeamChannel -TeamId $TeamID -ChannelID $Channel.id -IsFavoriteByDefault 
}

Conclusion

As mentioned at the start, automation is the key to consistency when performing repetitive tasks. Hopefully this post can aid with the understanding of how to achieve and automated approach to creating Teams and Channels within your organisation.

Resources

comments powered by Disqus