Following David Lee’s blog post, Azure Done Right Series: Automating Azure Resource Group Requests with Logic Apps, we thought it was a good opportunity to show a similar use of Logic Apps for automating New Team Requests – all within an E3 license. If you haven’t already, head over and check out David’s article, as it provides some tips and tricks for creating Forms and Logic Apps.
When it comes to managing Microsoft Teams, workplaces will usually take one of two approaches when it comes to Team creation. They will either let everyone create their own Teams or lock down Team creation entirely.
If everyone can create their own Team, very soon there will be an unmanageable mess of Teams and associated SharePoint sites, along with duplicate team names, a Team for every sport and TV show imaginable, and Teams that get created and never used. Additionally, allowing users to create teams grants Office365 group creation rights, which many organisations are not comfortable with.
The alternative is to lock down Teams creation to IT. This puts pressure on IT Teams, loading them up with simple tasks, and wasting time that could be better spent elsewhere. Most IT departments are busy, so there will be an inherent delay in having the Team approved and manually created. This slows down collaboration when users need to create Teams to get their job done.
To address the Teams creation issue, a quick solution we implemented recently for a client was to use an email-based approval workflow using Microsoft Forms, Azure Logic Apps and the Microsoft Graph API.
This allowed for any user to request a new Team quickly and easily, and the person responsible for approving new teams could have the Team created just by clicking a single “approve” button in an email.
Overview
An overview of the process is as follows:
- Microsoft Forms will be used as the mechanism for a new team request
- The form will supply the following information
- Team Requestor/Owner
- Team Name
- Team Description
- Justification
- Form submission will trigger the Logic App, which will
- start an email approval process
- If approved, utilise an Azure App Service Principal to create the Team via the Microsoft Graph API
- Add the team to a lifecycle policy (It’s good practice)
- Notify the requestor that their team has been approved and created, or rejected.
- Send notification emails if there are failures in creating a team or adding to policy.
- The Logic App will require a service account for reading form responses and sending email notifications
It’s worth noting that we could use Automate (AKA Microsoft Flow) instead of a Logic Apps, however an automated workflow that can communicate with an App Service requires a full power app or power automate license.
A Logic app provides the same functionality but will be charged on a per-execution basis. Unless we are executing hundreds of times per day, a Logic App will be much more cost effective than a Power Automate license. Fun fact – Power Automate sits on top of the same platform as Logic Apps!
Create a service account
The Logic App should run under an account that does not belong to a user, so if there isn’t one already available, create a service account for Office 365 automation. This account will require an Office 365 license to access Microsoft Forms, and a mailbox to send workflow emails.
Creating the request form
Head over to Microsoft Forms, sign in with the service account, and create a form that will capture the following information:
- Team Name
- Team Description
- Request Justification
Select the Only People in my Organisation can respond and Record Name options in form Settings. The form thank you message can also be customised here.
Creating the Azure App Service Principal
- Within portal.azure.com, navigate to the Azure Active Directory pane and select App Registrations to create a New App Registration
- For the Name use something descriptive like “Logic Apps MS Teams Automation Service Principal”
- For the account type, use the default: Accounts in this organisational directory only ([TenantName] only – Single tenant)
- A RedirectURI is not required
Assigning API Permissions
We need to assign permission to the Service Principal to allow teams creation via the Microsoft Graph API
- Select API Permissions in the left pane, then Add a permission
- Microsoft Graph will be listed under Commonly used Microsoft APIs, select it
- Select Application Permissions
- Search for Directory
- Select Directory.Read.ReadWrite.All
- Search for Group
- Select Group.ReadWrite.All
- Search for User
- Select User.Read.All
- Select Add Permissions
Permissions will be added and prepared for Administrator consent.
After a short delay, click the “Grant admin consent for ” button. We will need to sign in with an account that has rights to grant consent (Global Administrator) and Accept the requested permissions.
After assigning the “User.Read.All” permission, the default delegated “User.Read” permission is no longer required, so click the ellipsis next to “User.Read” and remove it. Once removed, revoke admin consent to remove it entirely.
Our final API Permissions will be as per below:
These permissions are required so that the service principal can perform all the necessary functions in our tenant to create Teams via the Graph API. We are assigning Application permissions rather than Delegated permissions. Application permissions allow an application in Azure Active Directory to act as its own entity, rather than on behalf of a specific user. Delegated permissions allow an application in Azure Active Directory to perform actions on behalf of a particular user.
Create a Client Secret
The logic app requires authentication to connect to our app service. To do this, we need to create a client secret. A good choice is to use Azure Key Vault and Azure Automation to take care of managing and rotating secrets. However, for this example we will keep it simple and create a secret with a set expiry within our app service principal. Once the secret expires, we will need to renew it and update our Logic App.
- Open the Certificates & Secrets pane and create a New Client Secret
- Provide a Description and define an expiry period – 1 year is fine
- Once you click Add, the new Client Secret will be displayed (obfuscated in red below)
- Note this down as you won’t be able to retrieve it later. You should treat this secret as you would credentials to any privileged account in your tenancy
- Once you have the secret recorded in a secure location return to the Overview pane
- Make note of the Client ID and the Directory ID. These plus the secret will be how you provide access to the Logic App
Lifecycle Policy
It’s good practice to assign a Lifecycle Policy to your Office 365 groups so they are removed when no longer in use. However, since you probably don’t want to enable expiry for every group in your tenant, the policy will be applied selectively. It’s important that newly created Teams have the policy applied, so our Logic App will complete this step for us.
If you are not using expiration, or are applying expiration to all groups, you can skip this section.
- Within portal.azure.com, navigate to the Azure Active Directory pane and select Groups
- In the left pane, select Expiration
- Select a Group lifetime and Email Contact
- Select Selected for Office 365 groups
Once we have created the Lifecycle policy, we will need to retrieve the ID of the policy so we can use it in the logic app.
The easiest way is via PowerShell with the Get-AzureADMSGroupLifecyclePolicy command, which is part of the AzureAD PowerShell module. Make note of the Id for later.
Create the Logic App
We now have everything we need to create the Logic App. We will need to add the Logic App to either an existing or a new resource group.
- pen the Logic App Designer and select a Blank Template to start
- Search for Microsoft Forms and use the option When a new response is submitted
We will need to connect to Forms, so we use our service account created earlier.
- Select the New Team Request form
- Add a new step, Get response details, and in the Form Id field select the form
- For Response Id select Add Dynamic Content, and List of response notifications Response Id (you may need to click “See more” before you can choose this option.)
We now need to initialise some variables for use in our Logic App.
- Select New step, Variables, and Initialise variable.
- Create the following Variables of type String, using the values noted earlier
- directory_Id
- The Id of our azure tenant
- application_id
- Id of our service principal
- secret
- The client secret for our service principal
- lifecycle_policy
- The Id of our tenant’s lifecycle policy
- directory_Id
Whilst it’s not needed, for readability it’s nice to give each initialise variable step a descriptive name as well.
It’s possible that we might have users complete the form and accidentally add leading or trailing spaces to responses.
If we take form data and use it exactly as entered, there could be situations where bad data could cause certain steps in our Logic App to fail.
In this case we will create sanitised variables from user input, by using the trim function. Trim removes all spaces from a string of text except for single spaces between words.
It’s good practice to always sanitise user input so it’s more difficult to abuse or pass bad data. In this case we are sanitising input purely for app functionality, not for security, which is a whole other topic (maybe for another blog post!).
- Create another variable of type string, and in the dynamic content pane select Expression. In the value field enter trim()
- Move your cursor to between the () and add dynamic content, Team Name. The expression will be created. Click ok
Repeat the process for Team Description, and the logic app should look like:
It’s now time to start the workflow with an approval email.
- Add a new step, and search for approval email
- Select Send approval email Office 365 Outlook. Sign in with the service account if prompted
The fields in the approval can be customised, using dynamic content where appropriate. User Options must be Approve, Reject.
The next step will handle the approval results.
- Add a new step. Under Choose an action, select Built-in, then Control and Condition
- In the choose a value field, add dynamic content, and enter SelectedOption is equal to Approve
As it’s a single step, we will complete the If false branch first. We would like to notify the requestor via email if their request for a new team was denied.
- Add an action of Send an email (V2) inside the If false branch
- Use dynamic content to construct the email response
In the If true branch we will create the new team, utilising our App service and the Microsoft Graph API. A useful tool for constructing Graph API calls is the Graph Explorer. If you sign into to the explorer with an account that has the required access rights, you can easily test API calls and view output.
Our first step is a GET request to retrieve the Id of the requestor – the user we will be assigning as team owner.
- Add an action and select HTTP
- Select GET as the method
- In the URI field enter https://graph.microsoft.com/beta/users/?$select=id
- Use dynamic content to insert Responders Email after “users/” and before the “? “
- For Authentication, Select Active Directory OAuth
- Use the directory_id variable for the Tenant
- Use the application_id variable for the Client ID
- Enter https://graph.microsoft.com as the Audience
- Select Secret for the credential type
- Use the Secret variable for the secret
Add a new action.
- Search for json and select Parse JSON
- Using Dynamic Content, select Body from the HTTP GET user Id section
As we add more steps to the Logic App, it becomes more important to name our actions so we can ensure we are selecting the right content.
Add the following code to the Schema field
{
"properties": {
"@@odata.context": {
"type": "string"
},
"id": {
"type": "string"
}
},
"type": "object"
}
Tip: If you have a payload from a HTTP GET, you can utilise the “Use sample payload to generate schema” link in the Parse JSON action. The Schema will be automatically constructed for you. The Graph Explorer is an easy way to retrieve a payload from an API call.
The next action will create the Team and add the requestor as the team owner.
- Add a HTTP action
- Select POST as the method
- Enter https://graph.microsoft.com/beta/teams as the URI
- For Headers, enter
- content-type
- application/json
- In Body, enter the below code
{
"description": "@{variables('TeamDescription')}",
"displayName": "@{variables('TeamName')}",
"owners@odata.bind": [
"https://graph.microsoft.com/beta/users('@{body('Parse_JSON')?['id']}')"
],
"template@odata.bind": "https://graph.microsoft.com/beta/teamsTemplates('standard')"
}
- Re-use the same Authentication methods from the earlier HTTP action
After creating the team, we should add a delay step, as we will shortly be querying the new team name, and it can often take a minute to complete the team creation.
Tip: The Microsoft Graph reference documentation is an invaluable resource for working with the Graph API.
Following delay step, we can check the check the response status code from the HTTP POST for a 202 success.
- Add a Condition
Again, we will deal with the false condition first, and send an email notifying of team creation failure. It’s useful to customise the email with some information so we know where the Logic App failed.
If you are applying a lifecycle policy to all O365 Groups, or not using one at all (not recommended) the logic app is complete, and you can finish the app by notifying user via email that their Team was created.
If you are selectively applying a lifecycle policy, our next step is to add the newly created team to that policy.
To be able to add our team to the lifecycle policy, we need to retrieve the policy Id.
- Add an HTTP GET action under the If true section.
- The URI will be:
https://graph.microsoft.com/beta/groups?$filter=displayName eq '@{variables('TeamName')}' and groupTypes/any(c:c+eq+'Unified')&$select=id
- Re-use the same Authentication methods from the earlier HTTP action
We need to take the output from the HTTP GET, and select the id
- Add a Parse JSON step, Add the following code to the Schema field
{ "type": "object", "properties": { "@@odata.context": { "type": "string" }, "value": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" } }, "required": [ "id" ] } } } }
- Now we have the Id, add a HTTP POST action, which will complete adding the group to the lifecycle policy
- URI: https://graph.microsoft.com/v1.0/groupLifecyclePolicies/@{variables(‘LifecyclePolicyId’)}/addGroup
- Body:
{
"groupId": ""
}
- Use the Dynamic content picker to add in the id output from the Parse JSON step
- As soon as we use the id field, the Logic App will automatically create a “For Each” step for us around the HTTP POST section. This is because the response is provided as an array
Once the POST completes, we need to add a condition to check for return code 200, indicating that the POST was successful, and if it was, send an email to the requestor letting them know their group has been created.
Our logic app is complete! All that remains is to share the form with staff, and we have an automated, simple, and repeatable method of creating new Teams with an easy approval workflow.
We can monitor logic app runs via the Overview pane, where any failures or issues will be highlighted. The runs history pane is especially useful as we can drill into each step and review the results of individual steps.
About Arinco
If you or your organisation wish to find out more about how Arinco can help you with our Teams Done Right approach to designing, implementing and managing Microsoft Teams, please reach out to us via our website.