Appliquer de la gouvernance sur la création de groupes Microsoft Teams avec Flow, Forms et Microsoft Graph

Djavan ROA
Nov 3 · 10 min read

Par défaut dans Office 365 tous les utilisateurs ont la possibilité de créer une équipe Teams, ce qui est à la fois fort pratique pour ne pas brider la productivité des collaborateurs et source d’angoisse pour les personnes en charge de la gouvernance de l’information dans l’organisation. Dans cet article je vais vous expliquer comment bloquer la création en libre-service et mettre en place un workflow de demande de création d’équipe Teams en utilisant les outils natifs à disposition.

⚠ IMPORTANT : A l’heure où j’écris ces lignes (Octobre 2019) certaines des fonctions du Microsoft Graph que j’utilise sont encore en preview (bêta). Il est fortement déconseillé de les implémenter dans un environnement de production. Cet article apour objectif d’illustrer un cas d’usage avec les fonctions à venir.

Prérequis

L’utilisateur qui va créer et administrer le flow aura besoin des éléments suivants :

  • Une licence “Microsoft Teams”, elle est incluse dans les licences de types Office 365 E1, E3, E5, Business Essential/Premium, etc…
  • Une licences “Microsoft Flow Premium Plan 1” pour utiliser le connecteur “HTTP Request”. Attention, cette licence n’est pas incluse dans les licences de base Office 365 E1, E3, E5, Business Essential/Premium, etc…
  • Pouvoir déclarer une application sur le tenant et déléguer certains droits sur l’API Graph (voir plus loin)

Overview du processus souhaité

Je vais décrire un scénario volontairement simple dans le cadre de cet article : une validation à une étape et l’application de quelques options de paramétrage.

Lors de la création de l’équipe je vais inclure certaines options comme le fait de créer une équipe publique ou privée et laisser l’option d’autoriser ou non l’ajout d’externes (Guests).

La création de la demande

Comme point de départ je vais prendre un formulaire Microsoft Forms. Rien de bien sorcier, quelques champs obligatoires pour collecter les informations nécessaires à la demande. Cf. capture.

Créer une identité applicative

Pour interagir avec le Microsoft Graph nous allons avoir besoin d’une identité applicative et lui déléguer le droit d’appeler les méthodes du Graph dont nous aurons besoin.

Déclarer l’application

Rendez-vous dans le portail AzureAD à l’adresse suivante : https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade

Ajoutez une nouvelle application via le bouton “+ New Registration”.

Donnez un nom, choisissez “… organizational directory only…” puis valider avec le bouton “Register”.

Déléguer les droits à l’application

Navigez vers https://portal.azure.com/#blade/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/AllApps/menuId/

Rechercher l’application que vous venez de créer puis ouvrez là.

Dans l’onglet “Overview” notez le “Application (client) ID” et le “Directory (tenant) ID” dans un coin pour plus tard.

Dans la partie “API permissions” ajoutez les permissions suivantes :

  • Directoy.Read.All
  • Directory.ReadWrite.All
  • Group.Read.All
  • Group.ReadWrite.All
  • User.Read.All

Pour valider la délégation utilisez le bouton “Grant admin consent…”. ⚠IMPORTANT : Il sera demandé de se connecter avec un compte ayant les droits suffisant pour compléter cette étape.

L’application à maintenant les droits pour utiliser le Graph.

Récupérer le Client & Secret

Il nous reste à générer des clés d’accès pour cette application.

Toujours sur l’écran de l’application, dans la sections “Certificates & secrets” générez une clé secrete et notez la dans un coin.

Traiter la demande avec Microsoft Flow

Rendez-vous dans Microsoft Flow : https://flow.microsoft.com/

Créer un nouveau Flow en partant du model vierge.

Donner un nom et sélectionnez le déclencheur “Forms” : “When a new response is sumitted”.

Le déclencheur

La première étape consiste à écouter les soumissions de notre formulaire. Choisir l’événement “Forms” correspondant et l’attacher au formulaire de demande. Le Flow sera maintenant déclenché à chaque fois qu’une personne soumet le formulaire.

Les variables

Pour des raisons de commodité je vais utiliser des variables pour stocker les informations de l’application que j’ai créée plus haut, je les utiliserais dans les étapes suivantes. J’utilise des actions du type “Initialize variable”.

Récupérer les valeurs saisies dans Forms

Je vais maintenant avoir besoin de récupérer les valeurs saisies dans la demande Forms. Cette routine est assez classique, j’ajoute une action “Get response details” qui génère les actions imbriquées suivantes, il reste à fournir quelques valeurs cf. capture.

❗ NOTE : Toutes les actions qui suivent devront être dans l’action “Apply to each” pour avoir accès aux valeurs du formulaire.

Demander l’approbation

J‘utilise une action “Get user profile” pour récupérer le nom du demandeur pour pouvoir l’insérer dans le mail d’approbation.

La demande d’approbation est faite avec l’action “Start and wait for an approval”. Dans mon exemple j’ai choisi de mettre en dur l’adresse de mon approbateur (Assigned to), mais on peut très bien imaginer une suite d’action Flow pour calculer cette valeur (manager direct, responsable de service…).

Pour traiter la réponse on pose une action “Condition” avec comme assertion “Outcome = Approve”.

Envoyer un mail pour notifier le refus

Dans la branche “If no” j’ajoute une action “Send an email notification” pour avertir le demandeur du refus.

Récupérer l‘Access Token OAuth

Dans la branche “if yes”, avant de pouvoir requêter le Microsoft Graph nous allons d’abord récupérer un token d’accès pour l’application que nous avons créée plus haut. Pour cela j’utilise une action de type “HTTP Request” dont voici le paramétrage.

Documentation : https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow

Peek Code :

{

“inputs”: {

“method”: “POST”,

“uri”: “https://login.microsoftonline.com/@{variables('TenantId')}/oauth2/v2.0/token",

“headers”: {

“Content-Type”: “application/x-www-form-urlencoded”

},

“body”: “client_id=@{variables(‘ClientId’)}&client_secret=@{variables(‘ClientSecret’)}&grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default”

}}

Pour lire la réponse de la requête je vais “Parser” le résultat à l’aide d’une action du type “Parse JSON” en lui donnant le schéma attendu pour que Flow comprenne comment lire les valeurs. Il y a de nombreuses valeurs dans la réponse mais je ne m’intéresse ici qu’à “access_token” qui va nous servir ensuite à dialoguer avec Microsoft Graph.

Schema :

{

“type”: “object”,

“properties”: {

“access_token”: {

“type”: “string”

}}}

Créer l’équipe Teams

Nous voici à l’étape de création de l’équipe. Notez qu’à ce stade on n’utilise que le nom et la description, et la visibilité “Privé” par défaut, les autres fonctions seront appliquées plus tard avec une autre API. J’utilise l’Id du demandeur pour définir le propriétaire de l’équipe.

Peek Code :

{

“inputs”: {

“method”: “POST”,

“uri”: “https://graph.microsoft.com/beta/teams",

“headers”: {

“Content-Type”: “application/json”,

“Authorization”: “Bearer @{body(‘Parse_Access_Token’)?[‘access_token’]}”

},

“body”: {

“template@odata.bind”: “https://graph.microsoft.com/beta/teamsTemplates('standard')",

“displayName”: “@{body(‘Get_response_details’)?[‘re57b9f0894524ef3b6b3620c30f82dfd’]}”,

“description”: “@{body(‘Get_response_details’)?[‘rff93071e70434b54920de2f7452dc07a’]}”,

“visibility”: “Private”,

“owners@odata.bind”: [

“https://graph.microsoft.com/beta/users('@{body('Get_user_profile_(V2)')?['id']}')"

]}}}

Documentation : https://docs.microsoft.com/en-us/graph/api/team-post?view=graph-rest-beta

Je récupère ensuite l’id du groupe fraîchement créé en “Parsant” la réponse. L’id se trouve dans “Location” au format “/teams(‘<groud id>’)/operations(‘<autre guid>’)”, il faudra le formater pour extraire l’id qui nous intéresse.

Voici l’expression Flow peu élégante pour extraire l’id :

substring(replace(body(‘Parse_Location’)?[‘Location’],’/teams(‘’’,’’),0,indexOf(replace(body(‘Parse_Location’)?[‘Location’],’/teams(‘’’,’’),’’’’))

J’ajoute ensuite un délai de 60 secondes pour laisser le temps au back office de finir de provisionner correctement notre groupe.

Définir le niveau de visibilité

Dans l’étape de création, le groupe a été créé avec la visibilité “privé” pour suivre une approche “least privilege strategy” en cas d’échec de changement cette valeur dans la suite du flow. Le passage dans cette étape ne se fait donc que si l’utilisateur demande que le groupe soit publique.

Notre première action est donc un test pour le cas où l’on souhaite un groupe “publique” en utilisant la valeur saisie du formulaire.

Dans la branche “If no” je ne met rien.

Dans la branche “If yes” je configure un nouvel appel HTTP, notez que “Outputs” correspond à notre Group ID.

Peek Code :

{

“inputs”: {

“method”: “PATCH”,

“uri”: “https://graph.microsoft.com/v1.0/groups/@{outputs('Format_GroupId')}",

“headers”: {

“Authorization”: “Bearer @{body(‘Parse_Access_Token’)?[‘access_token’]}”,

“Content-type”: “application/json”

},

“body”: {

“visibility”: “Public”

}}}

Documentation : https://docs.microsoft.com/en-us/graph/api/group-update?view=graph-rest-1.0&tabs=http

Définir l’autorisation d’inviter des d’externes

Idem que pour le chapitre précédent on commence par poser une condition selon la saisie du formulaire.

Dans mes deux branches j’appelle la même API de gestion des options du groupe et passe la valeur “True” ou “False” selon le choix de l’utilisateur.

Peek Code (branche “If yes”) :

{

“inputs”: {

“method”: “POST”,

“uri”: “https://graph.microsoft.com/v1.0/groups/@{outputs('Format_GroupId')}/settings",

“headers”: {

“Authorization”: “Bearer @{body(‘Parse_Access_Token’)?[‘access_token’]}”,

“Content-type”: “application/json”

},

“body”: {

“displayName”: “Group.Unified.Guest”,

“templateId”: “08d542b9–071f-4e16–94b0–74abb372e3d9”,

“values”: [{

“name”: “AllowToAddGuests”,

“value”: “True”

}]

}}}

Documentation : https://docs.microsoft.com/en-us/graph/api/groupsetting-post-groupsettings?view=graph-rest-1.0&tabs=http

Notifier le demandeur du succès

Puis finalement on peut notifier le demandeur du succès de sa demande et de la création.

Bloquer la création d’équipe Teams en libre service sur le tenant

⚠ Avant toute chose il faut savoir que chaque équipe Teams est techniquement un groupe Office 365. La seule option (à ma connaissance) pour bloquer la création d’équipe Teams consiste à bloquer la création de groupes Office 365. A l’heure où j’écris ces lignes il n’est pas techniquement possible d’appliquer cette limitation uniquement pour Teams, ce qui veut dire que changer ce paramètre va également bloquer la création de groupes depuis les workloads suivants :

  • Outlook
  • SharePoint
  • Yammer
  • Microsoft Teams
  • StaffHub
  • Planner
  • PowerBI
  • Roadmap

Nous allons limiter la création de groupes Office 365 aux seuls membres d’un groupe de sécurité AD et aux administrateurs des services. Pour effectuer ce changement il faudra s’armer de PowerShell et de la version preview du module AzureAD (A l’heure où j’écris ces lignes, oct. 2019, la version GA ne contient pas encore les commandes que nous allons utiliser). J’ai pour ma part fait ces changements avec la version 2.0.2.53 en preview du module AzureADPreview.

On commence par créer un groupe de sécurité dans AzureAD : https://aad.portal.azure.com/#blade/Microsoft_AAD_IAM/GroupsManagementMenuBlade/AllGroups

J’ai choisi nommer le mien “AllowGroupCreation” :

Depuis une console PowerShell lancez le script suivant (en modifiant préalablement le GroupName) :

$GroupName = “AllowGroupCreation”
$AllowGroupCreation = “False”

Connect-AzureAD

$settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value “Group.Unified” -EQ).id
if(!$settingsObjectID)
{
$template = Get-AzureADDirectorySettingTemplate | Where-object {$_.displayname -eq “group.unified”}
$settingsCopy = $template.CreateDirectorySetting()
New-AzureADDirectorySetting -DirectorySetting $settingsCopy
$settingsObjectID = (Get-AzureADDirectorySetting | Where-object -Property Displayname -Value “Group.Unified” -EQ).id
}

$settingsCopy = Get-AzureADDirectorySetting -Id $settingsObjectID
$settingsCopy[“EnableGroupCreation”] = $AllowGroupCreation

if($GroupName)
{
$settingsCopy[“GroupCreationAllowedGroupId”] = (Get-AzureADGroup -SearchString $GroupName).objectid
}

Set-AzureADDirectorySetting -Id $settingsObjectID -DirectorySetting $settingsCopy

(Get-AzureADDirectorySetting -Id $settingsObjectID).Values

En sortie “EnableGroupCreation” sera à “False” et “GroupCreationAllowedGroupId” correspond à l’id du groupe de sécurité autorisé à créer des groupes Office 365.

Maintenant en se connectant à teams avec un utilisateur qui ne fait pas partie de ce groupe de sécurité, on ne vois plus le bouton de création d’équipe :

Documentation : https://docs.microsoft.com/en-us/office365/admin/create-groups/manage-creation-of-groups?view=o365-worldwide

Conclusion

Nous avons maintenant bloqué la création de groupes Office 365 pour tous les utilisateurs “ordinaire” du tenant et mis à disposition un formulaire de demande de création d’équipe teams. Voici comment se déroule l’expérience utilisateur :

Création de la demande

L’utilisateur créé une demande depuis Forms.

Demande d’approbation

Voici à quoi ressemble la demande d’approbation reçu par mail :

L’approbateur peut saisir son commentaire directement depuis le mail :

Le corp de mail une fois approuvé :

Le mail de confirmation

Le contenu du mail de confirmation envoyé au demandeur :

L’équipe Teams

Le groupe fraîchement créé avec la visibilité demandé.

La gestion des invités externes à l’organisation (Guests)

Si j’ouvre le site SharePoint de l’équipe et cherche à ajouter un membre, le sélecteur me proposera uniquement les utilisateurs internes à mon organisation :

A titre de comparaison voici le même menu lorsque invitation de “Guests” est autorisé :

Ressources

Cet article m’a été inspiré par ce webcast : https://techcommunity.microsoft.com/t5/Healthcare-and-Life-Sciences/Automating-Selective-Guest-Access-in-Microsoft-Teams/ba-p/698138

Djavan ROA

Written by

Demain je serais productif… ou pas… Ma vie de tech dans l’écosystème collaboratif de Microsoft.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade