Automatically provisioning a Teams hosted SPFx webpart

Automatically provisioning a Teams hosted SPFx webpart

Strangely, this was a lot harder than i thought. This post describes the challenges i faced, any my solution to this issue.

>> DISCLAIMER: The methods written here are in no way shape or form supported by nor endorsed by Microsoft, they are absolutely not to be used for production applications and should be considered educative <<

[Jump to the technical section]

The above was perhaps one of my most frustrated Google-queries the past week. Seriously, nobody has done this?

So Teams provisioning is really hot these days, everyone wants a solution to have some governance on their teams, and pre-provision some content to make teams more usable and relevant for the department they’re working in. Some even want to use Teams as a tool, to churn data.

The challenge

So if you’ve made an SPFx webpart, and you want to add it to your Team, you deploy it via the App Catalog, then click the “Sync with Teams”-button, and there it is, ready to be added whenever the user should so desire.

So say you want to deploy this finely crafted SPFx webpart to your newly provisioned Team, but you don’t want the end-user to have to do it. Surely there must be tons of examples online, right? Nope. There are actually none. Why? Because it’s not yet “supported”, there “isn’t functionality for that”. So most solutions you’ll find, involve using one of the standard apps, or the non-user-friendly “Website” Teams app. The issue with that one however, is that you get the super ugly bar saying “If the site isn’t loading correctly, click here”.

As we say in Norway; Off on me. Now you can actually set this to a SharePoint page, but the background will be white, and it stands out as something separate. Somethings’ just not right.

In contrast, adding a TeamsTab SPFx webpart, will be shown with the team’s theme, gray background integrated into the app and even an integrated settings pane for configuring the webpart!

Let’s get technical

Ok, so what’s the big deal? Surely you can just add a webpart to some random Team, fetch the tab configuration using Graph and then just provision it? Surely it must be that simple.. right?

Not really, but let’s go through it to see. We’ll fire up our trusted Graph explorer for this.

First off, create a standard SPFx webpart and enable it for Teams by following the docs. open Teams and add your SPFx webpart to a team.

Next, head over to Graph explorer. Let’s do some queries:

  • https://graph.microsoft.com/v1.0/me/joinedTeams?$filter=displayName eq 'teamname'
    • Get the id from the result, we’ll need it later. For me, the ID for my team is
      6b38dba8-9cbf-4e5f-82e4-b12de6b06ced
  • https://graph.microsoft.com/v1.0/teams/6b38dba8-9cbf-4e5f-82e4-b12de6b06ced/primarychannel
    • Tabs live in the channels, so we need to get the ID of our General channel. Easiest way to do this, is to use the primarychannel endpoint of the team. Note down the id. Mine is:
      19:43ddeed0a99e484b833bff8fdd30e491@thread.tacv2
  • https://graph.microsoft.com/v1.0/teams/6b38dba8-9cbf-4e5f-82e4-b12de6b06ced/channels/19:43ddeed0a99e484b833bff8fdd30e491@thread.tacv2/tabs
    • Now we’re at the end. You’ll see a list of the tabs added to the team. Here’s mine:

The webUrl is the link to the tab. The interesting part here, is the contentUrl. The contentUrl specifies where the tab will navigate when the user opens it. Let’s examine the url more closely:
"contentUrl": "https://smebydev.sharepoint.com/sites/DMRAP-fcew324/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/sites/DMRAP-fcew324/_layouts/15/teamshostedapp.aspx%3Flist=fd4bd3ba-e285-4c3b-9ceb-2469715c0392%26id=1%26webPartInstanceId=aecd337d-4472-4e2f-8cda-dfa257c7f112",

Ok, so what’s happening here is that it first calls the TeamsLogon.aspx-file, which obviously does some kind of login action to Teams. It then calls teamshostedapp.aspx with some parameters. But wait, what is this? list? id? webPartInstanceId? Just when we thought it was going to be easy, it turns out it isn’t!

Luckily, Teams is web-based so we can still dissect what’s going on. This time i’ll save you the walkthrough and just reveal the secret immediately: The list-id and id are references to a hidden list on the site called “Hosted App Configs”. It’s a very special list, which you’re not supposed to play with. However, when you add a new app to a team, Teams does a request to an internal SharePoint api endpoint:

It sends in a hostType, and some webpart info.

This got me interested. It really looks as if it just adding an element to that list, right? So why don’t we just add an element to that list when we automatically provision the webpart, and dust off our hands and call it a days work?

Spoiler alert: The list does not exist until the user has interfaced with apps in Teams. Yup, that’s right, just as the General-folder doesn’t exist until the user has clicked in to “Files” (when you provision with Graph, not with their internal api)…

So this leaves us with a small issue. We really shouldn’t create the list, obviously we then risk actually breaking something, since creation is handled elsewhere. Also I’ve tried it, it didn’t work (although I honestly didn’t put in much effort).

So how can we trigger that list? Well – remember when you configured your manifest for your SPFx webpart? It looks like this:
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=34e2d1bf-92ea-4c72-a4f8-af1e094a863d",

This is actually the page showing the Teams logo before you add your app.

If you right click in the white space, you’ll see it’s an iframe that’s loaded the URL above. It’s the Content URL from our manifest that’s loaded – and it does a very important thing – it initializes the list (and probably a lot of other important things too). Notice also, how the Save-button is deactivated until the page has finished loading.

The solution

Starting to guess what my solution was?

It’s not optimal, i’ll tell you that, but i feel it’s as close to a stable solution as possible which should work

Basically:
1. User clicks “New team” – start provisioning, install the app to the site.
2. Once the team is created, show an iframe like Microsoft does, and track if it’s showing the Teams logo or not. If it is, the site is instantiated, and we can start finalizing he provisioning.

That’s right, the CanvasContent1 field is the webpart markup of the Teams hosted app page, so we can just make our own InstanceId, and update the field with our own webpart markup. We can then reference that instance Id in our tab markup on post-creation

Inspecting the ContentUrl, you see that we’re referencing the ListId, ItemId and our newly created webpart instance Id.

Happily, this works, and the webpart is beautifully rendered and hosted internally. Love it!

What are the drawbacks however?
Well, first off, obviously this is plain and simple “hacking” (in the coding sense). It uses a routine-based approach and doesn’t use an API. It’s obviously something that can change at any moment, and i hope it does. We need an API endpoint to initialize these Team sites.

Another drawback, is that the iframe solution sometimes fails if it’s shown too soon after site creation. I’ve found however that if we just refresh it automatically a couple of times, it pops up as expected.

Another drawback is that we have 0 guarantee that this doesn’t have an effect on other tabs.

But the biggest drawback, is that we still need the user to refresh that iframe…

So in conclusion, yes it’s possible to automatically provision Teams-hosted apps, but right now, not completely without user intervention.

Leave a Reply

Your email address will not be published. Required fields are marked *