Microsoft Teams Lifecycle - Archive

Updated: Feb 15

Register for free to comment or receive a notifications when a new articles are created.

Story behind the solution


Quite some time ago i start a discussion with my colleagues about how to manage the large amount of Teams that are getting created in our tenant. Some of you know that behind each team there is also a SharePoint site that servers for storing the Team's documents.

That means if you are an enterprise level organization with 5000 users in Office 365, there might be a lot of Teams and SharePoint sites. Most of the Teams will be created for some project, which has completed and the teams were left behind along with the SharePoint site. I'm a person that like to keep everything tidy and organized, so for me it's important to keep only things that are used, that includes Teams and SharePoint sites.


 

The Solution


We look into 3rd party solution, which are great but quite expensive, and I've decided that i should be probably able to create my own solution. For such a big projects, the use of a concept board solution is really helpful. This is how my process is looking from a high level.

We can see that the process will have two phases, 1 Archive the team (set it to read-only), and 2 Delete the team. Since the process is quite big with a lost of steps, I've decided to split the process into two separate one - Archive and Delete. This is also the also reason I've split this into two separate articles. You can find reference to the second part at the end of this page.


During building this process, there were a lot of questions that I had to answer myself

How can I easily get all information I need?
What information I want to back up (Documents, Chat History, Tasks, etc.)?

I've kept it as simple as possible, so i will backup only the Team's documents.


 

How can I easily get all information I need?


Here I've decided to create a SharePoint list that will store information about every team that has been created from the users. I added information such as my own Team Status (later you will see how I'm using it), Team name, Team ID (Perhaps the most important filed in the entire list), this gives me direct and dynamics access to the Team.



Responsible (this is name of the Owner), Last Reminder (this will store the last date when the owner was notifies, and this date is important for the overall process), Archive Team (Yes/No), Delete Team (Yes/No), Delete Date (stores the date when the team was delete).

After having most the data i need in one place, I've started with building the FLow.


 

Parent Flow


Initially when I build the flow, i face my first big problem and potential Show stopper. Because the flow retrieves a list of Teams from my SharePoint list which are active and created more than 6 months ago, the apply to each item can be 10-20-100 items.



That means that if only one item fail, the entire run will be considered as fail. I needed to find a way to handle each Item as a separate run, that way for the failed runs I can do some troubleshooting later on.


Increasing the Concurrency Control for Apply to each action, will only make sure to parallel send 50 messages, but still, the response is coming back to a one flow.


The solution was to use a Child flow which will be run for each item. That way each item is a separate run, and the main flow needs only the child one to complete.

Note: There is one thing to consider when using a Child flows, both Parent and Child flow must be in a solution.


  • Start by creating a new solution in Power Automate


Solutions are great way to move flows along with the connections from one tenant to another.

As you can see both, parent and child flows are inside the solution
  • Once the solution is ready, create the main flow (Parent). In my case I'm running the flow on demand, however here can be recurrence (for example, once every day).

  • Next action will be to get all teams from my Team requests SharePoint list (told you, that the list is very useful). We need to filter the results by getting only teams that are Active (excluding any other team status), which is my Status filed, and are created 90 days ago.

TeamType eq 'Organisational' and TeamStatus eq 'Active' and Created lt 'addDays(utcnow('yyyy-MM-ddTHH:mm:ssZ'),-90)

  • Next step is For each item returned trigger a child workflow. You cannot complete at this point the Parent flow because, you need to pass some information to the Child flow, so save it as it is, and later you will return back to add the TeamID value, which is required by the Child flow.

Child workflow will run separately for each item


 

Child flow

  • For the Child flow we will trigger it manually, and we will add a Number input called TeamID


  • Next step is to get the provided TeamID by the Main flow, to search in our Team Request list by list item id, remember that this list contains most of the information that we need, so it is better if we get the list item id, rather than just the Microsoft Teams group id value that we store there, you will see later why.

TeamID is provided by the parent flow for each item. This is SharePoint list item ID
  • Once we have the team, we need to ask the team owner if he want to keep the team (for example: postpone it with another 90 days), or archive it. For this step we need to post an adaptive card and wait for response.

Post as: Flow bot (but if you have Power Virtual Agent, you can use it as well)

Post in: Chat with Flow bot - this will post the message as a single chat with the user.

Message: is the actual adaptive card JSON, you can use the one provided by me, you create you own one using Teams Adaptive Card designer and then past the code.

{
	"type": "AdaptiveCard",
	"body": [ {
		"type": "Container",
		"style": "emphasis",
		"items": [ {
			"type": "ColumnSet",
			"columns": [ {
				"type": "Column",
				"items": [ {
					"type": "TextBlock", "size": "Large", "weight": "Bolder", "text": "**New Team Request**"
				}
				],
				"width": "stretch"
			}
			,
			{
				"type": "Column",
				"items": [ {
					"type": "Image", "url": "https://adaptivecards.io/content/pending.png", "altText": "Pending", "height": "30px"
				}
				],
				"width": "auto"
			}
			]
		}
		],
		"bleed": true
	}
	,
	{
		"type": "Container",
		"items": [ {
			"type": "ColumnSet",
			"columns": [ {
				"type": "Column",
				"items": [ {
					"type": "TextBlock",
					"size": "ExtraLarge",
					"text": "",
					"wrap": true
				}
				],
				"width": "stretch"
			}
			,
			{
				"type": "Column",
				"items": [ {
					"type": "ActionSet",
					"actions": [ {
						"type": "Action.OpenUrl",
						"title": "View Item",
						"url": ""
					}
					]
				}
				],
				"width": "auto"
			}
			]
		}
		,
		{
			"type": "FactSet",
			"spacing": "Large",
			"facts": [ {
				"title": "Team Name",
				"value": "outputs('Get_item')?['body/Title']"
			}
			,
			{
				"title": "Submitted On",
				"value": "outputs('Get_item')?['body/Created']"
			}
			,
			{
				"title": "Description",
				"value": "outputs('Get_item')?['body/Description']"
			}
			,
			{
				"title": "Department",
				"value": "outputs('Get_item')?['body/Department']"
			}
			]
		}
		]
	}
	,
	{
		"type": "Container",
		"items": [ {
			"type": "ActionSet",
			"actions": [ {
				"type": "Action.Submit",
				"title": "Keep",
				"style": "positive",
				"data": {
					"id": "_qkQW8dJlUeLVi7ZMEzYVw", "action": "keep"
				}
			}
			,
			{
				"type": "Action.ShowCard",
				"title": "Archive",
				"style": "destructive",
				"card": {
					"type": "AdaptiveCard",
					"body": [ {
						"type": "Input.Text", "id": "ArhiveCommentID", "placeholder": "Please specify an appropriate reason for arhiving.", "isMultiline": true
					}
					],
					"actions": [ {
						"type": "Action.Submit",
						"title": "Archive",
						"data": {
							"id": "_qkQW8dJlUeLVi7ZMEzYVw", "action": "archive"
						}
					}
					],
					"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
				}
			}
			]
		}
		]
	}
	],
	"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
	"version": "1.2",
	"fallbackText": "This card requires Adaptive Cards v1.2 support to be rendered properly."
}

As you can see, thank to our SharePoint list, we can get all the information that we want to post to the user regarding the team, such as Team Name, Description, date created, etc.

In the above code, the bold text is the everything that you want to change

Update message: Short confirmation message to the user

Recipient: The team owner


  • Set the Timeout setting for the card to 14 days or anything less than 30 days, as the default timeout is 30 days, mining that if the user do not respond within 30 days, the process will timeout.


Here the time out is set to 3 days P3D

  • Now we need a decision step, where for each response we need to do something. The filter here is if action equal to Archive body('PostAdaptiveCardToChatOrChannel')['submitActionId']

  • If the Action is Archive, we want to set the team as Archived, for this step you will need to use Microsoft Graph API. For instruction how to connect Graph API with Power Automate, you can review one of my articles in LinkedIn.

  • We will use the TeamID provided in our Team request SharePoint list (this ID is the group id, and not the list item id input from the first step)

  • Once we archive the team, we need to update the SharePoint list item, by setting the status to Archived and update the date when the team was archived. For the Archive Date filed I want to set the date when the flow was executed, but not in UTC (which is default value) but in my current time zone. We will use this expression: convertTimeZone(utcNow(),'UTC','W. Europe Standard Time','MM/dd/yyyy')

For a full list of time zones, check here
  • Only thing left is to add actions for the Keep action (or No decision). For this we only want to update the SharePoint list item, by setting up the last date when the owner was notified, so we can use the same formula as above.


  • Last Action is to Respond to the parent flow. In my case the Response is the action (Archive or Keep) body('PostAdaptiveCardToChatOrChannel')['submitActionId']


This is it! It's time to test the flow. You need to trigger the Parent flow only.


The Parent flow with finish once all child flows completes, this is why we set a timeout of the adaptive card to 14 days, so if the user don't respond within 14 days, the parent flow will never timeout.



Stay tuned for Part 2, where we will go trough the Delete process, which will trigger after a team is archived.


40 views0 comments

Recent Posts

See All