ロジックアプリからマネージド ID を用いて Office 365 ユーザーからメール通知を行う

# 1. はじめに
ロジックアプリからメール通知を行う構成では、標準コネクタで提供されている Office 365 コネクタを用いるケースが多いと思います。Office 365 コネクタを用いた実装はとてもお手軽なのですが、コネクタの認証画面から送信元ユーザー経由で認証を行う必要があり（※初回のみ）、カスタマイズできる項目に限界があるなどの制限があります。
今回、**Microsoft Graph API** を用いることで、ロジックアプリで設定したマネージド ID 経由でメール送信する手法をご紹介します。

詳細は Microsoft Graph API の ``user: sendMail`` の情報を参考としています。

https://learn.microsoft.com/ja-jp/graph/api/user-sendmail?view=graph-rest-1.0&tabs=python

# 2. [参考] ロジックアプリからメール送信を行うパターン
ロジックアプリからメール送信する方法は幾つかあります。
要件に合った方法を検討して下さい。

| 方式 | Office 365 | Azure Communication Services | Microsoft Graph API |
| ---- | ---- | ---- | ---- |
| ロジックアプリ<BR>コネクタ | Office 365 コネクタ | Azure Communication Services コネクタ | HTTP |
| O365 ユーザーの作成の必要性 | 必要 | 不要 | 必要 |
| 認証方式 | ユーザー認証(コネクタ内) | 接続文字列 | マネージドID | 

https://qiita.com/hisnakad/items/cfaeae8c7b92c7d26365

https://qiita.com/hisnakad/items/814ae1a87a6964012d05

# 3. 設定方法
Microsoft Graph API を用いて、マネージド ID で通知する流れを以下ご紹介します。
## 3.1 ロジックアプリ側の作成
- ロジックアプリを作成し、マネージド ID を有効化します
- 複数のロジックアプリで同じような通知方式を組むのであれば、ユーザー割り当てマネージド ID を一つ作成し、ロジックアプリに割り当てる方法を検討して下さい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/4d361979-2d00-520e-8ac2-e24326bf31bb.png)

## 3.2 Entra ID でマネージド ID に対して権限を与える
マネージド ID に Microsoft Graph 権限の ``Mail.Send`` を与えるのですが、2024.3 現在 Azure ポータルから設定することが不可となっており PowerShell / Azure CLI で設定する必要があります。
マネージド ID に対して Graph API の権限を付与する方法は公式 Docs に幾つかサンプルが掲載されています。以下サイトの例からマネージド ID に対して ``Mail.Send`` の権限を付与するスクリプトを実行します。

https://learn.microsoft.com/ja-jp/Azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-powershell


```powershell
# テナント ID と先ほどメモしたオブジェクト ID を設定
$TenantID="<Entra テナント ID>"
$spID="<ここにマネージド ID の object ID>"

# MS Graph の許可を指定、1 つのみ指定
# 複数必要の場合は「マネージド ID にアクセス許可を設定」のコマンドを繰り返し実施
$PermissionName = "Mail.Send"

# 事前に MS Graph PowerShell にログイン
Connect-MgGraph -TenantId $TenantID -Scopes Application.Read.All,AppRoleAssignment.ReadWrite.All

# Microsoft Graph のサービスプリンシパルを取得
$GraphServicePrincipal = Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'" | Select-Object -first 1

# マネージド ID にアクセス許可を設定
$AppRole = $GraphServicePrincipal.AppRoles | Where-Object {$_.Value -eq $PermissionName -and $_.AllowedMemberTypes -contains "Application"}

$params = @{
    PrincipalId = $spID
    ResourceId = $GraphServicePrincipal.Id
    AppRoleId = $AppRole.Id
}

New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $spID -BodyParameter $params
```
Powershell を実行して無事に成功すると、以下のようなメッセージが出力されます。

```
PS C:\> .\deploy.ps1
Welcome to Microsoft Graph!
Connected via delegated access using 14d82eec-204b-4c2f-b7e8-296a70dab67e
Readme: https://aka.ms/graph/sdk/powershell
SDK Docs: https://aka.ms/graph/sdk/powershell/docs
API Docs: https://aka.ms/graph/docs
NOTE: You can use the -NoWelcome parameter to suppress this message.
DeletedDateTime Id AppRoleId CreatedDateTime PrincipalDisplayName 
xxxxxxxxxxxxxxx xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 2024/03/13 6:54:58 <マネージドID の名前> 
PS C:\>
```
実際に Entra ID でマネージド ID に権限が付与されたかどうか確認します。
Entra ID の「すべてのアプリケーション」から「アプリケーションの種別 = マネージドID」でフィルタすると探しやすいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/771fc705-8523-ebaa-e3a0-16049b635f64.png)

## 3.3 ロジックアプリで HTTP コネクタを用いて RESTAPI を叩く
マネージド ID に権限を持たせることが出来たので、ロジックアプリ側で HTTP コネクタを用いて RESTAPI を叩くように設定します。
今回、テスト用に以下のようなロジックアプリを作成しています。
- **Reccurence** で定期実行
- **MailBody** で HTML メール本体
- **HTTP** で Microsoft Graph API 経由でメール送信

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/0a1722f6-e165-4e7d-1a61-bebcf4054443.png)

HTTP コネクタの設定例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/db3c736c-0a17-3ff4-3149-a47489963bbe.png)
パラメータとして以下を設定しています。

| 項目 | 内容 |
| ---- | ---- |
| 方法 | ``POST`` |
| URI | ``https://graph.microsoft.com/v1.0/(送信元メールアドレス)/sendMail``|
| ヘッダー | ``Content-Type : application/json`` |
| 本文 | [Graph API のメッセージフォーマット](https://learn.microsoft.com/ja-jp/graph/api/resources/message?view=graph-rest-1.0) <BR> (JSON) 形式 |
| 認証の種類 | マネージド ID |
| マネージド ID | システム割り当てマネージド ID |
| 対象ユーザー | ``https://graph.microsoft.com/``|

設定を行い、ロジックアプリを起動してメール通知が行われることを確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653790/8c1f617e-be8b-1805-b2b7-16950ac558e4.png)

# 4. カスタマイズ
本文に入る Graph API メッセージフォーマットですが、これだけのパラメータが適用可能です。添付ファイルなどを指定することも出来ます。

```json
{
  "bccRecipients": [{"@odata.type": "microsoft.graph.recipient"}],
  "body": {"@odata.type": "microsoft.graph.itemBody"},
  "bodyPreview": "string",
  "categories": ["string"],
  "ccRecipients": [{"@odata.type": "microsoft.graph.recipient"}],
  "changeKey": "string",
  "conversationId": "string",
  "conversationIndex": "String (binary)",
  "createdDateTime": "String (timestamp)",
  "flag": {"@odata.type": "microsoft.graph.followupFlag"},
  "from": {"@odata.type": "microsoft.graph.recipient"},
  "hasAttachments": true,
  "id": "string (identifier)",
  "importance": "String",
  "inferenceClassification": "String",
  "internetMessageHeaders": [{"@odata.type": "microsoft.graph.internetMessageHeader"}],
  "internetMessageId": "String",
  "isDeliveryReceiptRequested": true,
  "isDraft": true,
  "isRead": true,
  "isReadReceiptRequested": true,
  "lastModifiedDateTime": "String (timestamp)",
  "parentFolderId": "string",
  "receivedDateTime": "String (timestamp)",
  "replyTo": [{"@odata.type": "microsoft.graph.recipient"}],
  "sender": {"@odata.type": "microsoft.graph.recipient"},
  "sentDateTime": "String (timestamp)",
  "subject": "string",
  "toRecipients": [{"@odata.type": "microsoft.graph.recipient"}],
  "uniqueBody": {"@odata.type": "microsoft.graph.itemBody"},
  "webLink": "string",

  "attachments": [{"@odata.type": "microsoft.graph.attachment"}],
  "extensions": [{"@odata.type": "microsoft.graph.extension"}],
  "multiValueExtendedProperties": [{"@odata.type": "microsoft.graph.multiValueLegacyExtendedProperty"}],
  "singleValueExtendedProperties": [{"@odata.type": "microsoft.graph.singleValueLegacyExtendedProperty"}]
}
```
# 5. 制限事項など
色々と制限事項が重なるので、本番運用検討されている方は以下 Docs を参照下さい。
- 10000 リクエスト / 10 分 かつ / ユーザー / App ID

https://learn.microsoft.com/ja-jp/archive/blogs/exchangedev/throttling-coming-to-outlook-api-and-microsoft-graph

https://stackoverflow.com/questions/51167013/constraints-on-sending-mail-using-graph-api#:~:text=When%20sending%20mail%20using%20Graph%20API%20Per%20User%2C,maximum%20limit%20of%2010%2C000%20requests%20in%2010%20minutes

# 参考URL

https://zenn.dev/microsoft/articles/ae0bf38eebfa48

https://learn.microsoft.com/ja-jp/graph/api/resources/mail-api-overview?view=graph-rest-1.0

https://jpazureid.github.io/blog/azure-active-directory/microsoft-graph-api-signin-activity-reports-v2/