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
Hi Laura,
Cloning a team using app only permissionsdoes have a limitation that channel email address is no longer available in the cloned team. This doesn’t come right even after a delay. I have the following app permissions set up: Group.ReadWrite.All, User.ReadWrite.All, Directroy.ReadWrite.All
Regards,
Raj
Hi Raj,
Actually, the site email address won’t become available for any new Teams channels before someone has actually clicked on the “Get channel email” menu item in the Teams user interface. This is unfortunate as it means you can’t send email to a channel before someone has done this.
Laura
Hi Laura,
Thank you for nice article. It helps me a a lot.
I have some issues and I think you can help.
How can we parse the response header that contains team ID and operation ID? It doesn’t return JSON format.
When clone operation is completed, some tabs (Planner, Power BI) in General chanel do not get cloned. However it works well with other channel.
Do you experience the same?
Hi Nam,
You can extract the team ID with this line of code:
var newTeamId = response.Headers.Location.ToString().Split(‘\”)[1];
The operation ID is contained in another one of those string fragments, index 3 if I remember correctly.
The tabs don’t get cloned if you are cloning a team with application permissions. With delegated permissions, the tabs do get cloned, but you might need to wait for a couple of seconds on the channel for them to appear.
Laura
Hi Laura, Thanks for the great article. I’ve a similar requirement to auto provision teams based on a template and also to provision site columns, custom content types etc. upon team creation. The clone team operation runs successfully and I’m able to get the team id from the location header of response. Next I do a Http get operation to get the group details and that runs successfully. I haven’t put any wait operation between the clone team and get group. However when I try to get the group site (https://graph.microsoft.com/v1.0/groups//sites/root), I always end up getting exception 404. I realized that site is not provisioned yet and sometimes it takes more than an hour to get provisioned. Have you faced this issue? Should I instead provision a team site first using graph and then create a team somewhat along the lines of your other article https://laurakokkarinen.com/provisioning-teams-with-a-site-design-flow-and-microsoft-graph/
Hi Rajesh,
I must say that I have not personally run into the issue of the team site not getting provisioned almost instantly. Could you take a look at this other blog post where I am doing the same thing as what you are attempting and check if you are doing something differently? https://laurakokkarinen.com/cloning-teams-and-configuring-tabs-via-microsoft-graph-configuring-the-sharepoint-and-files-tabs/
And yes, you could create the team site first and then get the group and teamify it, but then you’d also need to write code for provisioning all the channels, apps, tabs, etc. separately.
Laura
Hi Laura,
Thank you for working this out. I have set it up similarly, however I found out that the new team never get’s its sharepoint directory (We are setting up your file directory.) and no channel email addresses.. If I open the team in the client, and ask for a channel’s email address, it keeps displaying ‘Were still setting up your team’, even after a few days.
Do you experience the same?
Hi Gabor,
I tested this just now with both delegated and application permissions. Had no problem with either. It took a couple of minutes for the “Get email address” and the Files tab to start working.
Hmm, I wonder what could be going wrong. Do let us know if you figure out the solution! I bet you are not the only person in the world struggling with these kinds of quirks. Your experiences could be very helpful to others, too.
Laura
Hi Laura,
Thank you. It must be a permission thing. I am working on delegated rights, see which one I miss at the moment. Will let you know the results.
Today, the entire Clone Team option was removed from our client 😉
I think there is work being done..
Creating the teams from fresh works, but same issue. Email is only added after requiring it manually from the client.
I also found out that the naming of teams is a crucial factor..
But all these little things are material for the team working on this. Your manual is still a very good starting point, thank you for writing it. If you send me an email I can give you more details of what does and doesn’t work in my setup at this moment.
Keep up the inspiring work!
Gabor
Hi Gabor,
I saved your email and will be in touch!
Laura
Dear,
I have read your blog posts, and I have to agree that there are still flaws in the API, but I had a question for you:
When we clone a team, the group shows the message that his team does not yet exist, although in Teams, when I use the explorer tot SharePoint I do find the site of the group…
Did you experienced the same error/problem…
Hi Sebastian,
When you use the Clone team operation, the new team isn’t set up immediately, but instead, you get a response “ok, your request has been received and we’ll process it as soon as possible”. In practice, this means receiving the HTTP status code 202 (Accepted), and the new team ID and the operation ID in the location header. You can then either poll the operation or the team/group to check when the group is actually ready for further operations.
What I tend to do is to add a loop with a try-catch structure, which contains the other operation(s) that I want to do after the group is ready. If the group is not yet ready, it will throw an exception, and I’ll then make the thread sleep for a little while (typically 5-10 seconds) in the catch block before trying again. I also always include a counter for a maximum amount of retries to ensure that the execution never ends up in an infinite loop.
Laura