Deleting the treacherous Wiki tab as a part of your Teams provisioning process

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.

If you are new to the Teams provisioning concept, read my earlier blog post Teams and SharePoint provisioning: what, why and how? to get acquainted with the topic. Also, if you are interested in adding and configuring the OneNote tab for channels automatically, I have a blog post about that as well.

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.

If you are using one of the operations that return status 202 Accepted as the response for creating the team, you should also poll the URL contained in the Location header, and wait until the operation status has changed to succeeded before proceeding.

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.

Note that it is important that you configure the next action (Set variable – Response status code) to also get executed if the HTTP action fails. Otherwise, the flow will end due to the error response.

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



20 thoughts on “Deleting the treacherous Wiki tab as a part of your Teams provisioning process”

  • Hi again.
    In response to my previous comment, I found that if I don’t compare the variable, but instead use the returned Status Code from the HTTP GET call, the condition returns TRUE.
    Could there be issues with strings/integers and converting these?

  • Hi Laura. Great article and well explained!
    I get an error I find weird, maybe you can help. Can’t post screenshots here, but I’ll try to explain:
    In the “Do until channels are ready” task I fetch the status code variable and the run log tells me the Status Code is set to 200.
    However, the condition where I check whether the Status Code is 200 is returning “False”.
    Have you (or anyone else) experienced this behaviour?

  • I also noticed (on day one of trying to set up / test / use the Wiki) that it is currently impossible to search within a Teams Wiki. WTF?! yep, a wiki with no search feature… go figure.

  • Thanks for the insight on wikis.
    Shouldn’t you be reading the retry-after header when getting a 429 though? This contains the required unthrottle wait time (usually 120 seconds) and also ensure that you don’t get blocked.

    • Hi Anders,

      Yes, that’d be ideal. In that case, separate the handling of the HTTP status code 429 to a separate branch because the Retry-After header is not present with the statuses 404 and 502.

      Laura

  • Recently discovered that at some point provisioning teams by Graph result in having double wiki tabs.
    Also discovered that your solution to catch wiki tabs and delete them didn’t work anymore because Microsoft seems to change something in the backend and when you call GRAPH to GET teamsAppiD you got a “null” result, which is problematic for the rest of the logic. I made a short change in the calls :

    OLD CALL : https://graph.microsoft.com/beta/teams//channels//tabs?$filter=teamsAppId eq ‘com.microsoft.teamspace.tab.wiki’
    NEW CALL : https://graph.microsoft.com/beta/teams//channels//tabs?$filter=displayName eq ‘Wiki’

    It’s not the best solution but it helps me to fix the issue and now i’m now able again to catch and delete wiki tabs in my provisioning process 😉

    Any other people faced this issue too ? I’ve got the “double wiki tabs” in many tenant where i enable Teams Governance / Provisioning through Power Automate.

    Again thx @Laura for this solution to remove Wiki Tabs 🙂

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.