Provisioning Teams with a SharePoint Site Template, Power Automate and Microsoft Graph

Provisioning Teams with a SharePoint Site Template, Power Automate and Microsoft Graph

Last updated on September 3, 2023

Microsoft recently added an option to create a team for an Office 365 group via its modern team site. There is a a button at the bottom of the left-hand side navigation that allows you to create a Team with a single click. This is a great change and makes it easier to create a team for a particular group — especially if you are already browsing its site.

But could things be made even easier? Why can’t we just have an option during team site creation to have the team created straight away, automatically? Actually, we can, and in this blog post, I’ll show you how we can make that happen.

To achieve this, we are taking advantage of SharePoint site templates, Microsoft Power Automate and Microsoft Graph. The target is that when a user creates a new modern team site via the SharePoint Online UI, a team will get created for it automatically. The site is created using a custom site template, which will trigger a Power Automate flow, where we’ll call Microsoft Graph to provision the team.

Table of Contents

The Power Automate flow

Let’s create the Power Automate flow first. We’ll start by adding a When HTTP request is received trigger and inserting the schema below into it. We use this trigger, because the site template triggerFlow action makes a HTTP request to our Power Automate flow. The schema matches the body of the request.

{
    "type": "object",
    "properties": {
        "webUrl": {
            "type": "string"
        },
        "parameters": {
            "type": "object",
            "properties": {
                "event": {
                    "type": "string"
                },
                "product": {
                    "type": "string"
                }
            }
        }
    }
}

After that, add a Compose action and use the expression below to extract the last bit of the site URL, so we can use it in other actions.

last(split(triggerBody()?['webUrl'], '/'))

Now save the flow and note down the URL that appeared in the trigger. We’ll need it later when we create our site script.

Calling Microsoft Graph from Power Automate

Before you can call Microsoft Graph from Power Automate, you need to set up an application registration in your Azure Active Directory. To do that, follow the instructions I’ve given in my other blog post: How to set up an Azure AD application registration for calling Microsoft Graph. We are going to use client credentials for authentication, so just follow the instructions under the application identity chapter. When setting the permissions, select Sites.Read.All and Group.ReadWrite.All permissions.

After your app registration is set up, note down the tenant ID, client ID and client secret as described in the Information required for authentication chapter. In Power Automate, add three Initialize variable actions and insert those values into them. We will need to use these values in three different places, so it will be a lot faster to configure those actions when you can just select them from dynamic content. Also, if you later want to copy this flow to a different tenant, you only need to update the values in these three variables to get your flow working against a new application registration.

Getting the group ID

Before we can create a Team for an Office 365 group, we need to know its group ID. When a user creates a new Office 365 group enabled team site using our site template, the group gets created automatically, but the only information we get in Power Automate about the created site is the site URL we extracted earlier. Quite often the site name in the URL matches the group mailNickname, but not always. Because of this, we can’t simply fetch the group information from Graph with query $filter=mailNickname eq ‘siteurl’. Instead, we need to take a little detour.

We first need to get the site object from Graph using the site URL we extracted earlier (Output in the image below), and parse the response, so we can use the response content in other actions.



Schema:

{
    "type": "object",
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "createdDateTime": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "lastModifiedDateTime": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "webUrl": {
            "type": "string"
        },
        "displayName": {
            "type": "string"
        },
        "root": {
            "type": "object",
            "properties": {}
        },
        "siteCollection": {
            "type": "object",
            "properties": {
                "hostname": {
                    "type": "string"
                }
            }
        }
    }
}

 

The response includes the site ID, which we can use to get the Documents library of the site, also known as the drive. The drive is shared by both the site and the Office 365 group objects, and as you can see from the response schema, the information we receive includes information about the owner > group > id. Right there is the group ID we need.


{
    "type": "object",
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "createdDateTime": {
            "type": "string"
        },
        "description": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "lastModifiedDateTime": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "webUrl": {
            "type": "string"
        },
        "driveType": {
            "type": "string"
        },
        "createdBy": {
            "type": "object",
            "properties": {
                "user": {
                    "type": "object",
                    "properties": {
                        "displayName": {
                            "type": "string"
                        }
                    }
                }
            }
        },
        "owner": {
            "type": "object",
            "properties": {
                "group": {
                    "type": "object",
                    "properties": {
                        "email": {
                            "type": "string"
                        },
                        "id": {
                            "type": "string"
                        },
                        "displayName": {
                            "type": "string"
                        }
                    }
                }
            }
        },
        "quota": {
            "type": "object",
            "properties": {
                "deleted": {
                    "type": "integer"
                },
                "remaining": {
                    "type": "integer"
                },
                "state": {
                    "type": "string"
                },
                "total": {
                    "type": "integer"
                },
                "used": {
                    "type": "integer"
                }
            }
        }
    }
}

Creating the team

During Ignite, Microsoft added support for application permissions for the Create team operation. Until then, you were required to authenticate to Graph using a set of user credentials with a Teams license to be able to utilize the operation. When a set of service credentials were used to take care of the automatic provisioning, it could become a problem in large organizations, because a single user is allowed to create only 250 teams. But luckily that is no longer the case and we are able to use application permissions for this operation as well.

Here is an example of the body, but you can of course alter the team settings to fit your purpose.

{
  "memberSettings": {
    "allowCreateUpdateChannels": true
  },
  "messagingSettings": {
    "allowUserEditMessages": true,
    "allowUserDeleteMessages": true
  },
  "funSettings": {
    "allowGiphy": true,
    "giphyContentRating": "strict"
  }
}

 

Now our Power Automate flow is ready and overall it should look like this. Make sure you’ve saved it, and let’s move on to create a site script for our site template.

 

Creating a SharePoint Site Template to trigger our Power Automate flow

Create a site script (JSON file) with the following content. Replace the URL with the one from your flow trigger.

{
    "$schema": "schema.json",
    "actions": [
        {
            "verb": "triggerFlow",
            "url": "https://prod-57.westeurope.logic.azure.com:443/workflows/9bb732 ...",
            "name": "Team site + Teams", 
            "parameters": 
            { 
               "event": "Microsoft Event", 
               "product": "SharePoint" 
            }
        }
    ], 
    "bindata": { }, 
    "version": 1
};

You might also want to include some other configurations in the site script while at it, such as activating a company branded theme for the site. If you are interested in doing that or want to know more about site templates and site scripts in general, check out my Ultimate Guide to SharePoint Site Templates and Site Scripts. It has been very popular among readers.

When your site script is finished, deploy it with this PowerShell script. You’ll also create the site template for it at the same time. If you have not yet installed the SharePoint Online Management Shell, check this other blog post for instructions.

$adminSiteUrl = "https://laurakokkarinen-admin.sharepoint.com"
$siteScriptFile = "C:\site-script.json" # path to site script file

$webTemplate = "64" # 64 = Office 365 group connected team site
$siteScriptTitle = "Team site + Teams"
$siteDesignTitle = "Team site + Teams"
$siteDesignDescription = "Custom team site template which provisions a Team via Power Automate."
$previewImageUrl = "" # if left empty, the default preview image will be used.

Connect-SPOService $adminSiteUrl

$siteScript = (Get-Content $siteScriptFile -Raw | Add-SPOSiteScript -Title $siteScriptTitle) | Select -First 1 Id

Add-SPOSiteDesign -SiteScripts $siteScript.Id -Title $siteDesignTitle -WebTemplate $webTemplate -Description $siteDesignDescription -PreviewImageUrl $previewImageUrl

 

Now you should be able to see your site template listed in the drop-down menu when creating a new modern team site in SharePoint Online. When you use it, you’ll get a team site and a Teams team for it automatically. You also retain the default Team Site template, which you can still use when you just want a team site but don’t want a team for it. Note that if your tenant has restrictions regarding who are allowed to create Office 365 groups, the custom site template is only visible to those users who can.

Afterword

Workspace provisioning is always interesting. The more we can automate and optimize, the better! I hope you enjoyed reading this article and found it useful. Or perhaps it sparked up your imagination? Let me know in the comments below if you have an interesting idea or if you’ve already built some useful provisioning solutions utilizing the site template -> Power Automate pipeline.

Also, feel free to take a look at my other articles, and follow me on Twitter if you’d like to get a tweet from me when I publish new content. Thank you for reading and until next time!

Laura



19 thoughts on “Provisioning Teams with a SharePoint Site Template, Power Automate and Microsoft Graph”

  • Hello,
    Thanks for this post,

    i get this error on the second step :
    Unable to process template language expressions in action ‘Compose’ inputs at line ‘1’ and column ‘2094’: ‘The template language function ‘split’ expects its first parameter to be of type string. The provided value is of type ‘Null’. Please see https://aka.ms/logicexpressions#split for usage details.’.

    could you please help me ?.

    Regards,
    Firas

    • Hi Firas,

      Sounds like the flow trigger or the Compose action isn’t set up correctly. Please make sure you are configuring them exactly as in the screenshots. The “webUrl” parameter should never be null when the flow is triggered via a site design, so it definitely sounds like there’s something wrong with the configurations.

      Laura

    • I am also getting the same error message: –

      Unable to process template language expressions in action ‘Compose_-_Extract_site_URL’ inputs at line ‘1’ and column ‘2121’: ‘The template language function ‘split’ expects its first parameter to be of type string. The provided value is of type ‘Null’. Please see https://aka.ms/logicexpressions#split for usage details.’.

      I have copied your JSON code and trigger expression exactly to no avail.

      I have also tried using GET and PUT as the method for when the HTTP request is received and tested the trigger.

      Any help would be much appreciated.

      Chris.

    • Hi Gaurav,

      Glad to hear you are finding the content useful! 🙂

      Regarding your question, you are missing the group ID between groups/ and /sites in that URL.

      Laura

  • Hi Laura,
    Thanks so much for this article! It worked fine until a got to the last HTTP action “HTTP – Create team for the new group”. Then I get an error “Specified HTTP method is not allowed for the request target.” Do you have any idea what went wrong?

  • Hi Laura, Thank you for this post, it is very helpful. Would you be able to help with an issue I having related to teams template? I have created a template using the graph API. What I need is every time a team is created using the template, I would like to have a few default folders created in the Files tab. We want to have multiple teams created liked this and each team will have the same set of folders. Thank you!

  • Hi Laura

    I found an issue i can’t really get around.
    If you clone a team using HTTP to graph in flow and the also create a private channel using HTTP to graph in flow you are not able to get the drive id for the private channel not even the SharePoint site for the private channel is available.

    For some reason the private channels SharePoint site and drive and folders are only created once you manually go to the teams application and open the private channel for the first time. I even tested with creating the team and private channel using PowerShell, same issue, your unable to get siteID or driveid or anything from private channel until you actually open the channel for the first time.

    Please let me know if you have a solution for this 🙂

    • Hi Jimmy,

      Even though your problem isn’t exactly related to the topic of this blog post, you have made some interesting discoveries! May I ask how you are attempting to get the site ID or drive ID via Graph for the private channel?

      Just to clarify: In the scenario of this blog post, we fetch the drive information so we can figure out the group ID. When you create a private channel, only a SharePoint site collection is created. There is no separate Office 365 group behind a private channel.

      Laura

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.