Cloning Teams and Configuring Tabs via Microsoft Graph: Cloning a Team
Last updated on October 16, 2021
This blog post is the first technical article in the series. In the prelude, I told you a little bit about the reasons why configuring tabs automatically is important. In this one, I will show you a couple of ways on how you can clone a team programmatically via Microsoft Graph.
Published articles in this blog post series
- Part 0: Prelude
- Part 1: Cloning a Team (you are here)
- Part 2: Configuring Tabs — the Fundamentals
- Part 3: Configuring the OneNote tab
- Part 4: Configuring the Planner tab
- Part 5: Configuring the SharePoint and Files tabs
Table of Contents
- Cloning a Team programmatically via Microsoft Graph
- Bugs or working as intended?
- The Future: Teams Templates
- What next?
Cloning a Team programmatically via Microsoft Graph
Instead of cloning some previously used teams, customers typically setup separate “template teams” for the sole purpose of cloning them. No actual collaboration happens in these teams; they are simply configured to meet the needs and perhaps adjusted later as new needs arise.
In this example, I’ve set up one such template called Project Team Template with a few channels and tabs.
Cloning a Team with an Azure Logic App
Let’s first take a look at how you can easily clone a team by using an Azure Logic App or a Power Automate flow. The process that clones the template team has been set to trigger whenever a new item is added to a specific SharePoint list. The user will provide the following pieces of information for provisioning the new team. The template name is there to help the user to select the correct one from the dropdown, and the guid is included for our logic.
And here’s the action that does the cloning. As you can see, we can directly reference the values we get from the SharePoint list item in the request body. What comes to authorization, we are using a client id and a client secret to execute the operation with application permissions. However, you can also use a service account if you prefer.
The Output in the request URL comes from a Compose action that extracts the template team guid from the dropdown value (splits the value from the colon, selects the last fragment and trims away the whitespace).
trim(last(split(triggerBody()?['Template']?['Value'], ':')))
Cloning a team with C#
OK, let’s then take a look at how you can do the same in C#. In the code below, we’ll pass the CloneTeam method the parameters that are required by the request body when we call the operation in Microsoft Graph. Here, the team visibility and the parts that should be cloned are optional pieces of information; if those are not specified, we’ll use some default values. In this case, teams are “private” and only “apps, tabs, settings and channels” are cloned by default.
After forming the body, we’ll create a new HttpClient object. To be able to call the clone operation in Microsoft Graph, we need to get authorized and set the access token to the Authorization header. In the code below, I’m calling a custom method called GetAccessTokenAsync, which will fetch the access token for me.
public static async Task<string> CloneTeam(string templateTeamId, string newTeamDisplayName, string newTeamMailNickname, string newTeamVisibility = null, string partsToClone = null) { var body = $"{{ 'displayName': '{newTeamDisplayName}', 'description': '{newTeamDisplayName}', 'mailNickname': '{newTeamMailNickname}', 'partsToClone': '{partsToClone ?? "apps,tabs,settings,channels"}', 'visibility': '{newTeamVisibility ?? "private"}' }}"; using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await Authorization.GetAccessTokenAsync("https://graph.microsoft.com")); var url = new Uri($"https://graph.microsoft.com/v1.0/teams/{templateTeamId}/clone"); var response = httpClient.PostAsync(url, new StringContent(body)).Result; var newTeamId = response.Headers.Location.ToString().Split('\'')[1]; return newTeamId; } }
Writing a blog post about the whole authorization process to support my other articles (like this one) is on my to-do list, but for now, you need to handle this by yourself while possibly utilizing other internet resources — sorry! However, you can already use my other article How to set up an Azure AD application registration for calling Microsoft Graph when setting up the required application registration in Azure AD.
Bugs or working as intended?
There are some quirks related to this whole clone team process though, especially when using application permissions. At first, all of the following features can seem like bugs, but some of them can also be working as intended.
At least one owner is always cloned
Even if you specify that members should not be cloned, one owner from the template team is always cloned to the new team; both as a member and an owner. This is most likely because a team should always have at least one owner — even though you are actually able to create Office 365 groups without any owners via Graph. Also, some operations performed to the group (such as creating a new Planner plan via Graph) fail if the user doesn’t have the member permission in addition to being an owner.
If you are using application permissions, the owner is chosen randomly, but if you are using a service account to do the cloning, the service account will always appear in the new team. If you want only the person who ordered the team to have permissions, you need to remove the other accounts from the team separately after cloning.
Delay for the cloned owner when using application permissions
As I mentioned earlier, if you use application permissions to do the cloning, one random owner from the template team is copied to the new team. What I did not mention yet is that the person who gets copied over won’t see the new team in Teams until the permission synchronization has occurred. The synchronization happens once in every two hours. Because of this, I recommend that you only have a service account as the template team owner. This way only an account that doesn’t even need to use the cloned teams suffers from this delay.
Delay in owner permissions
Whenever you clone a team and after that add other users as owners to the group, it can take a while for the new owner permissions to come into effect. The users can see the team in Teams but are not able to, e.g., invite new members to it. The users are shown to have owner permissions to the group in the Admin Center, but also, in this case, they need to wait for up to two hours for the permissions to get synchronized to Teams.
Luckily, you can work around this issue by adding the person to the group as a member first and only after that adding them also as an owner. To be on the safe side, add a few seconds of delay in between these operations.
Delay when referencing the user object
Also, make sure you are referencing the directory object when assigning the permissions. The permissions get assigned also when you reference the user object, but in this case, a similar kind of a delay as described above for the owner permissions happens for the member permissions too. This leads to the user not seeing the team at all in Teams until the synchronization has happened.
Request body should preferably be like this:
{ "@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/616a66d0-355b-43a9-8b15-bd94241bc4b4" }
If the request body is like this, there’s a delay before the team appears in Teams for the user:
{ "@odata.id": "https://graph.microsoft.com/v1.0/users/username@mytenant.onmicrosoft.com" }
The mail nickname property is ignored
Finally, even though the clone team operation allows you to specify a mail nickname in the request body, the parameter value is ignored, and the mail nickname is instead generated from the provided display name. The previous flaws are most likely “working as intended”, but I’m pretty sure that this one is a bug and will get fixed in the future.
Tabs don’t get cloned with application permissions
When you use the clone operation with delegated permissions, there are absolutely no problems with copying the tabs (although you might need to wait for a few seconds on the channel before the tabs appear in the UI for the first time). However, if you switch to using application permissions, you’ll notice that the tabs no longer get cloned.
The default Wiki tab always gets cloned – sometimes twice
When you clone a team, the default Wiki tab always gets added on all channels in the new team, even if it didn’t exist on the template team. For the General channel, it will only ever appear once. However, if you clone a team with application permissions and your template has custom channels that contain the Wiki tab, the tab will appear twice on those channels in the new team.
The Future: Teams Templates
A new feature called Teams Templates will be generally available in the near future. You can already use this feature by calling the Create Team operation in the Microsoft Graph beta endpoint. Basically, the Teams templates are JSON strings that follow a certain schema, and which we can then include in the request body when creating a new team with the operation. There are also some pre-made templates that you can simply reference without having to define all the channels, apps etc. separately. Check this page for what those different templates contain.
I was previously under the impression that the new “create team” operation would allow us to provision a team with certain predefined configurations for an existing Office 365 group. However, it looks like the new operation also ends up creating a new Office 365 group. So we still don’t have a way to provision teams for existing Office 365 groups with a template.
What next?
After the cloning has completed and the team appeared in Teams, you can see that the tabs have been copied successfully as requested, but they have not yet been configured. There’s just the Set up tab button.
Configuring the tab after the team has been cloned is something we need to do separately, and that is exactly what we are going to take a look at in the next chapter.
I hope you enjoyed reading this first part of the series. Make sure to be on a lookout for the sequels, and follow me on Twitter if you want to get notified they are out!
Until next time!
Laura
I don’t know at what point it was removed, but your link to https://www.microsoft.com/en-us/microsoft-365/roadmap?filters=&searchterms=teams%2Ctemplates shows nothing on the roadmap for “teams templates” 🙁
Hi Eric,
Thanks for the heads up! I’ve removed the old link from the article.
Laura
Hi Laura, excellent article and good explainations as i experienced exactly all the issues you mentioned. One other issue, or as intended by Ms, is the waiting period for the sucess of the cloning operation can be queued for a very long time. We experience waiting times up to 10 minutes for a simple cloning of a teams with 5 peoples and 4 channels. What is even wierder is the team is actually created in less than 15 seconds with one or two channels, then the queuing happens.
Is there anything about Ms getting this waiting better in the futur?
Hi Micael,
I don’t have any inside information about possible future changes to the team cloning (I don’t work for Microsoft). I recommend you to create a support ticket or a user voice request regarding this issue.
Laura
Hi Laura,
c# snippet is for console application or Azure function?
Raktim
Hi Raktim,
You can use it for both.
Laura
Hi Laura,
Is the method definition for “a custom method called GetAccessTokenAsync…” available as i am getting compiler error fo
Authorization.GetAccessTokenAsync.
or Are you using a whole custom class of Authorization or should i include a directive.
Raktim
Hi Laura,
Authorization.GetAccessTokenAsync is a custom static method
which uses tenantid, domain, client id, client secret, resource i.e., graph.microsoft.com, authority to create a access token.
As it is Microsoft Graph, i have replaced AAD graph https://graph.windows.net with https://graph.microsoft.com.
I have found the link very usefull in getting the token: https://www.youtube.com/watch?v=YwfvwyM_W7U&t=2137s
Raktim
Hi Laura,
Ignore my previous post. It looks as if the channel email address cannot be first requested by a user who is a guest member of the team. From my testing it looks as if a channel email address is not created automatically when the team/channel is setup. Rather the ‘Get channel email address’ menu item seems to function like a request. Only when an owner/administrator clicks the menu item is an email address generated. From that point onwards the email address becomes available to a guest as well.
Regards,
Raj
Hi Raj,
Yes, it is very unfortunate that the channel email address won’t be available before someone has clicked on the “Get channel email address” menu item via the GUI. I mentioned about this during my conference talk in the SharePoint Conference 2019 in Vegas, and a Microsoftie said to me afterwards that he had noted my feedback. Let’s hope the email address will be automatically generated in the future!
Laura