Microsoft 365にもWebhookはありまぁす!【Graph API change notifications】

すかんく

すかんく

はじめに

こんにちは。最近オーツラテにハマってるすかんくです。
運用支援チームの2025年Advent Calendar、11日目になります。

Cloudnative IRS Advent Calendar 2025 – Adventar

今回はMicrosoft 365環境で使えるGraph API change notificationsという機能を紹介したいと思います。言ってしまえばWebhookです。本機能は以前から存在していましたが、国内での利用例をあまり見かけないため、紹介することにしました。

今回やりたいこと

  • 特定のグループのメンバーシップが更新されたらLogic Appsへ通知する
    • Logic Appsである必要はないのですが、今回テンプレートとして用意しやすかったのでご容赦ください
  • 細かい仕様等は公式ドキュメントを各自参照ください

手順

Logic Appsをインポート

1. Azureポータルのカスタム デプロイ(https://portal.azure.com/#create/Microsoft.Template)へアクセス

2. [エディターで独自のテンプレートを作成する] をクリック

3. エディタへ以下に記載するテンプレートを張り付けて保存

json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "workflows_graphapisubscription_name": {
            "defaultValue": "graphapisubscription",
            "type": "String"
        },
        "workflows_graphapilifecycleNotification_name": {
            "defaultValue": "graphapilifecycleNotification",
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Logic/workflows",
            "apiVersion": "2017-07-01",
            "name": "[parameters('workflows_graphapilifecycleNotification_name')]",
            "location": "japaneast",
            "properties": {
                "state": "Enabled",
                "definition": {
                    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
                    "contentVersion": "1.0.0.0",
                    "parameters": {
                        "$connections": {
                            "defaultValue": {},
                            "type": "Object"
                        }
                    },
                    "triggers": {
                        "Microsoft_Graph_Lifecycle_Notifications": {
                            "type": "Request",
                            "kind": "Http"
                        }
                    },
                    "actions": {
                        "Response_validationToken-copy": {
                            "runAfter": {},
                            "type": "Response",
                            "kind": "Http",
                            "inputs": {
                                "statusCode": 200,
                                "headers": {
                                    "Content-Type": "text/plain; charset=utf-8"
                                },
                                "body": "@triggerOutputs()?['queries']?['validationToken']"
                            }
                        }
                    },
                    "outputs": {}
                },
                "parameters": {
                    "$connections": {
                        "value": {}
                    }
                }
            }
        },
        {
            "type": "Microsoft.Logic/workflows",
            "apiVersion": "2017-07-01",
            "name": "[parameters('workflows_graphapisubscription_name')]",
            "location": "japaneast",
            "properties": {
                "state": "Enabled",
                "definition": {
                    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
                    "contentVersion": "1.0.0.0",
                    "parameters": {
                        "$connections": {
                            "defaultValue": {},
                            "type": "Object"
                        }
                    },
                    "triggers": {
                        "Microsoft_Graph_Change_Notifications": {
                            "type": "Request",
                            "kind": "Http",
                            "inputs": {
                                "method": "POST"
                            }
                        }
                    },
                    "actions": {
                        "Response_validationToken": {
                            "runAfter": {},
                            "type": "Response",
                            "kind": "Http",
                            "inputs": {
                                "statusCode": 200,
                                "headers": {
                                    "Content-Type": "text/plain; charset=utf-8"
                                },
                                "body": "@triggerOutputs()?['queries']?['validationToken']"
                            }
                        }
                    },
                    "outputs": {}
                },
                "parameters": {
                    "$connections": {
                        "value": {}
                    }
                }
            }
        }
    ]
}

4. Logic Appsの作成先のリソースグループやリージョン、ワークフロー名を決定して作成を実行

5. 作成された各リソースのトリガーから HTTP URL を控えておく

Graph API change notificationsリソースの作成

1. Entra管理センター(https://entra.microsoft.com/)で、変更を通知させたいグループのオブジェクトIDを取得

2. GraphモジュールがインストールされたPowerShellで以下を実行
(※Graph APIが実行できれば実行手段は問いません)

  • サンプルスクリプトを利用する場合、$params の各値を適宜置き換えてください
powershell
Connect-MgGraph

$params = @{
   "changeType" = "updated"
   "notificationUrl" = "<Event Notification URL>"
   "resource" = "groups/<通知したいグループのオブジェクトID>/members"
   "expirationDateTime" = "<ISO 8601形式のDateTime(例:2025-12-15T10:00:00Z)>"
   "clientState" = "<任意の文字列>"
   "lifecycleNotificationUrl" = "<Expiration Notification URL>"
}

Invoke-MgGraphRequest -Method POST -Uri "/v1.0/subscriptions" -Body $params

3. 作成に成功したことを確認する

動作テスト

1. 動作確認を行いたいグループにメンバーを追加または削除

2. イベント通知先のLogic Appsから実行履歴を確認し、意図した通りのイベントが通知されていることを確認

組み合わせ次第で可能性が広がる

Webhookの送信先は自由なので、理論上はMicrosoft 365に限らず、API等で操作可能なすべてのサービスやシチュエーションで利用できます。

真っ先に思いつくのは、SCIMが提供されていないSaaSに対するユーザー作成や削除の自動化でしょうか。

  • SCIM未対応の各SaaSベンダーがこの機能を前提としたAWS LambdaやAzure Functions用のテンプレートを準備/公開してくれたら、めっちゃ嬉しいですけどね

個人的な用途として、Entraではネストされたグループに対応していない機能が一部存在することから、各グループの変更をキャッチして単一のグループ内でフラット化する機能を実装したりしています。

  • 動的グループにメンバーシップクエリがありますが、これは動的グループ同士の参照をサポートしていないので使えない

また、Entra P2ライセンスが必要になりますが、グループベースのPIMと組み合わせることで、時限的なコントロールも可能になりそうです。

  • PIMの有効期限に沿って、M365外サービスに対して時限的な特権付与を行う

運用上留意したいこと

本番環境で運用する際に留意すべき点について触れておきます。詳細は公式ドキュメントを参照してください。

Response

  • 3秒以内に200を返す
  • 10秒以上かかる場合は202を返す
  • レスポンスが無い(配信に失敗した)通知は再試行されるが、再送信には最大4時間かかる

Throttling

  • 応答速度が低下したり応答しなくなったりしたエンドポイントに対しては、Graph APIが通知送信を自動で制限する
  • 通知を受信するリソース側(今回はLogic Apps)でパフォーマンスを確保する必要がある

Subscription lifetime

  • lifecycleNotificationUrl(Expiration Notification URL)にはサブスクライブしているイベントの有効期限通知が届くので、対象リソースに対して自動延長を行う処理を実装してください
  • Graph APIで対象リソースにPATCH処理を実行するだけで問題ありません(以下サンプル)
json
PATCH https://graph.microsoft.com/v1.0/subscriptions/{id}
Content-Type: application/json

{
  "expirationDateTime": "2025-12-25T10:00:00Z"
}

おわりに

いかがでしたでしょうか。Microsoft 365のGraph API change notificationsを使えば、様々な操作をトリガーとした自動化が実現できます。

工夫次第で運用負荷の削減に大きく貢献できる機能だと思いますので、ぜひ試してみてください。ではまた!

参考

https://learn.microsoft.com/en-us/graph/change-notifications-overview

https://learn.microsoft.com/en-us/graph/api/resources/change-notifications-api-overview

https://learn.microsoft.com/en-us/graph/change-notifications-delivery-webhooks

https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/concept-pim-for-groups

この記事をシェア