マネージド ID で Graph API を使おう！

# Graph API を使おう！
私たちネクストリードでは、お客様の Microsoft 365 や Azure に関わる業務の自動化をお手伝いすることがよくあります。

自動化といえば PowerShell スクリプト！というイメージがありますが、Microsoft Graph API という API を使うことで PowerShell で出来ること以上のことが出来たりします。

:::note
Graph API（に限らず OAuth 2.0）のアクセス許可には「アプリケーション」と「ユーザー委任」の 2 種類がありますが、ここでは自動化を実現するという文脈でよく使われる「アプリケーション」のこととして話を進めます
:::

また、PowerShell を実行するにはその実行環境 (Windows のコンピューティング環境) を準備する必要がありますが、API を使用する場合は HTTP をリクエストできれば使えるので、環境を選ばないというメリットもあります。

そんな便利な Graph API ですが、Entra ID にアプリ登録を行ってシークレットを発行して・・のような使い方をしていないでしょうか？

もちろんそのような方法でも問題ないのですが、シークレットが漏えいしないように管理しなければならず、セキュリティ上好ましいとは言えないですよね。

# マネージド ID を使おう！
Azure には「マネージド ID (Managed Identity)」という機能が存在します。

:::note
マネージド ID には「システム割り当て済み」と「ユーザー割り当て済み」の 2 種類があるのですが、ここでは私たちがよく使う「システム割り当て済み」のこととして話を進めます
:::

「マネージド」というのは「この ID はマイクロソフトによって管理されているよ」という意味で、パスワードやシークレット、証明書のような、認証するために通常必要となる要素をユーザー側で管理する必要がない、という多大なメリットがあります。

ここでいう「システム」とは「Azure リソース」のことだと考えれば分かりやすいですが、例えばロジックアプリを作成したら、そのロジックアプリ（システム）自体を表すオブジェクトが Entra ID 上に作成され、それがマネージド ID の実体になります。

Azure 側のロジックアプリでマネージド ID をオンにすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/5dceb16b-2e46-b572-625c-06dac4a9e601.png)

Entra ID にマネージド ID のオブジェクトが自動作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/38d2f628-7f85-7cd0-4643-fd7ba798115c.png)

実際の使い方としては、ロジックアプリ内で HTTP アクションを使用する際に、下記画像のように認証の種類を「Managed Identity」と設定することで、ロジックアプリ自体の ID を使って（パスワードやシークレットがなくても）API をコールできる、というイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/a25f788b-e0fc-4944-1ef7-1f987c820a34.png)

また、作成したロジックアプリが不要になった際、ロジックアプリを削除すれば（ロジックアプリと一体として作成されている）マネージド ID も一緒に削除されるので、いつ何のために使ったか分からない謎のシステム用アカウントがある・・というシステム運用あるあるも回避できます。

マネージド ID はロジックアプリの他にも、Automation アカウントや Function アプリ、VM などの Azure リソースで有効化できます。

# マネージド ID で Graph API を使えるか？
ここまで読んできて「なるほど、マネージド ID で Graph API を使おう！」と思われると思いますが、これを実現するには少し課題があります。

端的に言うと Entra ID のダッシュボードからマネージド ID に対して Graph API の権限を付与することができない、という課題です。

普段からアプリ登録して Graph API を使っている方であれば分かると思いますが、アプリに Graph API のアクセス許可を割り当てるメニューは Entra ID の「アプリ登録」メニューにあります。しかし、マネージド ID はそこには表示されず、「エンタープライズ アプリケーション」メニューにだけに表示されます。

そして、エンタープライズ アプリケーションからアプリに API のアクセス許可を付与することはできません。（下記画像に「アクセス許可の追加」的なボタンがないことからも確認できます）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/17776071-17c7-596d-48ad-746a04c98c7b.png)

これはなぜかというと、マネージド ID の所有者はあなたではないからです。

そのアプリ（以降、Entra ID 上にあるアプリの呼び方であるサービス プリンシパルと言います）がどのアクセス許可を必要とするかは所有者が決めます。「アプリ登録」はその組織でサービス プリンシパルを新規に作成することを意味するためそのタイミングで API のアクセス許可を追加できますが、別のテナントで作成された自テナントが所有者ではないサービス プリンシパルのアクセス許可を変更することはできないということです。

# マネージド ID に Graph API の権限を付与する
### アプリ ロール
とはいえ、マネージド ID に API アクセス許可を付与できないということではありませんので、その背景を理解するために、ちょっと寄り道になりますが「アプリ ロール」の説明をします。

Graph API のアプリケーションのアクセス許可の実体は、（マイクロソフト テナントにアプリ登録された）「Microsoft Graph」という名前のサービス プリンシパルの「アプリ ロール」と呼ばれるものです。

試しに Get-MgServicePrincipal というコマンドレットを使って「Microsoft Graph」という名前のサービス プリンシパルのいくつかの情報を取得してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/e86af83c-5c77-ddaf-559d-ca3ff619dbbb.png)

AppOwnerOrganizationId は f8cdef31-a31e-4b4a-93e4-5f571e91255a となっていますが、これはマイクロソフトの Entra テナント ID ですので、サービス プリンシパルの所有者がマイクロソフトであることが確認できます。

その下の AppRoles にはいくつかの ID が並んでいますが、例えば最初の d07a8cc0-3d51-4b77-b3b0-32704d1f69fa は Graph API のアプリケーションのアクセス許可の 1 つである「AccessReview.Read.All」の一意識別子です。

:::note
Graph API のアクセス許可のリストは下記 URL で確認できます。
https://learn.microsoft.com/en-us/graph/permissions-reference
:::

:::note
本ブログで紹介しませんでしたが、ユーザー委任の API のアクセス許可の実体はサービス プリンシパルの「スコープ」と呼ばれるもので、OAuth2PermissionScopes プロパティで管理されています。
:::

### アプリ ロールの割り当て
Microsoft Graph というサービス プリンシパルにはアプリ ロールが定義されており、これが Graph API のアプリケーションのアクセス許可の実体でした。

Entra テナントの管理者は、このアプリ ロールを自テナントにある任意のサービス プリンシパルに「割り当てる」ことができます。あまり違いを意識する必要はないかもしれませんが、サービス プリンシパルのアプリ ロールを直接編集するのではなく、他のサービス プリンシパルが持っているアプリ ロールを借りてきて紐づける、みたいなイメージでしょうか。

先ほどと同じコマンドで作成済みのマネージド ID を取得してみると、確かにサービス プリンシパルとして取得できることが確認できます。ServicePrincipalType が ManagedIdentity となっている点が先ほどと異なりますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/23f61654-9812-3265-9948-df38af4acb65.png)

このようにマネージド ID もサービス プリンシパルですので、マネージド ID にアプリ ロール（Graph API のアプリケーションのアクセス許可）を割り当てることができるということになります。

アプリ ロールのサービス プリンシパルへの割り当ては New-MgServicePrincipalAppRoleAssignment というコマンドレットを使用することができ、下記のチュートリアルに沿って割り当て、更新、削除が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/094973c2-52a0-4be0-7568-3506c520d284.png)

https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/tutorial-grant-app-only-api-permissions

# やってみよう！
それでは、ロジックアプリでマネージド ID を有効化し、マネージド ID にアプリ ロールを割り当てることで、実際にマネージド ID から Graph API をコールしてみましょう。

今回は例として User.Read.All というアプリケーションのアクセス許可を付与してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/1bca5ec8-f358-2447-371b-bd3b31e9f0d8.png)

ロジックアプリに HTTP アクションを追加し、URI にテナント内のユーザーを取得する API エンドポイントの URL を指定します。（ユーザー タイプをメンバーに限定するフィルタをしています）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/ed2de681-1c85-5d44-ae07-36d4a4fab556.png)

認証の種類は「Managed identity」を選択します。Audience に https://graph.microsoft.com を指定するのを忘れないでください。指定しないと Azure REST API を使おうとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/8a200057-85c2-fa3d-7cc9-55ddd0056baa.png)

実行結果がこちらです。レスポンスの value という配列にユーザー情報が含まれていることが確認でき、マネージド ID で認証を通して Graph API を利用出来ていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563573/0fe4ad4e-2aee-8b3f-5f6b-90ebb07862a2.png)

上記はユーザーを取得するという基本的な例でしたが、Graph API のリファレンスを見ていただくと、本当に様々なことが実現できることが分かりますので、ざっと見てみてください。

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

# まとめ
Graph API を利用すると様々な業務の自動化が可能になる一方で、Entra ID にアプリ登録をするとシークレットや証明書の管理が必要になってしまったり、システムが不要になった場合の棚卸が必要になってしまったりといった課題が発生します。

今回紹介した方法で上記の課題が解消され、安全に Graph API を利用することが出来るようになりますので、ぜひ参考にしていただければと思います。

本ブログの内容が、安全な方法で業務の自動化を実現する一助となれば幸いです。

# 参考情報
https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/

https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/tutorial-grant-app-only-api-permissions

https://learn.microsoft.com/en-us/graph/permissions-reference

https://jpazureid.github.io/blog/azure-active-directory/oauth2-application-resource-and-api-permissions/