Adding apps & tabs in Microsoft Teams using Graph – part 1

In part one of this two part tutorial I will show you how to add tabs within your channels in Microsoft Teams, using the Graph APIs. In part two I will also cover how you can automate the procedure using PowerShell, allowing for scenarios where you need to bulk add tabs to a large set of teams.

microsoft_graph

Microsoft Graph

First things first, what is Graph? Microsoft Graph is the gateway to data and intelligence in Microsoft 365, and allow applications to access digital work and digital life data across the intelligent Microsoft cloud (Source: Microsoft).

Sound great, but what does it really mean? In short it’s a set of REST APIs we can connect to in order to programatically interact with services within Microsoft 365, whether it’s to get meetings and calendar data from Exchange, mobile device status from Intune or feedback and grades from assignments in Microsoft Teams for Education.

Technically Microsoft has multiple Graph APIs, or endpoints, like the Intelligent Security Graph, the education API, Office Graph and more, but we can easily interact with them simultaneously, as well as use data from one in the other, so lets refer to Microsoft Graph as an entity going forward.

Like the title says, we’re going to focus on adding tabs within Teams today, a feature, or possibility if you will, announced at Microsoft Ignite 2018.

 

Who needs to add tabs programatically?

In many if not most cases, teams are setup and managed largely by team owners. But there are organizations that combine tools like Forms or PowerApps with Flow, in order to govern the team creation process using a request form. Others bulk create teams using the Microsoft Teams PowerShell module, and sectors like education have tools like School Data Sync to automate the teams lifecycle.

In those scenarios the team is created for the owner, often pre-populated with relevant members, and we might like to prepare it with a set of predefined channels, apps and tabs.

 

Getting the IDs

Microsoft love IDs, whether they’re called globalIDs, groupIDs or teamsAppIDs, and I’m sure also great ideas.

In order to add a tab, we first need to get the teams ID, the channel ID, and we need some info about the tab we’re adding. A great way to get started with Graph is using the Graph Explorer, so lets use that and see if we can find some of those IDs.

You must first login to the service, then modify the permissions (either according to the documentation or just add everything), and finally choose beta.

Graph_logon.png

This worked at the time of writing, but since these APIs are still in beta there might have been changes by the time you read this and they might have been moved into production (v1.0).

 

Lets first run a GET to see if the logged on user has any team memberships.

GET https://graph.microsoft.com/beta/me/joinedTeams

Response:
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#groups",
    "value": [
        {
            "id": "74bacc8d-dd90-4b5a-aba2-72cf9c044eea",
            "displayName": "GraphTeam",
            "description": "GraphTeam",
            "isArchived": false
        }
    ]
}

 

We can see this particular user is a member of one team caled GraphTeam, and we also get the teams ID. Lets use that and see if there are any channels in that team.

GET https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/

Response:
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#teams('74bacc8d-dd90-4b5a-aba2-72cf9c044eea')/channels",
    "value": [
        {
            "id": "19:3244b3fd6f8a42d390a79706b68678f1@thread.skype",
            "displayName": "General",
            "description": null
        },
        {
            "id": "19:d6789651b4704d2db79c9f76a13009c2@thread.skype",
            "displayName": "Channel1",
            "description": null
        }
    ]
}

 

You can see the ID was added to the query, as well as /channels/ since that is what we’re after, and we get two channels in return; General and Channel1. Again we’re getting the IDs, let’s choose General and see if we can query for tabs.

GET https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/19:3244b3fd6f8a42d390a79706b68678f1@thread.skype/tabs

Response:
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#teams('74bacc8d-dd90-4b5a-aba2-72cf9c044eea')/channels('19%3A3244b3fd6f8a42d390a79706b68678f1%40thread.skype')/tabs",
    "value": [
        {
            "id": "0d042444-0c01-4fbd-afb8-2f936f7ba751",
            "name": "Wiki",
            "teamsAppId": "com.microsoft.teamspace.tab.wiki",
            "sortOrderIndex": "10000",
            "webUrl": "https://teams.microsoft.com/l/channel/19%3a3244b3fd6f8a42d390a79706b68678f1%40thread.skype/tab%3a%3a3accd260-ec69-43c8-8130-3908c1fbe02d?label=Wiki&groupId=74bacc8d-dd90-4b5a-aba2-72cf9c044eea&tenantId=6ac1a27f-51d8-47e6-8485-5ee4f0e58fee",
            "configuration": {
                "entityId": null,
                "contentUrl": null,
                "removeUrl": null,
                "websiteUrl": null,
                "wikiTabId": 1,
                "wikiDefaultTab": true,
                "hasContent": false
            }
        }
    ]
}

 

From the look of it we have one tab, this is obviously not right since all channels have at least one tab for Conversations and one called Files.

GraphTeam_tabs.png

The query does however list all tabs that we are able to edit, which makes sense.

The tab that was returned was a Wiki tab, and what is the first thing we do with the Wiki tab? We replace it with a OneNote tab! So let’s remove it by adding the tab ID to the query and changing from GET to DELETE.

DELETE https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/19:3244b3fd6f8a42d390a79706b68678f1@thread.skype/tabs/0d042444-0c01-4fbd-afb8-2f936f7ba751

 

It doesn’t return anything, but we can clearly see that the Wiki tab has been removed.

GraphTeam_tabs_noWiki.png

 

To learn how to construct the object when creating a tab there are examples in the API reference documents. You can also get information querying for existing tabs, I have added a website tab, lets see what that looks like.

GET https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/19:3244b3fd6f8a42d390a79706b68678f1@thread.skype/tabs

Response:
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#teams('74bacc8d-dd90-4b5a-aba2-72cf9c044eea')/channels('19%3A3244b3fd6f8a42d390a79706b68678f1%40thread.skype')/tabs",
    "value": [
        {
            "id": "862ba9d9-9e68-48ad-a0b8-5ca4ac637c55",
            "name": "Teams.rocks",
            "teamsAppId": "com.microsoft.teamspace.tab.web",
            "sortOrderIndex": "10100",
            "webUrl": "https://teams.microsoft.com/l/channel/19%3a3244b3fd6f8a42d390a79706b68678f1%40thread.skype/tab%3a%3a577a969c-9d7b-4ef5-bb2e-35d8086a3be5?webUrl=https%3a%2f%2fTeams.rocks&label=Teams.rocks&groupId=74bacc8d-dd90-4b5a-aba2-72cf9c044eea&tenantId=6ac1a27f-51d8-47e6-8485-5ee4f0e58fee",
            "configuration": {
                "entityId": "",
                "contentUrl": "https://Teams.rocks",
                "removeUrl": "",
                "websiteUrl": "https://Teams.rocks"
            }
        }
    ]
}

 

Lets also have a look at the example from the API references.

POST https://graph.microsoft.com/beta/teams/{id}/channels/{id}/tabs

{
  "name": "My Contoso Tab",
  "teamsAppId": "06805b9e-77e3-4b93-ac81-525eb87513b8",
  "configuration": {
    "entityId": "2DCA2E6C7A10415CAF6B8AB6661B3154",
    "contentUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154/tabView",
    "websiteUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154",
    "removeUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154/uninstallTab"
  }
}

 

We’re going to need to give the tab a name, reference the correct teamsAppId and provide a URL.

POST https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/19:3244b3fd6f8a42d390a79706b68678f1@thread.skype/tabs

Request body:

{
  "name": "Teams.rocks",
  "teamsAppId": "com.microsoft.teamspace.tab.web",
  "configuration": {
    "entityId": "2DCA2E6C7A10415CAF6B8AB6661B3154",
    "contentUrl": "https://Teams.rocks",
    "websiteUrl": "https://Teams.rocks",
    "removeUrl": ""
  }
}

 

There you go, a new website tab called Teams.rocks pointing at https://Teams.rocks.

Teams.rocks-website-tab.png

 

What about apps?

To add a tab for an app we need to first add the app itself to the team. In this example I will continue along the education scenario, which actually fits very well since schools need to provision a large amount of teams every summer and often have a requirement to have the same set of tabs in every team.

In order to get the necessary information we repeat the procedure from earlier: add the app to a team and run a GET querying apps. When we’ve got the teamsAppId, all we need to do is run a POST with with the teamsAppId in the request body.

POST https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/apps

Request body:

{
  "id": "95bab789-a64a-4cbf-a83e-70b7a7b06193"
}

 

We can see the Skooler app was added to the team by the user logged on to Graph Explorer.

Added_Skooler.png

 

Next we need to add the corresponding tab in the appropriate channel.

POST https://graph.microsoft.com/beta/teams/74bacc8d-dd90-4b5a-aba2-72cf9c044eea/channels/19:3244b3fd6f8a42d390a79706b68678f1@thread.skype/tabs

Request body:
{
  "name": "Skooler",
  "teamsAppId": "95bab789-a64a-4cbf-a83e-70b7a7b06193",
  "configuration": {
    "entityId": "",
    "contentUrl": "https://example_app_url.azurewebsites.net/",
    "websiteUrl": "",
    "removeUrl": ""
  }
}

Response:

{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#teams('74bacc8d-dd90-4b5a-aba2-72cf9c044eea')/channels('19%3A3244b3fd6f8a42d390a79706b68678f1%40thread.skype')/tabs/$entity",
    "id": "c697cf16-41f2-478f-8198-3610bb530208",
    "name": "Skooler",
    "teamsAppId": "95bab789-a64a-4cbf-a83e-70b7a7b06193",
    "sortOrderIndex": "10100",
    "webUrl": "https://teams.microsoft.com/l/channel/19%3a3244b3fd6f8a42d390a79706b68678f1%40thread.skype/tab%3a%3a94876f30-ecce-4476-b1a4-0bbfc8aa2f51?label=Skooler&groupId=74bacc8d-dd90-4b5a-aba2-72cf9c044eea&tenantId=6ac1a27f-51d8-47e6-8485-5ee4f0e58fee",
    "configuration": {
        "entityId": "",
        "contentUrl": "https://example_app_url.azurewebsites.net/",
        "removeUrl": "",
        "websiteUrl": ""
    }
}

 

The Skooler app is now available as a tab within the General channel.

Skooler_app.png

Stay tuned for more in part two of this series, where you will learn how to combine what you learned above with a bit of PowerShell automation magic to add tabs as part of a team creation process.

7 thoughts on “Adding apps & tabs in Microsoft Teams using Graph – part 1

  1. Pingback: Create new tabs in your Teams in Microsoft Teams using Power Automate

Leave a comment