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 accidentally deleted our teams Wiki tab which already had content. Terror!
This is how I found the content from the web UI.
1. Go to Sharepoint’s team page.
2. Select ‘Site contents’ from the left side menu.
3. Select ‘Teams Wiki Data’ –> three dots –> Settings.
4. Select item under ‘Recent’ from left side menu with a long cryptic title.
Thanks, Juho, for the steps for recovering the content! I wonder for how long the item remains under Recent. I wish there was a more “legit” solution for restoring the content.
Laura
Stumbled on this article (after watching one of your Youtube videos) while I was looking up Sharepoint site design for my team and my intention was to build out a Wiki for our knowledge base. I just wanted to see if the Wiki you’re talking about is the same thing as the app I intended to use (and if I should just, ya know, not). I didn’t see a Wiki tab when I created the team site, and it, to my knowledge, wasn’t there by default. I simply added an app called Wiki Page Library (not sure if this is the same thing), which then creates a page library. Is this the data that can get deleted? Or is this something different? I’m just concerned that my direction might be wrong after reading this.
Hi Alexander,
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).
Laura
Hi Laura, thanks for the post – super helpful as usual!
Just wanted to add for others – it IS possible to manually recover the wiki data after someone deletes the tab. You need to unhide the list hidden list (via PnP Powershell), delete the new entries created when the wiki tab was re-added, go to the original entries and set the wikiDeleted boolean field back to false. Reload the teams tab and the wiki hould be back to how it was before the tab was deleted and recreated.
HTH,
Bruce
Hi Bruce,
And wow! How did you come across those instructions somewhere, or did you investigate all of that yourself? If you have a link to some documentation, I’d love to take a look. That’s definitely something that could be useful to some people, even though being able to restore the wiki tab as an end-user via the UI would still be the best option.
Laura
Hi Laura,
Tripped over that solution myself. Not a great one, but at least it could be automated (if you had the will and patience)!
Thanks again,
Bruce
HI Laura, thanks alot.
I was about to add the functionality to our Deployment Powershell framework, and for some reason I decided to drop by your site as I always finds something new and usefull whenever I drop by. Lo and behold, you had already covered the subject 🙂
Hi Kasper! Glad to hear you are finding the content helpful. 🙂
Laura
Hi Laura,
I am trying to copy wiki content after cloning a team. The problem that I have is that wiki content in the new Team is not available (hidden lists in the background) until you click on the wiki tab in the newly created team. Do you know, is there any way to ‘force’ to get provisioned during cloning the team?
Thank you
Marko
Hi Marko,
To my knowledge, there is no way to make that happen. It is similar to the channel email addresses. It doesn’t get created before the first person actually clicks on the “Get email address” option on a channel in the Teams UI.
Laura