Programmatically creating a Team in a tenant with a different language
By: Loryan Strant
Tags: Azure, Exchange, Flow, Automation, Graph

Let me start this by saying that this does not aim to be yet another blog post that shows how to create a Team in Microsoft Teams using tools and functionality available in Microsoft 365.

While it does do that, this post outlines how to overcome a very specific challenge: creating a Team in a multi-national tenant where the native language is not your own.

Challenge

Let’s say you work for a multi-national organisation headquartered in The Netherlands. As a result, the default language when creating anything connected to SharePoint (such as a M365 Group or even Team) is Dutch.

While you can set the site’s alternative language to English, connected apps and even your browser will only go so far when it comes to displaying things in the correct language.

Because while I as a user will see this:

What is actually shown in the address bar is this:

The problem here, is that the site needs to be created in English before anything else to ensure that the libraries are created in the right language.

Potential Solutions

In trying to work through this I looked at a variety of solutions that could do everything in one for me. Unfortunately, Graph does not allow us to set the language, so that was an immediate fail.

The closest I came was using PnP to create a PowerShell script that would:

Create the site and attached Group in English (US) — because you can’t choose any variant of English other than US or UK Set the regional settings of the site to use English (Australia), and the time zone to the correct one Add a Team to the Group Configure Team settings (e.g. who can create/delete channels, apps, tabs, etc.) Remove the “Everyone except external users” from the site Members group and add it to the site Visitors group (just because it’s public Group/Team doesn’t mean you should have edit rights without joining it IMHO)

Unfortunately, the Group mailbox still needed to be changed to English (Australia) so that meeting invites showed in the correct format — so I had to add some Exchange PowerShell in there too.

While this seemed to be ok, there were a few catches:

It required a script to be run from someone’s computer

That computer required the PnP and Exchange PowerShell modules to be installed

The Team settings didn’t work in build 1.6.0 of PnP so I had to use either 1.5.0 or any nightly build of 1.6.x after June 11

Different PnP cmdlets require different types of authentications; some -interactive, and some -UseWebLogin

It worked but was a hack and very fallible due to those dependencies.

Perhaps switching to certificate-based authentication against an Azure AD app registration would take care of those varying authentication mechanisms?

No.

It did not.

It failed on the first step.

Actual Solution

A key objective of this, is for the process to be programmatic. To avoid these dependencies and quirks required to operate.

What I ended up with was a solution that is driven by Power Automate with almost no external dependencies such as app registrations.

Unfortunately, it’s still not as clean as I’d like, as the solution actually involves:

Microsoft Forms for end-user input

SharePoint REST API to create the site & group, and remove/add the “Everyone except external users”

Graph to Teamify the Group (although I use and OOTB connector to achieve this)

Azure Automation to call the runbook housing my PowerShell script which executes the Exchange mailbox language change

Outlook to send the email with details

Some human effort to finish off some things that I couldn’t figure out (or ran out of patience to figure out at this point)

Front end

To start the process, users fill in a Microsoft Form that asks a few key questions:

Team name

Team short name (i.e. alias / URL)

Team description

Workflow

At a high-level, the workflow looks like this:

Perhaps make a coffee before continuing, while I break down each section (because I hate blog posts that only show you part of the workflow, or a collapsed action with no details).

First, we capture the Form submission and its details:

I wasn’t able to specify the Form used from a drop-down, because like the good governance citizen I am, the Form is connected to a Group — so therefore you can only refer to it by utilising the Form ID (easily obtained from the URL).

Then, we create the site/Group using REST API, passing details from the Form submission as well as defining the site language as English (US):

Two specific things at this point:

The Group is created public by default. While I could have a condition that instead sets it as private if chosen, this is a tiny detail that I have made the requestor’s task to perform at this stage.

As this workflow is largely used for client Teams and is localised, I have all sites join our local SharePoint Hub site for easier navigation and access.

Next, let’s clean up the outputs from the site/Group creation into something we can utilise later:

Then, let’s have a sip of our coffee and wait for the engine to cool before performing changes to the SharePoint site group membership, as we only just created them and there’s a chance the next steps will fail as they can’t find the groups:

Now that we’ve had that sip, let’s continue on by moving “Everyone except external users” around, again through SharePoint REST API:

Then we’re going to Teamify the site/Group using an action from the Office 365 Groups connector (look ma, no custom connector or app registration!):

At the same time, I’ve applied the Team restrictions that I recommend.

(Side note, I’m not against members having these options — but I feel that it should be up to the owner to either promote members to owners or to deliberately make the choice to enable this level of Team control.)

So far so good. We’re almost done, but we still need to change the language of the Group mailbox to English (Australia). Unfortunately, we can’t do that via Graph, so need to leverage Azure Automation and a runbook with PowerShell script:

(The next part assumes you know what you’re doing when it comes to Azure Automation, otherwise there are a number of great blogs that you can find that explain the process.)

Ensure you have the ExchangeOnlineManagement module added:

Create your runbook:

Use the following script:

The cleaned-up version of this is (without the commented out text from my previous attempts):

param(

[Parameter(Mandatory=$true)]

[string]$TeamName

)

$ErrorActionPreferance = “Stop”

try {

$tenantName = “<YOURTENANTNAME>.onmicrosoft.com”

$connection = Get-AutomationConnection -Name ‘AzureRunAsConnection’

Write-Output “Connecting to Exchange Online”

Connect-ExchangeOnline -CertificateThumbprint $connection.CertificateThumbprint -AppId $connection.ApplicationID -Organization $tenantName

Write-Output “Setting en-AU on team $TeamName”

Set-UnifiedGroup -Identity $TeamName -Language en-AU

Get-UnifiedGroup -Identity $TeamName | fl

}

catch {

Write-Error “Unable to set locale on Exchange mailbox”

}

And finally, let’s send an email to the requestor with their Team details:

The expression used to generate the URL in the email is:

concat(body(‘Parse_JSON’)?[‘d’]?[‘CreateGroupEx’]?[‘SiteUrl’],’/_layouts/15/regionalsetng.aspx’)

And the output looks like this:

And there you have it.

Is it perfect? No, not at all.

Could it be better, do more for the user, have options for public or private Groups/Teams, clean up after itself, add more owners, add members, anything else? Absolutely.

That will come later. Right now it works.

Wait…. hold up… wait a minute… we’re not done yet!!!

If regional and language settings are so important, and we’re setting the Group mailbox to English (Australia), why aren’t we doing the same for the SharePoint site?

Well, this can be achieved using SharePoint site scripts and designs, delivered through another couple of REST API calls like these:

But while this worked in several tenants, it didn’t work in my production work tenant and to be honest I’d run out of steam. So, for that I put the text you saw above in the email with the link directly to the relevant page for the requestor to action.

If you have any recommendations for how to improve this convoluted process, please let me know.

For now, it works, it’s programmatic, it’s systematic, it’s hyyyyydromatic… and it’s not dependent on a human to operate.

(I probably shouldn’t write blog posts at 10pm!)

I would love for all of this to be achieved via Graph API, however unfortunately almost none of it can be, other than the connection of the Team to a Group.