Zum Hauptinhalt springen

Einrichtung

Diese Anleitung führt Schritt für Schritt durch alles, was nötig ist, um einen Microsoft-Teams-Bot mit OctoMesh zu verbinden — zuerst im Azure-Portal, dann die entsprechenden Azure-CLI-(az)-Befehle und schließlich die OctoMesh-Seite (Konfiguration, Pipeline, Teams-App-Paket, Test).

Voraussetzungen​

  • Ein Azure-Abonnement und die Berechtigung, eine App-Registrierung und eine Azure-Bot-Ressource anzulegen.
  • Ein Microsoft-Teams-Konto mit der Berechtigung, eine benutzerdefinierte App querzuladen (ist die Option ausgegraut, muss ein Teams-Administrator Benutzerdefinierte Apps hochladen in der App-Einrichtungsrichtlinie aktivieren).
  • Ein laufender OctoMesh-Mesh Adapter für den Ziel-Tenant (der Messaging-Endpunkt des Bots zeigt darauf).
  • Für die lokale Entwicklung: ein Tunneling-Werkzeug wie Dev Tunnels oder ngrok, um den lokalen Adapter über HTTPS verfügbar zu machen.
Nachfolgend verwendete Benennung

Die Beispiele verwenden die Ressourcengruppe my-rg, das Bot-Handle octo-accounting-bot und die Tenant-ID salzburgdev. Ersetzen Sie sie durch Ihre eigenen Werte.


Schritt 1 — App-Registrierung + Client-Secret​

Die App-Registrierung ist die Identität des Bots. Ihre Client-ID/ihr Secret werden sowohl für die eingehende Authentifizierung als auch für das ausgehende Token von TeamsBotReply@1 verwendet.

Portal​

  1. Microsoft Entra ID → App-Registrierungen → + Neue Registrierung.
  2. Name: octo-accounting-bot.
  3. Unterstützte Kontotypen: Nur Konten in diesem Organisationsverzeichnis (Einzelmandant).
  4. Registrieren, dann Anwendungs-(Client-)ID und Verzeichnis-(Tenant-)ID notieren.
  5. Zertifikate & Geheimnisse → + Neuer geheimer Clientschlüssel → Ablaufdatum festlegen → Hinzufügen → den Wert sofort kopieren (er wird nur einmal angezeigt).

Azure CLI​

# create a single-tenant app registration
az ad app create --display-name "octo-accounting-bot" --sign-in-audience AzureADMyOrg
# -> note the "appId" from the output

# add a client secret (prints the secret once)
az ad app credential reset --id <APP_ID> --append --display-name teams-bot --years 1 \
--query password -o tsv

# your tenant id
az account show --query tenantId -o tsv

Bewahren Sie drei Werte für später auf: App-(Client-)ID, Client-Secret, Tenant-ID.


Schritt 2 — Azure-Bot-Ressource​

Portal​

  1. Ressource erstellen → nach „Azure Bot" suchen → Erstellen.

  2. Bot-Handle: octo-accounting-bot.

  3. Wählen Sie Ihr Abonnement und Ihre Ressourcengruppe.

  4. Tarif: Tarif ändern → F0 (Kostenlos) genügt für die meisten Szenarien.

  5. Microsoft App-ID:

    • App-Typ: Einzelmandant.
    • Erstellungstyp: Vorhandene App-Registrierung verwenden → die App-ID aus Schritt 1 einfügen.
    Wählen Sie nicht „User-Assigned Managed Identity"

    Ein Bot mit Managed Identity hat kein Client-Secret und kann den selbst gehosteten Mesh Adapter nicht authentifizieren. Verwenden Sie Einzelmandant mit der App-Registrierung aus Schritt 1.

  6. Überprüfen + erstellen → Erstellen.

  7. Öffnen Sie auf der Bot-Ressource Kanäle → Microsoft Teams, akzeptieren Sie die Bedingungen und Übernehmen.

  8. Legen Sie unter Konfiguration den Messaging-Endpunkt fest (die URL siehe Schritt 3) und Übernehmen.

Azure CLI​

RG=my-rg
BOT=octo-accounting-bot
APP_ID=<APP_ID> # from Step 1
TENANT_ID=<TENANT_ID> # from Step 1
ENDPOINT="https://<public-host>/salzburgdev/teamsBot" # from Step 3

# create the bot (single tenant, free tier)
az bot create -g $RG -n $BOT --app-type SingleTenant --appid $APP_ID \
--tenant-id $TENANT_ID --endpoint "$ENDPOINT" --sku F0

# enable the Microsoft Teams channel
az bot msteams create -n $BOT -g $RG

# (later) update just the messaging endpoint, e.g. after the tunnel URL changes
az bot update -n $BOT -g $RG --endpoint "$ENDPOINT"

# verify
az bot show -n $BOT -g $RG \
--query "{appId:properties.msaAppId, appType:properties.msaAppType, endpoint:properties.endpoint}" -o json
az bot msteams show -n $BOT -g $RG --query "properties.properties.isEnabled" -o tsv
Überprüfen Sie sowohl den Endpunkt ALS AUCH den Teams-Kanal

Ein fehlender Messaging-Endpunkt oder ein deaktivierter Teams-Kanal äußern sich in Teams beide als „Invalid bot". Die beiden obigen show-Befehle bestätigen, dass der Endpunkt gesetzt und isEnabled true ist.


Schritt 3 — Den Messaging-Endpunkt verfügbar machen​

Der Messaging-Endpunkt ist https://<public-host>/{tenant}/teamsBot, wobei {tenant} die Tenant-ID des Adapters ist (zum Beispiel salzburgdev) und <public-host> den Mesh Adapter erreicht.

Lokale Entwicklung​

Machen Sie den HTTP-Listener des Adapters über einen Tunnel verfügbar (vermeidet den Sprung über selbstsigniertes TLS):

# dev tunnels
devtunnel host -p 5041 --allow-anonymous

# or ngrok
ngrok http 5041

Nehmen Sie die öffentliche HTTPS-URL des Tunnels und setzen Sie den Messaging-Endpunkt auf https://<tunnel-host>/salzburgdev/teamsBot. Mit dem kostenlosen Tarif von ngrok ändert sich die URL bei jedem Neustart — reservieren Sie eine statische Domain oder führen Sie nach jedem Start erneut az bot update --endpoint … aus.

Produktion​

Richten Sie den Messaging-Endpunkt auf die öffentliche HTTPS-Adresse des Adapters (zum Beispiel hinter einem Ingress / Reverse Proxy) und behalten Sie dabei den Pfad /{tenant}/teamsBot bei.


Schritt 4 — OctoMesh konfigurieren​

1. Bot-Anmeldedaten (MicrosoftGraphConfiguration)​

Stellen Sie die App-ID / das Secret / die Tenant-ID als System.Communication/MicrosoftGraphConfiguration-Entität bereit. Bewahren Sie das Secret außerhalb der Versionsverwaltung auf (eine lokale Secrets-Datei, ein Key Vault, …).

- rtId: <config-rtId>
ckTypeId: System.Communication/MicrosoftGraphConfiguration
rtWellKnownName: MicrosoftGraphDocuments
attributes:
- id: System.Communication/AzureTenantId
value: "<tenant id>" # REQUIRED for a single-tenant bot
- id: System.Communication/ClientId
value: "<app (client) id>"
- id: System.Communication/ClientSecret
value: "<client secret>"

2. Pipeline mit FromTeamsBot@1 und TeamsBotReply@1​

Die Pipeline muss eine System.Communication/Uses-Assoziation zur MicrosoftGraphConfiguration tragen (die GlobalConfiguration wird ausschließlich aus Uses-Assoziationen aufgebaut — der Name allein wird nicht aufgelöst).

# on the Pipeline entity
associations:
- roleId: System.Communication/Uses
targetRtId: <config-rtId>
targetCkTypeId: System.Communication/MicrosoftGraphConfiguration

Eine minimale Echo-Pipeline-Definition:

triggers:
- type: FromTeamsBot@1
serverConfiguration: MicrosoftGraphDocuments
route: /teamsBot
validateInboundToken: false # local dev; enable + harden before public exposure
transformations:
- type: TeamsBotReply@1
serverConfiguration: MicrosoftGraphDocuments
messageBody: "Hello from OctoMesh!"
# serviceUrlPath / conversationIdPath default to $.Conversation.ServiceUrl / .ConversationId

Eine echte Pipeline verzweigt anhand von $.Emails[0].Attachments: Anhänge → die Datei(en) einlesen, reiner Text → antworten (z. B. mit AnthropicAiQuery@1), dann über TeamsBotReply@1 antworten, das $.Conversation liest.

3. Bereitstellen​

Importieren Sie die Konfiguration und die Pipeline und stellen Sie den Datenfluss mit der octo-cli bereit (ImportRt, DeployDataFlow). Nach der Bereitstellung protokolliert der Adapter FromTeamsBot: listening on /teamsBot.


Schritt 5 — Teams-App-Paket​

Ein Bot wird in Teams über ein kleines App-Paket erreicht (ein Zip, das manifest.json und zwei Icons enthält). Die bots[].botId im Manifest muss die App-ID des Bots sein, und supportsFiles: true ist erforderlich, damit der Bot in 1:1-Chats Datei-Uploads empfangen kann.

manifest.json
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.17/MicrosoftTeams.schema.json",
"manifestVersion": "1.17",
"version": "1.0.0",
"id": "<app (client) id>",
"developer": {
"name": "Your Company",
"websiteUrl": "https://example.com",
"privacyUrl": "https://example.com/privacy",
"termsOfUseUrl": "https://example.com/terms"
},
"icons": { "color": "color.png", "outline": "outline.png" },
"name": { "short": "Accounting Assistant", "full": "OctoMesh Accounting Assistant" },
"description": { "short": "Upload invoices and ask questions.", "full": "…" },
"accentColor": "#0076D7",
"bots": [
{ "botId": "<app (client) id>", "scopes": ["personal"], "supportsFiles": true }
],
"permissions": ["identity", "messageTeamMembers"],
"validDomains": []
}

Stellen Sie color.png (192×192) und outline.png (32×32, transparent) bereit, komprimieren Sie die drei Dateien im Wurzelverzeichnis des Archivs und dann in Teams: Apps → Ihre Apps verwalten → App hochladen → Benutzerdefinierte App hochladen und wählen Sie das Zip aus.


Schritt 6 — Test​

  1. Öffnen Sie den Bot aus Teams (der Hinzufügen-Dialog nach dem Querladen oder die Suche nach seinem App-Namen).
  2. Senden Sie eine Textnachricht → die Pipeline läuft und TeamsBotReply@1 antwortet im Chat.
  3. Hängen Sie eine Datei an (z. B. ein PDF) → der Bot liest sie ein ($.Emails[0].Attachments[0].Data) und antwortet.

Beobachten Sie das Adapter-Log, um den Roundtrip zu bestätigen:

FromTeamsBot: listening on /teamsBot
FromTeamsBot: processed activity from <user> with <n> attachment(s)

Node-Referenz​

FromTeamsBot@1 (Trigger)​

EigenschaftBeschreibung
serverConfigurationWellKnownName der MicrosoftGraphConfiguration (Bot-App-ID/-Secret + Tenant).
routeRelative Route des Messaging-Endpunkts; das Tenant-Präfix wird vom Adapter hinzugefügt. Standard /teamsBot.
validateInboundTokenDas eingehende Bot-Framework-JWT validieren. Standard false (lokale Entwicklung). Siehe Sicherheit unten.
botAppIdErwartetes Token-Audience; standardmäßig die ClientId der Konfiguration.

Ausgabe: $.Emails[] (Nachricht + Anhänge, gleiche Form wie bei den E-Mail-Triggern) und $.Conversation (ServiceUrl, ConversationId, ActivityId, FromId, FromName, FromAadObjectId).

TeamsBotReply@1 (Load)​

EigenschaftBeschreibung
serverConfigurationWellKnownName der MicrosoftGraphConfiguration.
serviceUrlPathJSONPath zur Bot-Framework-serviceUrl. Standard $.Conversation.ServiceUrl.
conversationIdPathJSONPath zur Konversations-ID. Standard $.Conversation.ConversationId.
replyToActivityIdPathOptionaler JSONPath für eine Antwort im Thread. Standard $.Conversation.ActivityId.
messageBodyPathJSONPath zum Antworttext (z. B. eine KI-Antwort).
messageBodyLiteraler Antworttext (wird verwendet, wenn messageBodyPath leer ist).
timeoutSecondsHTTP-Timeout. Standard 30.
continueOnErrorDie Pipeline fortsetzen, wenn das Senden fehlschlägt. Standard true.

Sicherheit​

validateInboundToken steuert die eingehende Authentifizierung:

  • false (Standard) — keine Authentifizierung. Nur akzeptabel für die lokale Entwicklung hinter einem privaten Dev Tunnel oder dem Bot Framework Emulator.
  • true — das eingehende Bot-Framework-JWT wird geprüft (Audience + Ablauf).
Härten Sie den Endpunkt, bevor Sie ihn öffentlich verfügbar machen

Die aktuelle Eingangsprüfung validiert Audience und Ablauf des Tokens, aber noch nicht dessen kryptografische Signatur. Bevor Sie den Endpunkt öffentlich verfügbar machen (Test/Produktion), platzieren Sie ihn hinter einer vollständigen Bot-Framework-Token-Validierung und weisen Sie nicht authentifizierte Anfragen zurück. Bis dahin kann jeder Aufrufer, der die URL kennt, die Pipeline auslösen.


Fehlerbehebung​

SymptomUrsache / Behebung
„Invalid bot" / „Ungültiger Bot" in TeamsMessaging-Endpunkt leer oder Teams-Kanal nicht aktiviert. Prüfen mit az bot show … --query properties.endpoint und az bot msteams show … --query properties.properties.isEnabled; beheben mit az bot update --endpoint … und az bot msteams create.
Bot-Framework-POSTs kommen an (200), aber keine Pipeline läuftDie ersten Aktivitäten beim Öffnen eines Chats sind conversationUpdate/Typing-Events, die der Trigger absichtlich ignoriert. Nur message-Aktivitäten lassen die Pipeline laufen.
invalid_client / es wird keine Antwort zugestelltFalsche Token-Authority. Für einen Einzelmandanten-Bot muss AzureTenantId auf der MicrosoftGraphConfiguration gesetzt sein (der Antwort-Node verwendet dann die Tenant-Authority).
In einem 1:1-Chat werden keine Dateien empfangenIm Teams-App-Manifest fehlt "supportsFiles": true im bots-Eintrag.
Antwort schlägt mit einem DNS-/Socket-Fehler an die Service-URL fehlErwartbar beim Testen mit einer fingierten serviceUrl; eine echte Teams-Konversation liefert eine erreichbare serviceUrl.
Kein FromTeamsBot: listening on /teamsBot im LogDer Datenfluss ist nicht bereitgestellt, oder der Adapter-Build ist älter als der Node — neu bereitstellen und den Adapter neu starten.