Deleting the treacherous Wiki tab as a part of your Teams provisioning process
Last updated on October 16, 2021
Whenever you create a new channel for a team, a Wiki tab is automatically added to the channel. I don’t know who has originally thought it to be a good idea, because using the Wiki tab can easily lead to a permanent loss of information.
The information written in the Wiki gets stored in a hidden SharePoint library called Teams Wiki Data on the team site connected to the team (https://yourtenant.sharepoint.com/sites/yourteamsite/Teams Wiki Data). When the Wiki tab is removed from a channel, the information in that library is automatically deleted. However, instead of ending up in the SharePoint recycle bin, the Wiki content gets deleted permanently on the spot, and there’s no way of recovering it. If the Wiki tab is readded to the channel, the channel wiki starts again from blank.
“We keep putting Wiki tabs on all of your Teams channels so you can live on the edge knowing that wiping your entire knowledgebase is just two clicks away!”
There’s always the option of training users to do things in a certain way. Use the OneNote tab instead of the Wiki tab! But that relies on people’s ability to remember to do the right thing. As a developer, I always try to think if we can avoid potential issues entirely through automation. In this case, that is possible by deleting the wiki tab as a part of a Teams provisioning process.
Deleting the Wiki tab as a part of your Teams provisioning solution
In this blog post, I’ll show you one example of how to handle deleting the Wiki tabs in Teams provisioning solutions. As always, I love describing processes with Azure Logic Apps or Power Automate, because of their colourful appearance and clarity to all readers regardless of their developer background.
The importance of the retry logic
Deleting the Wiki tab starts by us fetching all the channels for the newly created team. I’m not showing the creation of the team in this blog post; just imagine that it has already happened, and we’ve got to know the team ID (which is the same as the group ID) as a result of that.
Whenever we use Microsoft Graph to create a new team, it takes a little while for it and all of its resources to get provisioned in the background. Because of this, it is good to check for the response status code, and have some kind of retry logic in place just in case the resource we are attempting to manage is not yet ready.
At the beginning of my flow, I’ve initialized a variable StatusCode which I’m using for tracking the response status code, so I know if I need to retry the request again. I’m hoping to immediately get status code 200 which indicates Success, but alas, the reality is often quite different.
If the response contains status code 404 Not Found, it means the team or the channels are not yet ready. We should wait a moment before attempting to fetch them again.
Another reason for the retry logic is throttling. Lately, throttling has been happening very frequently with Teams related operations. It is most likely because of the enormous increase in Teams usage in general. Sometimes the response contains the correct “throttled” status code (429, Too Many Requests), but sometimes the status gets returned as 502, Bad Gateway. Regardless, we can solve the problem by trying the request again after a little while.
Whenever we receive one of the expected error codes (404, 429 or 502), we stop to wait for 10 seconds. After that, no matter which one of the errors we’ve received, we set “404” to the StatusCode variable, because that’s the value that allows us to keep on looping. If you find that misleading, you can change the value to anything you want, as long as you change your loop to track that same value.
If the status code is something else than one of the acceptable error codes or 200 (Succeeded), it means that a more severe problem has occurred which can’t be solved simply by retrying. In that case, we exit the loop without doing anything, and consequentially the flow will eventually fail.
Fetching and deleting the Wiki tabs
We, of course, hope that instead of getting an error, we succeed in requesting a list of all the team channels — if not on the first try, then on one of the retries. When we do get the success status code (200), we can finally parse the response body into an object which properties can easily use in later actions.
Click to view the complete schema{ "type": "object", "properties": { "value": { "type": "array", "items": { "type": "object", "properties": { "description": {}, "displayName": { "type": "string" }, "id": { "type": "string" } }, "required": [ "description", "displayName", "id" ] } } } }
After fetching all the team channels, we proceed to get the tabs for each channel. In the request, we want to filter the tabs based on their type, so we only get the Wiki tabs in the response. Note that the teamsAppId property currently only exists in the beta endpoint, so to apply this kind of filtering, we are required to use the beta version of the operation for performing this step!
If the request succeeds, we again parse the response body into an object. You should also introduce similar retry logic here as what I showed earlier because throttling might happen at this point as well.
Click to view the complete schema{ "type": "object", "properties": { "@@odata.context": { "type": "string" }, "@@odata.count": { "type": "integer" }, "value": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "displayName": { "type": "string" }, "teamsAppId": { "type": "string" }, "sortOrderIndex": { "type": "string" }, "messageId": {}, "webUrl": { "type": "string" }, "configuration": { "type": "object", "properties": { "entityId": {}, "contentUrl": {}, "removeUrl": {}, "websiteUrl": {}, "wikiTabId": { "type": "integer" }, "wikiDefaultTab": { "type": "boolean" }, "hasContent": { "type": "boolean" } } } }, "required": [ "id", "name", "displayName", "teamsAppId", "sortOrderIndex", "messageId", "webUrl", "configuration" ] } } } }
Finally, we are in business! After all that hassle, deleting the Wiki tab is just a matter of executing a single HTTP request. Even though we are expecting to receive only one Wiki tab, we do the deletion within a loop because whenever we use the $filter
OData parameter while querying content from Microsoft Graph, we get a collection of objects. Also, it isn’t unheard of that sometimes a channel gets created with multiple Wiki tabs for some reason.
Consider implementing retry logic for this step also. Otherwise, that’s it! A lot of looping and error handling, but they are all worth the trouble in the end.
What about future channels?
Deleting Wiki tabs as a part of a Teams provisioning solution is just the start. Ideally, of course, we create all the necessary channels during the initial setup, so the team owner doesn’t need to set up any new channels for the team afterwards. But what if a need still arises at some point, and the owner creates a new channel manually? The Wiki tab will continue to get automatically added for those. Unfortunately, there is no way to disable the Wiki app on the tenant level.
Now, you might think how about we create a separate order form for provisioning new channels? The downside of this approach is that we still can’t prevent team owners from creating new channels in the usual way. When it comes to provisioning teams, we can always prevent people from creating teams in the normal way by restricting their ability to create Office 365 groups. For channels, we can only prevent team members from creating them.
We could train the owners to use the channel provisioning process instead, but if we need to start training users, we might as well teach them just to delete the Wiki tab. For example, we could write a document about what are the team owner’s responsibilities and have the user consent to having read them before being able to submit their Teams provisioning order.
A third option is setting up a scheduled script that regularly checks through all teams and deletes any lingering Wiki tabs from channels. This one seems like the most reliable option when it comes to actually preventing people from using the Wiki. It would still be good to inform the users of this kind of process running in the background, though, so they won’t get surprised when the Wiki tabs get snatched away.
I can’t tell you which one of these options is the best practice. I’d say it depends on the organization you are building the provisioning process for. If you have any better ideas, let me know in the comments! I’d love to hear them.
Afterword
Thank you for reading this blog post, it is always appreciated! If you’d like to see more content from me, be sure to check out my social media accounts (Twitter, LinkedIn, YouTube, GitHub) and subscribe for my high quality, low volume Insider Newsletter via the sidebar or the popup form. Other than that, have fun provisioning, and until next time!
Laura
“We keep putting Wiki tabs on all of your Teams channels so you can live on the edge knowing that wiping your entire knowledgebase is just two clicks away!”
That is a FANTASTIC quote. Too bad it is undoubtedly fictional. 🙂
The Wiki app in Teams is yet another in a long string of examples of Microsoft’s lack of effective product planning discipline, and an unfortunate tendency amongst their developer community to re-invent wheels, probably because some Dev somewhere thinks she can do it better than the OneNote team did.
It’s sad that millions of users have to suffer this constant insult to rationality and spend time removing it from their environment, when it should never have been allowed to come into existence in the first place.
Thanks for providing an effective means of purging this evil demon!