Vai al contenuto

Integrazione Microsoft Teams

Un'integrazione Microsoft Teams può essere aggiunta alla vostra installazione di Drovio Server. Permette di avviare e di raggiungere sessioni Drovio da Microsoft Teams, tramite la casella di scrittura o tramite un comando del bot. Si presenta sotto forma di un file JAR che aggiungete a Drovio Server e poi configurate. Dovrete creare un Azure Bot e un'applicazione in Microsoft Entra ID.

Dichiarare il bot in Azure

La dichiarazione del bot può avvenire dal portale Azure, dall'Azure CLI o da Terraform. I tre metodi portano allo stesso risultato: una registrazione di applicazione, un secret client e una risorsa Azure Bot con il canale Teams attivato.

Creare la risorsa Azure Bot

Nel portale Azure, scegliete Create a resource, cercate Azure Bot, poi Create.

Campo Valore
Bot handle Un nome univoco, per esempio Drovio
Subscription L'abbonamento da fatturare
Resource group Un nuovo gruppo, per esempio Drovio
Pricing tier Free (F0)
Type of app Single Tenant
Creation type Create new Microsoft App ID

Far puntare il bot al vostro server

Aprite la risorsa, poi Settings e Configuration. Compilate il Messaging endpoint con:

https://<your server>/msteams/api/messages

Punto di accesso pubblico

La rotta /msteams/api/messages deve essere raggiungibile in HTTPS dai server di Microsoft. Se il vostro Drovio Server è dietro un firewall o un proxy inverso, fate in modo che questo percorso sia aperto su Internet.

Applicate, poi andate in Settings e Channels, e aggiungete Microsoft Teams.

Rilevare i tre valori di cui il plugin ha bisogno

Sempre sotto Settings e Configuration, fate clic su Manage Password accanto al Microsoft App ID. Si apre così la registrazione di applicazione in Microsoft Entra ID.

Nel riquadro Overview, rilevate:

Valore in Azure Usato come
Application (client) ID client_id
Directory (tenant) ID tenant_id

Andate poi in Certificates & secrets, scheda Client secrets, e fate clic su New client secret. Copiate subito la colonna Value: viene mostrata una sola volta, e occorre generare un nuovo secret se la perdete. È il client_secret.

Esporre l'API e concedere l'accesso a Graph

Sotto Expose an API, fate clic su Set accanto all'Application ID URI, poi su Save.

Sotto API permissions, aggiungete un'autorizzazione su Microsoft Graph, scegliete Application permissions, e selezionate le due autorizzazioni elencate più sotto. Concedete loro il consenso amministratore.

Dichiarare l'URI di reindirizzamento

Utile soltanto se intendete pubblicare l'applicazione dal pannello di amministrazione anziché caricarla voi stessi.

Sotto Authentication, Add a platform, scegliete Web, e aggiungete l'URI di reindirizzamento:

https://<your server>/admin/access/msteams/publish/callback

Senza di essa, la pubblicazione fallisce con AADSTS500113: No reply address is registered for the application. Se l'accesso all'amministrazione è limitato per indirizzo IP su questo server, anche l'indirizzo dal quale il browser ritorna deve essere autorizzato, altrimenti il ritorno viene rifiutato da un 403.

Sostituite <your server> con l'URL pubblica della vostra istanza, e scegliete il vostro gruppo di risorse e la vostra regione.

Qui l'applicazione precede il bot

Il portale crea la registrazione di applicazione e il bot in un solo modulo. La CLI non può farlo: az bot create richiede l'identificatore di applicazione come argomento obbligatorio, quindi la registrazione deve esistere prima. Il risultato finale è identico.

Fissate prima i cinque nomi, il resto dei comandi potrà poi essere incollato così com'è:

Variabile Ciò che nomina
SERVER L'URL pubblica del vostro Drovio Server
APP_NAME Il nome visualizzato della registrazione di applicazione
BOT_NAME La risorsa bot, che deve essere libera su tutta Azure
GROUP Il gruppo di risorse che ospita il bot
LOCATION La regione del gruppo di risorse
SERVER=https://<your server>
APP_NAME=Drovio
BOT_NAME=Drovio
GROUP=Drovio
LOCATION=westeurope

Create la registrazione di applicazione, il suo service principal, il suo Application ID URI e il suo secret client:

APP_ID=$(az ad app create \
  --display-name "$APP_NAME" \
  --sign-in-audience AzureADMyOrg \
  --web-redirect-uris "$SERVER/admin/access/msteams/publish/callback" \
  --query appId --output tsv)

TENANT_ID=$(az account show --query tenantId --output tsv)

az ad sp create --id "$APP_ID"

az ad app update --id "$APP_ID" --identifier-uris "api://$APP_ID"

CLIENT_SECRET=$(az ad app credential reset --id "$APP_ID" --append \
  --display-name drovio-server --query password --output tsv)

La riga az ad sp create corrisponde al passaggio che il portale vi nasconde. Crea il service principal al quale sono concesse le autorizzazioni, e senza di esso il consenso amministratore fallisce. L'URI di identificazione è ciò a cui punta il manifest Teams. Il secret client viene mostrato una sola volta, conservate con cura quanto cattura l'ultimo comando.

Chiedete le due autorizzazioni Graph, risolte per nome affinché nessun identificatore debba essere ricopiato. Il consenso arriverà più tardi, alla fine di questa scheda:

GRAPH_ID=00000003-0000-0000-c000-000000000000

USER_READ=$(az ad sp show --id $GRAPH_ID \
  --query "appRoles[?value=='User.Read.All'].id | [0]" --output tsv)
CATALOG_READ=$(az ad sp show --id $GRAPH_ID \
  --query "appRoles[?value=='AppCatalog.Read.All'].id | [0]" --output tsv)

az ad app permission add --id "$APP_ID" --api $GRAPH_ID \
  --api-permissions "$USER_READ=Role" "$CATALOG_READ=Role"

Questo comando mostra un messaggio che vi invita a lanciare az ad app permission grant: ignoratelo, quel comando riguarda le autorizzazioni delegate, mentre queste due sono autorizzazioni di applicazione.

Poi la risorsa bot stessa, e il suo canale Teams:

az group create --name "$GROUP" --location "$LOCATION"

az bot create \
  --name "$BOT_NAME" \
  --resource-group "$GROUP" \
  --app-type SingleTenant \
  --appid "$APP_ID" \
  --tenant-id "$TENANT_ID" \
  --endpoint "$SERVER/msteams/api/messages" \
  --sku F0

az bot msteams create --name "$BOT_NAME" --resource-group "$GROUP"

echo "client_id     $APP_ID (Application ID)"
echo "tenant_id     $TENANT_ID (Directory ID)"
echo "client_secret $CLIENT_SECRET"

Concedere il consenso per ultimo

Il consenso è l'ultimo passaggio, e deve essere verificato. Microsoft Entra ID ha bisogno che la registrazione di applicazione, il suo service principal e le autorizzazioni richieste si siano propagati prima di poter agire su di essi. Lanciato troppo presto, il comando non segnala alcun errore e non concede nulla:

az ad app permission admin-consent --id "$APP_ID"

SP_ID=$(az ad sp show --id "$APP_ID" --query id --output tsv)
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_ID/appRoleAssignments" \
  --query "length(value)" --output tsv

L'ultimo comando deve mostrare 2. Se mostra 0, aspettate un minuto e rilanciateli entrambi. Concedere il consenso richiede il ruolo Privileged Role Administrator o Global Administrator.

Il portale è il ripiego affidabile

Se il conteggio resta a 0, aprite la registrazione di applicazione in Microsoft Entra ID, andate in API permissions e fate clic su Grant admin consent for <il vostro tenant>. La colonna Status mostra allora Granted, e il comando qui sopra mostra 2.

La stessa configurazione, per un'infrastruttura già descritta sotto forma di codice. Richiede il provider azuread in versione 3 o superiore, dove sono comparse le piccole risorse di applicazione componibili. I file qui sotto formano un unico modulo radice.

terraform {
  required_version = ">= 1.0.0, < 2.0.0"

  required_providers {
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 3.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}
provider "azurerm" {
  subscription_id = var.subscription_id
  features {}
}
variable "subscription_id" {
  description = "Azure subscription hosting the bot resource"
  type        = string
}

variable "server_url" {
  description = "Public URL of the Drovio Server instance, without a trailing slash"
  type        = string
}
data "azuread_client_config" "current" {}

data "azuread_application_published_app_ids" "well_known" {}

data "azuread_service_principal" "msgraph" {
  client_id = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
}
resource "azuread_application_registration" "drovio" {
  display_name     = "Drovio"
  sign_in_audience = "AzureADMyOrg"
}

resource "azuread_application_identifier_uri" "drovio" {
  application_id = azuread_application_registration.drovio.id
  identifier_uri = "api://${azuread_application_registration.drovio.client_id}"
}

resource "azuread_application_redirect_uris" "drovio" {
  application_id = azuread_application_registration.drovio.id
  type           = "Web"
  redirect_uris  = ["${var.server_url}/admin/access/msteams/publish/callback"]
}

resource "azuread_application_api_access" "drovio" {
  application_id = azuread_application_registration.drovio.id
  api_client_id  = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]

  role_ids = [
    data.azuread_service_principal.msgraph.app_role_ids["User.Read.All"],
    data.azuread_service_principal.msgraph.app_role_ids["AppCatalog.Read.All"],
  ]
}

resource "azuread_application_password" "drovio" {
  application_id = azuread_application_registration.drovio.id
}

resource "azuread_service_principal" "drovio" {
  client_id = azuread_application_registration.drovio.client_id
}

# The two assignments below are the admin consent
resource "azuread_app_role_assignment" "user_read_all" {
  app_role_id         = data.azuread_service_principal.msgraph.app_role_ids["User.Read.All"]
  principal_object_id = azuread_service_principal.drovio.object_id
  resource_object_id  = data.azuread_service_principal.msgraph.object_id
}

resource "azuread_app_role_assignment" "app_catalog_read_all" {
  app_role_id         = data.azuread_service_principal.msgraph.app_role_ids["AppCatalog.Read.All"]
  principal_object_id = azuread_service_principal.drovio.object_id
  resource_object_id  = data.azuread_service_principal.msgraph.object_id
}

resource "azurerm_resource_group" "drovio" {
  name     = "Drovio"
  location = "West Europe"
}

resource "azurerm_bot_service_azure_bot" "drovio" {
  name                    = "Drovio"
  resource_group_name     = azurerm_resource_group.drovio.name
  location                = "global"
  sku                     = "F0"
  microsoft_app_id        = azuread_application_registration.drovio.client_id
  microsoft_app_type      = "SingleTenant"
  microsoft_app_tenant_id = data.azuread_client_config.current.tenant_id
  endpoint                = "${var.server_url}/msteams/api/messages"
}

resource "azurerm_bot_channel_ms_teams" "drovio" {
  bot_name            = azurerm_bot_service_azure_bot.drovio.name
  location            = azurerm_bot_service_azure_bot.drovio.location
  resource_group_name = azurerm_resource_group.drovio.name
}
output "client_id" {
  description = "Application (client) ID, for the plugin configuration"
  value       = azuread_application_registration.drovio.client_id
}

output "tenant_id" {
  description = "Directory (tenant) ID, for the plugin configuration"
  value       = data.azuread_client_config.current.tenant_id
}

output "client_secret" {
  description = "Client secret, for the plugin configuration"
  value       = azuread_application_password.drovio.value
  sensitive   = true
}

Tre cose da sapere prima di applicare:

  • Il secret client finisce nello stato Terraform, che detiene allora una credenziale verso il vostro tenant e deve essere protetto come tale. Togliete la risorsa azuread_application_password e create il secret a mano se ciò non è accettabile là dove conservate il vostro stato.
  • Le due risorse azuread_app_role_assignment fanno ciò che realizza Grant admin consent nel portale. L'identità che esegue Terraform deve disporre del ruolo Privileged Role Administrator o Global Administrator per crearle.
  • azurerm_bot_service_azure_bot è la risorsa Azure Bot. Non usate la vecchia azurerm_bot_channels_registration, che descrive la generazione precedente di dichiarazione del bot.

Un bot mono-tenant serve il vostro tenant, che è ciò di cui ha bisogno una distribuzione self-hosted. La distribuzione multi-tenant verso i tenant dei vostri clienti è un altro argomento, che passa da AppSource.

Qualunque sia il metodo scelto, il nome del bot conta tra 4 e 42 caratteri e deve essere libero su tutta Azure, tanto che Drovio da solo può essere già preso. La risorsa bot stessa vive nella posizione global, qualunque sia la regione del suo gruppo di risorse.

Il secret client scade

Lo stesso secret autentica il bot quando risponde in Teams e il plugin quando chiama Graph. Alla sua scadenza, l'applicazione smette di rispondere e il pannello perde di vista il catalogo, finché un nuovo secret non viene inserito nel pannello di amministrazione. La sua durata dipende dal modo in cui l'avete creato:

Metodo Durata
Portale A vostra scelta, 24 mesi al massimo, con Microsoft che consiglia meno di 12
Azure CLI Un anno, modificabile con --years su az ad app credential reset
Terraform Due anni, il valore predefinito applicato da Microsoft Entra ID quando end_date_relative è omesso

Annotate da qualche parte la data di scadenza e pianificate la rotazione: create prima il nuovo secret, poi incollatelo nel pannello.

Free o Standard

Il livello Free (F0) consente 10.000 messaggi al mese sui canali premium, il che basta alla maggior parte delle distribuzioni self-hosted. Passate a Standard (S1) se prevedete volumi superiori.

Autorizzazioni Graph

Autorizzazione Ciò che apporta Senza di essa
User.Read.All I nomi e le foto dei partecipanti L'integrazione funziona, le schede non mostrano alcuna foto
AppCatalog.Read.All Il pannello legge il catalogo di applicazioni della vostra organizzazione Il pannello non dice nulla del catalogo, e il suo pulsante propone di pubblicare o di aggiornare senza sapere quale dei due

Sono entrambe autorizzazioni di applicazione, ed entrambe richiedono il consenso amministratore. Il plugin chiede un token nuovo a ogni lettura del catalogo, tanto che un consenso concesso a server avviato si applica già dalla lettura successiva, senza riavvio.

La pubblicazione richiede una terza autorizzazione, la delegata AppCatalog.ReadWrite.All, e funziona diversamente. Non è dichiarata sulla registrazione di applicazione: il pannello di amministrazione la chiede nel momento in cui tentate di pubblicare.

Installare il plugin

Copiate drovio-teams.jar nella cartella dei pacchetti del server, poi dichiaratelo in settings.conf:

"plugin_management": {
  "plugins": [
    {
      "classpath": "/opt/drovio-server/packages/drovio-teams.jar",
      "path": "com.drovio.server.teams",
      "ext": "java",
      "base_address": "drovio.server.msteams"
    }
  ]
}

La chiave è plugin_management. Scrivere plugin_manager dà un plugin che non si carica mai, senza che alcun errore lo segnali. Il base_address vale drovio.server.msteams, indirizzo che l'applicazione Drovio e il server usano entrambi nel loro codice.

Riavviate il server. Una voce Microsoft Teams compare nel pannello di amministrazione, e vi compare soltanto quando il plugin è caricato.

Perché qui serve un riavvio

La maggior parte della configurazione di Drovio Server si applica a caldo, ma i plugin sono caricati solo all'avvio. Qualsiasi modifica di plugin_management, compresa quella di questo plugin, ha effetto solo al riavvio successivo.

Configurare l'integrazione

Aprite la voce Microsoft Teams del pannello di amministrazione.

Campo Da dove viene
Status Enabled o Disabled
Application ID Il client_id rilevato in Azure
Client secret Il secret che avete copiato
Tenant ID Il tenant_id rilevato in Azure
Launch URI Precompilato dall'URL del server
Join URI Precompilato dall'URL del server

Il salvataggio scrive i valori nella configurazione del server e ha effetto già dalla chiamata successiva, senza riavvio. Il secret client non viene più mostrato: lasciare il campo vuoto conserva quello memorizzato.

Prima della 3.6.6, cambiare l'Application ID richiedeva un riavvio

Le versioni precedenti lo leggevano una sola volta all'avvio del server. Il bot continuava quindi a verificare le chiamate in entrata contro il valore precedente e rispondeva 401 a ogni richiesta, registrando Bot authentication failed: Invalid JWT audience.

Passare lo stato a Disabled ferma il bot e le azioni provenienti dall'applicazione Drovio, lasciando comunque accessibile questo pannello per configurare l'integrazione.

Riferimento di configurazione

Ecco le chiavi dell'elemento msteams di settings.conf, se preferite modificarlo direttamente.

Il plugin scrive esso stesso questo elemento al primo avvio che non ne trova alcuno, tanto che un server può essere distribuito con il plugin e poi configurato interamente dal pannello.

Chiave Predefinito Significato
enabled false Attiva l'integrazione. In sua assenza, il valore adottato è false
client_id Application (client) ID del bot
client_secret Secret client del bot
tenant_id Directory (tenant) ID. Vuoto, designa un bot multi-tenant, la cui creazione è abbandonata da Microsoft da luglio 2025
launch_uri <url del server>/launch L'indirizzo verso il quale una scheda Teams manda un utente per avviare una chiamata
join_uri <url del server>/join/app L'indirizzo verso il quale una scheda Teams manda un utente per raggiungere una chiamata

Costruire e pubblicare l'applicazione Teams

L'applicazione è un archivio zip contenente il manifest e le icone. La sezione App package del pannello di amministrazione permette di costruirlo.

Campo Note
Application ID Precompilato dalla configurazione
Application ID URI Precompilato con api://<app id>. Cambiatelo solo se Entra ID ne espone un altro
Version Fornita con il plugin, mostrata a titolo informativo. È l'aggiornamento del plugin a pubblicare una nuova versione

Download vi dà l'archivio da caricare voi stessi, dal centro di amministrazione Teams.

L'altro pulsante vi connette a Microsoft e mette l'applicazione direttamente nel catalogo della vostra organizzazione. La sua etichetta segue ciò che contiene il catalogo: Publish quando l'applicazione non c'è, Update to x.y.z quando ne contiene una versione più vecchia, e Up to date quando non c'è nulla da inviare. È la lettura di questo stato che apporta AppCatalog.Read.All. Senza di essa, il pulsante mostra Publish or update e copre i due casi, il che funziona altrettanto bene.

La pubblicazione richiede un account che disponga del ruolo di amministratore Teams: qualsiasi altro account può solo sottoporre l'applicazione a revisione, la quale attende poi un'approvazione nel centro di amministrazione Teams. Nulla di questa connessione viene conservato: pubblicare di nuovo riconnette.

Il catalogo riflette le politiche di gestione delle applicazioni del tenant, che Microsoft applica entro 24 o 48 ore da una pubblicazione. Un'applicazione pubblicata poco fa può quindi non figurarvi ancora, e il pannello può continuare a proporre di pubblicarla.

Autorizzare l'applicazione e renderla facile da trovare

Nel centro di amministrazione Teams, andate in Teams apps poi Manage apps, cercate Drovio, e passate il suo stato a Allowed.

L'applicazione mostra la foto del profilo dei partecipanti. Nella scheda Permissions, fate clic su Review permissions e concedetele.

Per mettere l'applicazione davanti ai vostri utenti, andate in Teams apps poi Setup policies. Modificate la politica Global (Org-wide default), oppure createne una e assegnatela a un gruppo. Sotto Pinned apps, aggiungete Drovio e ordinatela nell'ambito Messaging extensions.

Ciò che occorre sapere sulle politiche di aggiunta

  • L'applicazione si apre dal + della casella di scrittura. Il client Teams attuale non mostra più le icone delle estensioni di messaggio accanto a questa casella. Non c'è quindi alcuna icona da aggiungere davanti. La politica fissa l'ordine all'interno del menu +, ed è questo che mette Drovio in testa.
  • Una modifica della politica impiega qualche ora a raggiungere i client, e altrettanto un ritorno indietro.
  • Lasciare User pinning disattivato rimuove gli elementi che i vostri utenti hanno aggiunto da soli.