データ接続と統合利用可能なコネクターMicrosoft Teams

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

Microsoft Teams

Microsoft Teams コネクターは、Microsoft Teams 用の Palantir 提供ドライバーです。

新しい Microsoft Teams ソースを作成するには、Palantir 提供ドライバーの標準セットアップフローに沿って進め、以下のセクションで Microsoft Teams 固有の設定とネットワーク設定を行います。プロパティの完全なリファレンスについては、Microsoft Teams ドライバーの公式ドキュメント ↗を参照してください。

サポートされる機能

機能ステータス
探索🟢 一般提供
バッチ同期🟢 一般提供
インクリメンタル(差分連携)🟢 一般提供
OAuth 2.0 認証🟢 一般提供
テーブルエクスポート🟢 一般提供

はじめに

Microsoft Teams コネクターは、Microsoft Graph API を介して組織の Teams データを読み取ります。チーム、チャネル、チャット、メンバー、メッセージをリレーショナルテーブルとして表示し、ソースエクスプローラーで一覧を見ることができます。

定期的に読み取る場合は、バッチ同期を使用して Teams データをデータセットに書き込みます。対話的に読み取りや書き込みを行う場合は、関数からコード内で Microsoft Graph を呼び出します。

コネクターがアクセスできる Teams データは、認証と Microsoft Graph の権限によって決まります。認証方法を選択するには認証を、各操作に必要な権限については機能別の権限を参照してください。

認証

Microsoft Teams コネクターは、Microsoft Entra ID を通じて Microsoft Graph に対する認証を行います。AuthScheme を使用して認証方法を選択します。

AuthScheme認証方法使用する状況
AzureAD委任されたユーザー認証ソースへのアクセスをサインインしたユーザーの権限に従わせる必要があり、対話的な権限付与が許容される場合。
AzureServicePrincipalクライアントシークレットを使用するサービスプリンシパルソースへのアクセスをサインインしたユーザーに限定すべきでない場合。Microsoft Graph の権限により、テナント全体へのアクセス、またはリソース固有の同意を通じて選択したチームに限定したアクセスを付与できます。
AzureServicePrincipalCert証明書を使用するサービスプリンシパルユースケースは AzureServicePrincipal と同じですが、組織で証明書ベースの資格情報が必要な場合。

委任されたユーザー認証(AzureAD)

AuthScheme=AzureAD の場合、ソースは対話的にユーザーとしてサインインし、そのユーザーとして Graph を読み取ります。

アプリケーションを登録する

Microsoft Entra 管理センター ↗でアプリケーションを登録し、クライアントIDを記録して、クライアントシークレットを生成し、テナント(ID または contoso.onmicrosoft.com などのドメイン)を控えます。

  • ユースケースに応じて、機能別の権限に記載されている委任された Microsoft Graph の権限を付与します。
  • テナントでこれらのスコープに対するユーザーの同意が制限されている場合は、先にグローバル管理者がアプリケーションに管理者の同意を付与します。
  • ソースを設定する際に、CallbackURL プロパティを追加します。Foundry が自動入力する URL をコピーし、Web リダイレクト URI としてアプリケーションに追加します。

ソースを設定する

AuthScheme=AzureAD、AzureTenant、OAuthClientId、OAuthClientSecret、InitiateOAuth=REFRESH を設定し、自動入力される CallbackURL プロパティを追加します。権限付与を選択し、読み取りたい Teams データを持つユーザーとしてサインインします。Foundry が対話的な OAuth のやり取りを完了し、トークンを自動的に更新します。

AuthScheme を AzureAD に設定した Foundry の Microsoft Teams ソース接続設定と、承認された graph.microsoft.com および login.microsoftonline.com へのネットワークエグレス。

サービスプリンシパル認証(AzureServicePrincipal)

AuthScheme=AzureServicePrincipal の場合、ソースはサインインしたユーザーではなく、アプリケーションとして認証を行います。アプリケーションは、付与された権限に応じてテナント全体のデータを読み取ることができます。

アプリケーションを登録する

委任方式と同様に、Microsoft Entra 管理センター ↗でアプリケーションを登録し、クライアントIDとテナントを記録します。権限は異なります。

  • API のアクセス許可で、ユースケースに必要な Microsoft Graph のアプリケーション権限を追加し、グローバル管理者に管理者の同意を与えるを選択してもらいます。アプリケーション権限は、管理者が同意するまで有効になりません。
  • 証明書とシークレットで、クライアントシークレットを生成するか、組織で証明書の資格情報が必要な場合は証明書をアップロードします。
  • 対話的なサインインがないため、リダイレクト URI は不要です。

Microsoft は、ユーザーなしでアクセスを取得する ↗でフロー全体を説明しています。

ソースを設定する

AuthScheme=AzureServicePrincipal、AzureTenant、OAuthClientId、OAuthClientSecret を設定します。権限付与の手順はありません。証明書の資格情報を使用する場合は、代わりに AuthScheme=AzureServicePrincipalCert を設定し、Microsoft Teams ドライバーの公式ドキュメント ↗に従って証明書のプロパティを設定します。

機能別の権限

ユースケースに必要な Microsoft Graph の権限のみを付与してください。テナントの同意ポリシーによって、追加の制限が課される場合があります。完全な一覧については、Microsoft の Graph のアクセス許可リファレンス ↗を参照してください。

実行したい操作委任アプリケーション
チームとチャネルを発見可能にするTeam.ReadBasic.All および Channel.ReadBasic.All。ユーザーの同意が必要です。Team.ReadBasic.All および Channel.ReadBasic.All。管理者の同意が必要です。
チャネルメッセージの内容を読み取るChannelMessage.Read.All。管理者の同意が必要です。ChannelMessage.Read.All はテナント全体へのアクセスを付与し、管理者の同意が必要です。ChannelMessage.Read.Group はアプリケーションがインストールされたチームへのアクセスを付与し、そのチームに対するリソース固有の同意が必要です。
チャットとチャットメッセージを読み取る—Chat.Read.All。管理者の同意が必要です。
メンバーと所有者の一覧を表示するGroupMember.Read.All、TeamMember.Read.All、ChannelMember.Read.All。管理者の同意が必要です。GroupMember.Read.All、TeamMember.Read.All、ChannelMember.Read.All。管理者の同意が必要です。
チャネルメッセージを送信するChannelMessage.Send。ユーザーの同意が必要です。ChannelMessage.Send.Group。アプリケーションがインストールされている各チームに対するリソース固有の同意が必要です。

データを読み取る

Microsoft Teams データを Foundry に読み込むには、ソースエクスプローラーでテーブルを選択し、バッチ同期を設定します。委任認証では、サインインしたユーザーが利用可能なデータを同期で読み取ります。サービスプリンシパル認証では、アクセスはアプリケーションに付与された権限によって決まり、テナント全体または選択したチームを対象にできます。アクセスを設定するには、認証を参照してください。

レート制限

Microsoft は、GET /teams/{team-id}/channels/{channel-id}/messages に対して、アプリケーションあたり毎秒200リクエスト、アプリケーションおよびテナントあたり毎秒20リクエスト、チャネルあたり毎秒1リクエストの制限を適用します。Microsoft の Teams サービスの制限 ↗を参照してください。

設定

以下のプロパティは必須または推奨です。

プロパティ必須かどうか説明デフォルト
AuthScheme ↗推奨Microsoft Teams に接続する際に使用する認証の種類を指定します。このプロパティを空白のままにすると、デフォルトの認証が使用されます。AzureAD
AzureTenant ↗推奨データへのアクセスに使用する Microsoft Teams テナントを、名前(たとえば contoso.onmicrosoft.com)または ID で識別します。(条件付き)。—
InitiateOAuth ↗推奨OAuth アクセストークンを取得または更新するプロセスを指定します。このトークンは、認証および承認されたユーザーが作業している間、そのユーザーのアクセスを維持します。GETANDREFRESH
OAuthClientId ↗推奨カスタム OAuth アプリケーションの作成時に割り当てられたクライアントIDを指定します。(コンシューマー鍵とも呼ばれます。)この ID により、カスタムアプリケーションが OAuth 認可サーバーに登録されます。—
OAuthClientSecret ↗推奨カスタム OAuth アプリケーションの作成時に割り当てられたクライアントシークレットを指定します。(コンシューマーシークレットとも呼ばれます。)このシークレットにより、カスタムアプリケーションが OAuth 認可サーバーに登録されます。—

ネットワーク

以下のテーブルは、ソースの実行に必要なアクセス先ドメインを示しています。

各ドメインに対応するエグレスポリシーを追加します。ソースがオンプレミスでホストされており、Foundry から直接アクセスできない場合は、代わりに Agent プロキシエグレスポリシーを使用します。Agent ホスト自体も、記載されているドメインにアクセスできる必要があります。詳細については、Agent をプロキシとして使用するを参照してください。

ドメイン必要となる条件
graph.microsoft.comAzureEnvironment=GLOBAL(デフォルト)の場合
login.microsoftonline.comAuthScheme=AzureAD(デフォルト)、AzureServicePrincipal、AzureServicePrincipalCert のいずれかで、かつ AzureEnvironment=GLOBAL(デフォルト)の場合
microsoftgraph.chinacloudapi.cnAzureEnvironment=CHINA の場合
login.chinacloudapi.cnAuthScheme=AzureAD(デフォルト)、AzureServicePrincipal、AzureServicePrincipalCert のいずれかで、かつ AzureEnvironment=CHINA の場合
graph.microsoft.usAzureEnvironment=USGOVT の場合
login.microsoftonline.usAuthScheme=AzureAD(デフォルト)、AzureServicePrincipal、AzureServicePrincipalCert のいずれかで、かつ AzureEnvironment=USGOVT または USGOVTDOD の場合
dod-graph.microsoft.usAzureEnvironment=USGOVTDOD の場合

OAuth 2.0 認証

このコネクターは OAuth 2.0 認証をサポートしています。Palantir 提供ドライバー向けの OAuth 2.0 ガイダンスに従って、接続を設定し、認可します。

テーブルエクスポート

このコネクターはテーブルエクスポートをサポートしています。テーブルエクスポートを設定する方法を参照してください。

コードで Microsoft Teams ソースを使用する

パイプラインまたは関数からチャネルメッセージを読み取るには、Microsoft Teams ソースを使用して Microsoft Graph を呼び出します。サインインしたユーザーとしてメッセージを送信するには、アウトバウンドアプリケーションを設定した REST API ソースを使用します。以下の例では、Python と TypeScript で両方のワークフローを示します。

チャネルメッセージを読み取る(サービスプリンシパル)

これらの例を実行するには、Microsoft Teams ソースをコードリポジトリにインポートします。サービスプリンシパル認証とチャネルメッセージの読み取りに必要な権限を使用してソースを設定します。

プレースホルダーを、ソース、テナント、アプリケーション、チーム、チャネルの識別子に置き換えます。チームとチャネルの識別子は、ソースエクスプローラーの Teams テーブルと Channels テーブルで確認できます。TypeScript v1 の例では、MicrosoftTeamsSource をソースのAPI名から生成された識別子に置き換えます。各例は、最新のメッセージを最大20件取得し、コンテンツを含まないメタデータを返します。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import pandas as pd import requests from transforms.api import Output, transform from transforms.external.systems import ResolvedSource, Source, external_systems SOURCE_RID = "<source_rid>" TENANT_ID = "<tenant_id>" CLIENT_ID = "<client_id>" TEAM_ID = "<team_id>" CHANNEL_ID = "<channel_id>" OUTPUT_RID = "<output_dataset_rid>" @external_systems(teams=Source(SOURCE_RID)) @transform.using(output=Output(OUTPUT_RID)) def read_channel_messages(teams: ResolvedSource, output) -> None: session = requests.Session() proxy = teams.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) token_response = session.post( f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data={ "client_id": CLIENT_ID, "client_secret": teams.get_secret("OAuthClientSecret"), "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials", }, timeout=30, ) token_response.raise_for_status() response = session.get( f"https://graph.microsoft.com/v1.0/teams/{TEAM_ID}/channels/{CHANNEL_ID}/messages", params={"$top": 20}, headers={"Authorization": f"Bearer {token_response.json()['access_token']}"}, timeout=30, ) response.raise_for_status() rows = [ { "id": message.get("id"), "created_date_time": message.get("createdDateTime"), "web_url": message.get("webUrl"), } for message in response.json().get("value", []) ] output.write_table(pd.DataFrame(rows))
Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import json import requests from functions.api import function from functions.sources import get_source SOURCE_ALIAS = "<source_alias>" TENANT_ID = "<tenant_id>" CLIENT_ID = "<client_id>" TEAM_ID = "<team_id>" CHANNEL_ID = "<channel_id>" @function(sources=[SOURCE_ALIAS]) def list_channel_messages() -> str: source = get_source(SOURCE_ALIAS) session = requests.Session() proxy = source.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) token_response = session.post( f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data={ "client_id": CLIENT_ID, "client_secret": source.get_secret("OAuthClientSecret"), "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials", }, timeout=30, ) token_response.raise_for_status() response = session.get( f"https://graph.microsoft.com/v1.0/teams/{TEAM_ID}/channels/{CHANNEL_ID}/messages", params={"$top": 20}, headers={"Authorization": f"Bearer {token_response.json()['access_token']}"}, timeout=30, ) response.raise_for_status() messages = [ { "id": message.get("id"), "createdDateTime": message.get("createdDateTime"), "webUrl": message.get("webUrl"), } for message in response.json().get("value", []) ] return json.dumps(messages)
Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import { ExternalSystems, Function } from "@foundry/functions-api"; import { MicrosoftTeamsSource } from "@foundry/external-systems/sources"; const TENANT_ID = "<tenant_id>"; const CLIENT_ID = "<client_id>"; const TEAM_ID = "<team_id>"; const CHANNEL_ID = "<channel_id>"; export class TeamsGraphFunctions { @ExternalSystems({ sources: [MicrosoftTeamsSource] }) @Function() public async listChannelMessages(): Promise<string> { const tokenResponse = await MicrosoftTeamsSource.fetch( `https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: MicrosoftTeamsSource.getSecret("OAuthClientSecret"), scope: "https://graph.microsoft.com/.default", grant_type: "client_credentials", }).toString(), }, ); if (!tokenResponse.ok) throw new Error(`Microsoft token exchange returned ${tokenResponse.status}`); const token = await tokenResponse.json() as { access_token: string }; const response = await MicrosoftTeamsSource.fetch( `https://graph.microsoft.com/v1.0/teams/${TEAM_ID}/channels/${encodeURIComponent(CHANNEL_ID)}/messages?$top=20`, { headers: { Authorization: `Bearer ${token.access_token}` } }, ); if (!response.ok) throw new Error(`Microsoft Graph returned ${response.status}`); const payload = await response.json() as { value?: Array<{ id?: string; createdDateTime?: string; webUrl?: string }>; }; return JSON.stringify((payload.value ?? []).map((message) => ({ id: message.id, createdDateTime: message.createdDateTime, webUrl: message.webUrl, }))); } }
Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import { getFetch, getSource } from "@palantir/functions-sources"; const SOURCE_RID = "<source_rid>"; const TENANT_ID = "<tenant_id>"; const CLIENT_ID = "<client_id>"; const TEAM_ID = "<team_id>"; const CHANNEL_ID = "<channel_id>"; export const config = { sources: [SOURCE_RID] }; export default async function listChannelMessages(): Promise<string> { const source = await getSource({ rid: SOURCE_RID }); const clientSecret = source.secrets.OAuthClientSecret; if (!clientSecret) throw new Error("Add OAuthClientSecret to the source"); const sourceFetch = await getFetch(source); const tokenResponse = await sourceFetch( `https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: clientSecret, scope: "https://graph.microsoft.com/.default", grant_type: "client_credentials", }).toString(), }, ); if (!tokenResponse.ok) throw new Error(`Microsoft token exchange returned ${tokenResponse.status}`); const token = (await tokenResponse.json()) as { access_token: string }; const response = await sourceFetch( `https://graph.microsoft.com/v1.0/teams/${TEAM_ID}/channels/${encodeURIComponent(CHANNEL_ID)}/messages?$top=20`, { headers: { Authorization: `Bearer ${token.access_token}` } }, ); if (!response.ok) throw new Error(`Microsoft Graph returned ${response.status}`); const payload = (await response.json()) as { value?: Array<{ id?: string; createdDateTime?: string; webUrl?: string }>; }; return JSON.stringify((payload.value ?? []).map((message) => ({ id: message.id, createdDateTime: message.createdDateTime, webUrl: message.webUrl, }))); }

チャネルメッセージを送信する(委任ユーザー)

サインインしたユーザーとしてチャネルメッセージを送信するには、https://graph.microsoft.com 用の REST API ソースにアウトバウンドアプリケーションを設定します。チャネルメッセージの送信に必要な権限を付与し、メッセージを送信するユーザーとしてアウトバウンドアプリケーションを認可します。

以下の例では、Python または TypeScript v2 の関数からメッセージを送信します。REST API ソースを Functions リポジトリにインポートし、ソース、チーム、チャネルの識別子を置き換えます。各例はユーザーのアクセストークンを取得し、リクエストの Authorization ヘッダーに含めます。

選択したチームにアプリケーションからアクセスするには、リソース固有の同意を伴う ChannelMessage.Send.Group と、読み取りの例に示すアプリケーショントークンのパターンを使用します。Teamwork.Migrate.All は過去のメッセージのインポート ↗を目的としており、新しいチャネルメッセージの送信を目的としたものではありません。

この関数を Workshop または自動化されたワークフローで直接使用する前に、関数ベースのアクションを通じてアウトバウンドアプリケーションを認可してください。この関数は、呼び出し元ユーザーの認可を使用します。その認可が期限切れになるか取り消された場合、ユーザーは対話型ワークフローを通じて再度認可する必要があります。

どちらの関数を実行しても、チャネルメンバーが閲覧できるメッセージが投稿されます。入力を確認し、テスト時には非本番のチャネルを使用してください。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import requests from functions.api import function from functions.sources import get_source SOURCE_ALIAS = "<source_alias>" TEAM_ID = "<team_id>" CHANNEL_ID = "<channel_id>" @function(sources=[SOURCE_ALIAS]) def send_channel_message(text: str) -> str: source = get_source(SOURCE_ALIAS) credentials = source.get_session_credentials().get() session = requests.Session() proxy = source.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) response = session.post( f"https://graph.microsoft.com/v1.0/teams/{TEAM_ID}/channels/{CHANNEL_ID}/messages", headers={"Authorization": f"Bearer {credentials.access_token}"}, json={"body": {"content": text}}, timeout=30, ) response.raise_for_status() return response.json()["id"]
Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { getFetch, getSource } from "@palantir/functions-sources"; const SOURCE_RID = "<source_rid>"; const TEAM_ID = "<team_id>"; const CHANNEL_ID = "<channel_id>"; export const config = { sources: [SOURCE_RID] }; export default async function sendChannelMessage(text: string): Promise<string> { const source = await getSource({ rid: SOURCE_RID }); const credentials = await source.sessionCredentials?.get(); if (credentials?.type !== "oauth") throw new Error("Authorize the source first"); const sourceFetch = await getFetch(source); const response = await sourceFetch( `https://graph.microsoft.com/v1.0/teams/${TEAM_ID}/channels/${encodeURIComponent(CHANNEL_ID)}/messages`, { method: "POST", headers: { Authorization: `Bearer ${credentials.accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ body: { content: text } }), }, ); if (!response.ok) throw new Error(`Microsoft Graph returned ${response.status}`); return ((await response.json()) as { id: string }).id; }