【GraphAPI】Teams エクスポート API 

# はじめに

Teams エクスポート Graph API を利用すると、Teams のメッセージを簡単な処理でエクスポートすることができます。Teams エクスポート API について検証した内容をまとめてみました。

# Teams エクスポート API 

Teams のメッセージを取得する Graph API として、メッセージ一覧取得 API があります。Teams エクスポート API とメッセージ一覧取得 API には以下のような違いがあります。

(参考：Microsoft Teamsエクスポート API を使用してコンテンツをエクスポートする)
https://learn.microsoft.com/ja-jp/microsoftteams/export-teams-content

(参考：シナリオに適した API の選択)
https://learn.microsoft.com/ja-jp/graph/teams-messaging-overview#choosing-the-right-api-for-your-scenario

## メッセージ一覧取得 API との違い

| 名前 | 取得対象 | アクセス権限 | $top で一度に取得できる件数 | 課金 |
|:-:|:-:|:-:|:-:|:-:|
| エクスポート API | 特定の **チーム** またはグループチャット内のメッセージ | アプリケーション許可権限のみ | 1,000~(※) | あり |
| メッセージ一覧取得 API |特定の **チャネル** またはグループチャット内のメッセージ | ユーザー委任権限、またはアプリケーション許可権限 | 50 | なし |

::: note warn
エクスポート API を利用するにはライセンスや従量課金のため Azure サブスクリプションが必要になります。ライセンス要件については[後述](#ライセンス要件)しています。
:::

::: note
エクスポート API ではメッセージの返信も含めて取得することができます。
メッセージ一覧取得 API では、メッセージの返信を取得することはできず、返信を取得する API によって別途取得する必要があります。
:::

※ エクスポート API では $top = 250 を指定してページングによってメッセージを取得すると効率よく処理することができます (詳しくは[こちら](#取得件数と処理時間))。

(参考：応答を一覧表示する)
https://learn.microsoft.com/ja-jp/graph/api/chatmessage-list-replies?view=graph-rest-1.0&tabs=http

### URI の比較

#### チーム内のメッセージ

* エクスポート API
  /teams/<ins>**{team-id}**</ins>/channels/getAllMessages
* メッセージ一覧取得 API
   /teams/{team-id}/channels/<ins>**{channel-id}**</ins>/messages

::: note
エクスポート API はチーム ID を指定するだけで、チーム内のすべてのチャネルのメッセージを取得することができます。
メッセージ一覧取得 API はチャネル ID の指定が必要です。
:::

(参考：channel: getAllMessages)
https://learn.microsoft.com/ja-jp/graph/api/channel-getallmessages?view=graph-rest-1.0&tabs=http

(参考：チャネル メッセージを一覧表示する)
https://learn.microsoft.com/ja-jp/graph/api/channel-list-messages?view=graph-rest-1.0&tabs=http

#### グループチャットのメッセージ

* エクスポート API
  /users/<ins>**{user-id}**</ins>/chats/getAllMessages
* メッセージ一覧取得 API
  /chats/<ins>**{chat-id}**</ins>/messages

::: note
エクスポート API はユーザー ID を指定するだけで、ユーザーが参加しているすべてのグループチャットのメッセージを取得することができます。
メッセージ一覧取得 API はチャット ID の指定が必要です。
:::

(参考：チャット: getAllMessages)
https://learn.microsoft.com/ja-jp/graph/api/chats-getallmessages?view=graph-rest-1.0&tabs=http

(参考：チャット内のメッセージを一覧表示する)
https://learn.microsoft.com/ja-jp/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http

## ライセンス要件

エクスポート API は用途によってライセンス要件が異なります。

| 用途 | 支払いモデル | DLP ライセンス | 課金 | 備考 |
|:-:|:-:|:-:|:-:|:-:|
| セキュリティやコンプライアンス | A | 必要 | あり | アプリごとに毎月ライセンス数 x 800 メッセージまで無料 |
| バックアップ | B | 不要 | あり | 無料枠なし |
| 評価 | - | 不要 | なし  | アプリごとに毎月 500 メッセージまで|

エクスポート API を実行する際に、URI の **"model"** パラメータによって支払いモデルを指定します。指定しない場合は評価モードになります。

* 支払いモデル A で実行する
  /teams/{team-id}/channels/getAllMessages<ins>?model=A</ins>
* 支払いモデル B で実行する
  /teams/{team-id}/channels/getAllMessages<ins>?model=B</ins>
* 評価モードで実行する
  /teams/{team-id}/channels/getAllMessages

::: note warn
従量課金でエクスポート API を実行するためには Azure サブスクリプションを使用して、従量課金を有効化する必要があります。この記事の[最後](#支払いモデル-a--b-の従量課金を有効にする場合)で手順を紹介しています。
:::

### 支払いモデル A の場合に必要なライセンス

支払いモデル A の場合には **"Microsoft Communication DLP"** が含まれるライセンスが必要になります。Microsoft 365 E5 などのライセンスに含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/ee6bfde3-368a-452d-998c-64508e760c97.png)

::: note
"Microsoft Communication DLP" は Microsoft 365 E5 Developer ライセンスにも含まれています。
:::

(参考：Microsoft Teams API の支払いモデルとライセンス要件)
https://learn.microsoft.com/ja-jp/graph/teams-licenses

(参考：Teams データ損失防止 (DLP) および Teams エクスポート向け Microsoft Purview データ損失防止 Graph API)
https://learn.microsoft.com/ja-jp/office365/servicedescriptions/microsoft-365-service-descriptions/microsoft-365-tenantlevel-services-licensing-guidance/microsoft-purview-service-description#microsoft-purview-data-loss-prevention-graph-apis-for-teams-data-loss-prevention-dlp-and-for-teams-export

# 利用方法

## エクスポート API の検証

Graph PowerShell でテスト用のチームに 1,000 件のメッセージを投稿して、エクスポート API によって取得してみます。

### 事前準備

以下のように特定のチャネルに 1,000 件メッセージを投稿します。チャネルへのメッセージの投稿は **"Send chatMessage in channel"** API を<ins>ユーザー委任権限</ins>で実行します。

::: note warn
Graph API によるアプリケーション許可権限でのメッセージの送信は、他のサードパーティ製品からメッセージを移行する場合のみ行うことができます。
:::

(参考：チャネルで chatMessage を送信する)
https://learn.microsoft.com/ja-jp/graph/api/channel-post-messages?view=graph-rest-1.0&tabs=http
>アプリケーションのアクセス許可は 、移行でのみサポートされます。 

* アクセス許可を指定してサインイン
  ```powershell
  # ダイアログが表示されたらメッセージを投稿するチームに所属するユーザーでサインインしてください
  Connect-MgGraph -Scopes "Team.ReadBasic.All", "ChannelSettings.Read.All"
  ```
* テスト用チームの ID を取得
  ```powershell
  # チームの名前を指定して実行してください
  Get-MgTeam -Filter "DisplayName eq 'テストチーム01'" | select Id, DisplayName
  ```
  ```
  Id                                   DisplayName
  --                                   -----------
  d4923767-ea0d-4168-9422-108464d47b6d テストチーム01
  ```
* テスト用チャネルの ID を取得
  ```powershell
  # テスト用チームの ID を指定して実行してください
  Get-MgTeamChannel -TeamId "d4923767-ea0d-4168-9422-108464d47b6d" | select Id, DisplayName
  ```
  ```
  Id                                                           DisplayName
  --                                                           -----------
  19:SSOzOEGtTCpZ9TgVSlyqlAz3GbGHAkoySC7BfOsk1xU1@thread.tacv2 テストチャネル01
  ```
* テスト用チャネルに 1,000 件メッセージを送信
   ```powershell
   Connect-MgGraph -Scopes "ChannelMessage.Send"
   # テスト用チームの ID を指定してください
   $teamId = "d4923767-ea0d-4168-9422-108464d47b6d"
   # テスト用チャネルの ID を指定してください
   $channelId = "19:SSOzOEGtTCpZ9TgVSlyqlAz3GbGHAkoySC7BfOsk1xU1@thread.tacv2"

   # テスト用チャネルにテストメッセージを 1,000 件送信します
   for ($i = 1; $i -le 1000; $i++) {
      $params = @{
	      body = @{
             # 送信するメッセージの内容を指定します
		     content = "テスト メッセージ：$i"
		  }
	   }
      # Send chatMessage in channel API を使用してチャネルにメッセージを送信します
      New-MgTeamChannelMessage -TeamId $TeamId -ChannelId $ChannelId -BodyParameter $params
      # Invoke-MgGraphRequest コマンドの場合は以下のように実行します
      # Invoke-MgGraphRequest -Method POST -Uri "/v1.0/teams/$TeamId/channels/$ChannelId/messages" -Body $params -OutputType PSObject

      # API のスロットリングによる制限のため 1 秒ごとに 1 件のメッセージを送信します
      Sleep 1 
   }
   ```

※ 実行結果の例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/84cdfd20-a8cf-4820-8391-42d9faa3aff3.png)

#### スロットリングについて

::: note info
Graph API で特定のチャネルに対してメッセージを送信する場合、1 秒間に 1 回 (1 RPS) メッセージを送信することができます。
:::

::: note warn
上限を超えて実行すると、スロットリングによるエラーが返されます。
:::

<details><summary>スロットリングについての詳細はこちら</summary>

Graph API では、1 秒間に実行できるリクエストの回数 (Request Per Seconds: **RPS**) に上限が定められています。上限を超えて Graph API を実行した場合、以下のようなエラーが返されます。

※ スロットリングによるエラーの例


```
{
    "error": {
        "code": "TooManyRequests",
        "message": "{\"errorCode\":429,\"message\":\"API calls quota exceeded! 10Per10Secs\",\"standardizedError\":{\"errorCode\":429,\"errorSubCode\":1,\"errorDescription\":\"API calls quota exceeded! 10Per10Secs\"}}",
        "innerError": {
            "date": "2025-06-12T04:04:02",
            "request-id": "8c38680d-9912-4a5d-822d-ff6a3bbbdfa7",
            "client-request-id": "17eb0b4a-922e-4d67-9362-58e67726a25b"
        }
    }
}
```

::: note warn
RPS の上限は Graph API ごとに異なります。
:::

(参考：Microsoft Teams サービスの制限)
https://learn.microsoft.com/ja-jp/graph/throttling-limits
> 特定のチャットまたはチャネルで POST メッセージを実行するときに、ユーザーごとに 1 秒あたり最大 1 つの要求を発行できます 

</details>

### エクスポート API の実行

あらかじめ **"ChannelMessage.Read.All"** アプリケーション許可権限を追加して Connect-MgGraph コマンドを実行しておきます。

※ 参考

https://qiita.com/atsmin77/items/6bcc9fb88b305e124399

#### メッセージの取得

* 評価モードでメッセージを取得
  ```powershell
  # テスト用チームの ID を指定してください 
  $TeamId = "d4923767-ea0d-4168-9422-108464d47b6d"
  # チャネルメッセージ エクスポート API の URI を指定します
  # "model" パラメータは指定せずに評価モードで実行します
  $Uri = "/v1.0/teams/$TeamId/channels/getAllMessages" 
  $response = Invoke-MgGraphRequest -Method GET -Uri $Uri -OutputType PSObject
  # 取得したメッセージを表示します
  $response.value | select body
  # 取得したメッセージの件数を表示します
  $response.value.length
  ```
  * 10 件のメッセージが取得されました
  ```
  body
  ----
  @{contentType=text; content=テスト メッセージ：1}
  @{contentType=text; content=テスト メッセージ：2}
  @{contentType=text; content=テスト メッセージ：3}
  @{contentType=text; content=テスト メッセージ：5}
  @{contentType=text; content=テスト メッセージ：6}
  @{contentType=text; content=テスト メッセージ：4}
  @{contentType=text; content=テスト メッセージ：7}
  @{contentType=text; content=テスト メッセージ：8}
  @{contentType=text; content=テスト メッセージ：9}
  @{contentType=text; content=テスト メッセージ：10}
  10
  ```
  
* 取得されたメッセージの詳細情報を確認
  ```powershell
  $response.value[0]
  ```
  ```
  id                   : 1750209634494
  replyToId            :
  etag                 : 1750209634494
  messageType          : message
  createdDateTime      : 2025/06/18 1:20:34
  lastModifiedDateTime : 2025/06/18 1:20:34
  lastEditedDateTime   :
  deletedDateTime      :
  subject              :
  summary              :
  chatId               :
  importance           : normal
  locale               : en-us
  webUrl               : https://teams.microsoft.com/l/message/19%3AQgQEUFxqWRzg7F1kNlPx86DBGHifIs__vsr8kcN87JE1%40thread
                       .tacv2/1750209634494?groupId=05683739-7108-43f8-ab99-3754a7fbd82d&tenantId=8d6d6649-4030-4e9f-a6
                       cb-7e1443e7af6b&createdTime=1750209634494&parentMessageId=1750209634494
  policyViolation      :
  eventDetail          :
  from                 : @{application=; device=; user=}
  body                 : @{contentType=text; content=テスト メッセージ：1}
  channelIdentity      : @{teamId=05683739-7108-43f8-ab99-3754a7fbd82d; channelId=19:QgQEUFxqWRzg7F1kNlPx86DBGHifIs__vsr8
                       kcN87JE1@thread.tacv2}
  attachments          : {}
  mentions             : {}
  reactions            : {}
  ```

::: note
取得されたメッセージには以下のような情報が含まれています。
* 投稿日時 (createdDateTime)
* 投稿者 (from)
* 件名 (subject)
* 本文 (body)
* チーム / チャネルの ID (channelIdentity)
* 添付ファイル (attachments)
* メンション (mentions)
* リアクション (reactions)
:::

* URI に **"$top=1000"** を追加してメッセージを取得
  ```powershell
  # テスト用チームの ID を指定してください
  $TeamId = "d4923767-ea0d-4168-9422-108464d47b6d"
  # チャネルメッセージ エクスポート API の URI で "$top" パラメータを指定します
  $Uri = "/v1.0/teams/$TeamId/channels/getAllMessages?`$top=1000" 
  $response = Invoke-MgGraphRequest -Method GET -Uri $Uri -OutputType PSObject
  # 取得したメッセージを表示します
  $response.value | select body
  # 取得したメッセージの件数を表示します
  $response.value.length
  ```
  * 1,000 件のメッセージがすべて取得されました
  ```
  body
  ----
  @{contentType=text; content=テスト メッセージ：1}
  @{contentType=text; content=テスト メッセージ：2}
  @{contentType=text; content=テスト メッセージ：3}
  ...
  @{contentType=text; content=テスト メッセージ：1000}
  1000
  ```
#### CSV ファイルへ出力

  * 取得したメッセージを CSV ファイルへ出力するコマンド例です
  ```powershell
  # CSV ファイルの出力先を指定してください
  $FilePath = "C:\Temp\messages.csv"
  # 取得したメッセージを Export-Csv コマンドで CSV ファイルへ出力します
  $response.value | select id,replyToId,createdDateTime,lastEditedDateTime,deletedDateTime,@{Name="fromUser"; Expression={$_.from.user.displayName}},messageType,subject,@{Name="bodyContent"; Expression={$_.body.content}},webUrl | Export-Csv -Path $FilePath -NoTypeInformation -Encoding UTF8
  ```
  ※ 出力結果のサンプル
  ```
  "id","replyToId","createdDateTime","lastEditedDateTime","deletedDateTime","fromUser","messageType","subject","bodyContent","webUrl"
  "1749101186629",,"2025/06/05 5:26:26",,,"テスト ユーザー01","message",,"テスト メッセージ：1","https://teams.microsoft.com/l/message/19%3ASSOzOEGtTCpZ9TgVSlyqlAz3GbGHAkoySC7BfOsk1xU1%40thread.tacv2/1749101186629?groupId=d4923767-ea0d-4168-9422-108464d47b6d&tenantId=ac2852a6-88d6-4ddc-9a63-93391e191144&createdTime=1749101186629&parentMessageId=1749101186629"
  "1749101187126",,"2025/06/05 5:26:27",,,"テスト ユーザー01","message",,"テスト メッセージ：2","https://teams.microsoft.com/l/message/19%3ASSOzOEGtTCpZ9TgVSlyqlAz3GbGHAkoySC7BfOsk1xU1%40thread.tacv2/1749101187126?groupId=d4923767-ea0d-4168-9422-108464d47b6d&tenantId=ac2852a6-88d6-4ddc-9a63-93391e191144&createdTime=1749101187126&parentMessageId=1749101187126"
  "1749101187452",,"2025/06/05 5:26:27",,,"テスト ユーザー01","message",,"テスト メッセージ：3","https://teams.microsoft.com/l/message/19%3ASSOzOEGtTCpZ9TgVSlyqlAz3GbGHAkoySC7BfOsk1xU1%40thread.tacv2/1749101187452?groupId=d4923767-ea0d-4168-9422-108464d47b6d&tenantId=ac2852a6-88d6-4ddc-9a63-93391e191144&createdTime=1749101187452&parentMessageId=1749101187452"
  ...
  ```

#### 取得件数と処理時間

一度のリクエストで取得するメッセージの件数は **"$top"** パラメータで指定することができます。

取得件数が 250 件以下の場合には、1 秒ほどでメッセージを取得することができました。250 件を超えるとメッセージを取得するのに数秒かかりました。

| 取得件数 | 処理時間 |
|:-:|:-:|
| 10 件 | 0.4 秒 |
| 100 件 | 1 秒  |
| 250 件 | 1.6 秒 |
| 500 件 | 3.1 秒 |
| 1000 件 | 6.7 秒 |

::: note
250 件を超えるメッセージを取得する際には、公開情報に記載されているとおり $top=250 を指定して[ページング](https://qiita.com/atsmin77/items/a1d53031b120da5d9697#%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)によって繰り返し取得すると、効率よく処理できるようです。
:::

(参考：Teams エクスポート API でサポートされる内容)
https://learn.microsoft.com/ja-jp/microsoftteams/export-teams-content#what-is-supported-by-the-teams-export-apis
>Teams Meesage の上位制限: Teams メッセージ API の TOP フィルター制限は、パフォーマンスが制限される上限として 250 に設定することをお勧めします。

※ Invoke-MgGraphRequest コマンドでページングによってメッセージを取得するスクリプトの例です。

```powershell
# テスト用チームの ID を指定してください
$TeamId = "d4923767-ea0d-4168-9422-108464d47b6d"
# チャネルメッセージ エクスポート API の URI で "$top" パラメータを指定します
$Uri = "/v1.0/teams/$TeamId/channels/getAllMessages?`$top=250" 

# Invoke-MgGraphRequest でページング処理を行うコマンド(関数) です
Function Invoke-MgGraphRequestWithPaging($Method, $Uri, $OutputType) {
    $responses = @()
    $response = Invoke-MgGraphRequest -Method $Method -Uri $Uri -OutputType $OutputType
    $responses += $response
    # ページング処理を行います
    # '@odata.nextLink' が返されなくなるまで情報を取得します
    while($response.'@odata.nextLink' -ne $null){
        $nextLink = $response.'@odata.nextLink'
        $response = Invoke-MgGraphRequest -Method "GET" -Uri $nextLink -OutputType $OutputType
		$responses += $response
    }
	# 取得したレスポンスをすべて返します
    return $responses
}
# ページングによって 250 件ずつメッセージを取得します
$responses = Invoke-MgGraphRequestWithPaging -Method GET -Uri $Uri -OutputType PSObject
# 取得したメッセージを表示します
$responses | % {$_.value | select body}
# 取得したメッセージの件数を表示します
$responses | % {$_.value.length}
```

## 補足

### 無料枠を使い切った場合

評価版や支払いモデル A の無料枠をすべて消費した場合には、以下のような応答が返されメッセージを取得することができなくなります。

::: note
無料枠はアプリケーションごとため、別の Entra ID アプリケーションを使用して API を実行すれば再度無料枠を利用できます。
:::

```
HTTP/1.1 402 Payment Required

{
    "error": {
        "code": "PaymentRequired",
        "message": "Evaluation capacity for the month has exceeded. To continue beyond the evaluation limits complete billing onboarding. Visit https://docs.microsoft.com/en-us/graph/teams-licenses for API specific requirements.",
        "innerError": {
            "date": "2025-06-06T01:52:24",
            "request-id": "29a5cf79-69eb-4134-a472-575fdb529daa",
            "client-request-id": "53ca74b4-920c-4117-89d7-c12d488d2168"
        }
    }
}
```

### 支払いモデル A / B の従量課金を有効にする場合

従量課金を有効化は Azure ポータルから行います。

1. 右上のボタンから Azure Cloud Shell を開き、**[Bash]** をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/4e8a6b3e-b868-49da-ad8e-fdae9ff9e30c.png)
1. 従量課金で使用する Azure サブスクリプションを選択し、**[適用]** をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/9a72270b-91af-4c5d-8abc-e294a0735519.png)
1. Aure Cloud Shell へ以下のコマンドを入力して実行します
   ```bash
   resource_group=<リソースグループ名>
   resource_name=<任意のリソース名>
   subscription_id=<Azure サブスクリプション ID>
   application_clientid=<Entra アプリケーションの ClientID>
   az graph-services account create --resource-group $resource_group --resource-name $resource_name --subscription $subscription_id --location global --app-id $application_clientid
   ```
   ※ 以下はリソースグループも同時に作成する場合の実行例です
   ```bash
   resource_group=testRG
   resource_name=testGraphAppBilling
   subscription_id=d6cc086c-b4fd-4bc1-b3e9-f14b601a792b
   application_clientid=3649c339-9b40-4e08-b36c-804b509890a8
   # リソースグループも新規に作成する
   az group create -l japaneast -n $resource_group
   az graph-services account create --resource-group $resource_group --resource-name $resource_name --subscription $subscription_id --location global --app-id $application_clientid
   ```
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/d027c768-474c-4f61-8a0c-3fe3662f9a99.png)
1. **"provisioningState" : "Succeeded"** と表示されたら完了です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/51b1e662-007a-4cfc-8d06-9481aa5c4a58.png)

#### 従量課金を無効化する場合

従量課金を無効化するには以下のコマンドを実行します。

```bash
az graph-services account delete --resource-group $resource_group --resource-name　$resource_name --subscription $subscription_id 
```

(参考：Microsoft Graph で従量制課金 API とサービスを有効にする)
https://learn.microsoft.com/ja-jp/graph/metered-api-setup

(参考：az graph-services account)
https://learn.microsoft.com/ja-jp/cli/azure/graph-services/account?view=azure-cli-latest#az-graph-services-account-delete