Cloning Teams and Configuring Tabs via Microsoft Graph: Cloning a Team

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

Table of Contents

  1. Cloning a Team programmatically via Microsoft Graph
    1. Cloning a Team with an Azure Logic App
    2. Cloning a team with C#
  2. Bugs or working as intended?
    1. At least one owner is always cloned
    2. Delay for the cloned owner when using application permissions
    3. Delay in owner permissions
    4. Delay when referencing the user object
    5. The mail nickname property is ignored
    6. Tabs don’t get cloned with application permissions
  3. The Future: Teams Templates
  4. 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.

Because of all the issues discussed in this chapter, I recommend you to do the cloning with delegated permissions for the time being if you wish to clone the tabs. What you need to keep in mind though is that a single user account can only be an owner of 250 teams at most. If you use a service account for cloning the teams, they’ll be an owner in every one of those new cloned teams by default. Because of this, I recommend you implement some kind of a process for assigning another user as the owner and removing the service account from team owners, so you won’t one day run into an issue of the service account no longer being able to provision any teams.

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