Azure AD マルチテナントアプリを用いてテナントまたぎのリソース管理


この記事は「[NEXTSCAPE Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nextscape)」の3日目です。

本日は、Azureのリソースを管理するサービスを作ったときに利用した
「***Azure Active Directory （以下Azure AD）のマルチテナントアプリ***」をご紹介いたします。

Azure リソースにアクセスしたり操作したりするサービスを作ろうと思ったときに、
認証の部分で躓いてしまったときの話です。

詳細については私自身まだ理解しきれていない部分も多いのですが、
当時こういうケースで利用できるものとして理解するのに時間がかかりましたので、
同じようなことをしたい方のソリューションの参考になれば幸いです。

## 実現したかったこと

以下のような仕様を持つサービスを検討していました。

- 仕様
    - サービスには自分のAzure AD 組織アカウントでログイン
    - 所属しているAzure AD テナント配下の全リソース（App Service, SQL Database, etc.）が探索できる
    - リソースを探索するUIは、テナント > サブスクリプション > リソースグループの順にフィルタリング
        1. 自分の組織アカウントが所属しているAzure AD テナントの一覧を表示
        2. 1で選択したテナント内のサブスクリプション一覧を表示
        3. 2で選択したサブスクリプション内のリソースグループ一覧を表示<br/>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216819/1b1ba284-9587-83b1-482e-56b9cb890a2e.png)

## 当初の悩み
このUIを実現する方法を調べてみると・・・

テナント、サブスクリプション、リソースグループは、
それぞれ以下のAzure Resource Management Rest API エンドポイントを利用して取得できることが分かりました。

- テナント一覧の取得：[Tenants - List](https://docs.microsoft.com/en-us/rest/api/resources/tenants/list)
- サブスクリプション一覧の取得：[Subscriptions - List](https://docs.microsoft.com/en-us/rest/api/resources/subscriptions/list)
- リソースグループ一覧の取得：[Resource Groups - List](https://docs.microsoft.com/en-us/rest/api/resources/resourcegroups/list)

また、このRest API を利用するためには、
以下のプロセスでアクセストークンを取得し、そのアクセストークンが必要であることもわかりました。

- Azure ADに「アプリ」を登録してRest APIを呼び出すために必要なアクセス許可を定義
- このアプリを利用した[一連の認証フロー](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-implicit-grant-flow#protocol-diagram)に則って、APIを利用するためのアクセストークンを取得

ここまでは、Azure のリソースを操作するAPIを利用する上での一般的な認証プロセスですが、
今回のように複数のテナントにまたがってリソースを取得するUIを作ろうとした際には、
「***どうやってユーザーの所属するテナントにアプリを登録するか？***」を検討しなければいけません。

「***テナントの切り替えに伴いその配下のリソースにアクセスするためのアクセストークンも取得しなおす***」
必要があるため、このUIを実現するためには、***①で選択できる全テナントに対して*** アプリが必要になります。

しかし、アプリ登録の方法として以下のどちらも、サービスの運用として現実的な方法とは思えません。

- サービス提供者である我々がユーザーの所属するテナントを事前に特定してアプリを登録させてもらう
- 利用者であるユーザーがAzure AD上でのアプリ登録を実施する

## 解決策

この「***テナント毎にアプリを作成する***」という要件をとてもスマートな方法で実現できるのが
「***Azure AD マルチテナントアプリ***」でした。

マルチテナントアプリとは、あくまでもアプリオブジェクトではあるのですが、
「ユーザーのテナントにユーザーの同意を得てアプリ（サービスプリンシパル）を登録してくれるアプリ」です。

参考：[Azure Active Directory のアプリケーション オブジェクトとサービス プリンシパル オブジェクト](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/app-objects-and-service-principals)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216819/4bef5fbd-dea6-6da2-0d87-4f7d6f0af08b.png)


これによって、ユーザーはAzure AD テナントごとに手動でアプリ登録をする必要がなくなり、
***サービス側でリダイレクト表示される承認ダイアログに応答するだけ***で、
①で選択したテナントのリソースにアクセスできるサービスプリンシパルを生成することができます。

生成されたサービスプリンシパルには、元のアプリと同じClientIDが付与されるので、サービス側では元のアプリ（マルチテナントアプリ）
のClientIDさえ知っておけばよいという寸法です。

これぞ、求めていたもの！

ちなみに、自力で調査していた時にはこの方法にたどり着けず半ばあきらめていました。
Microsoft の Azure サポートの方に問い合わせたところこの方法をご紹介いただきました。
（サポート担当者の方に感謝！）

## 手順

具体的に、どのような手順でこれを実現するかを説明していきます。

### Azure AD テナントにマルチテナントアプリを作る

まずはアプリ登録をします。基本的には通常のアプリ登録と同じです。
Azure AD > アプリの登録から登録します。
「認証」>「サポートされているアカウントの種類」という設定にて、
「***任意の組織ディレクトリ内のアカウント(任意のAzure ADディレクトリ-マルチテナント)***」オプションを選択します。<br/>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216819/16b43c39-9900-84ad-6c0b-067f68a9e1a7.png)


### マルチテナントアプリに必要なアクセス許可を付与する

今回はAzure Resource Management Rest API を利用するので、
「このアプリはAzure Resource Management Rest APIを利用できるアプリです」ということを定義します。

アプリのAPIのアクセス許可 > アクセス許可の追加にて、Azure Service Managementを選択し、
user_impersonation スコープのアクセス許可をチェックして、アクセス許可の追加をクリックします。
<br/>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216819/095ac719-7828-fa2e-db64-31efec795a5f.png)

これで、アクセストークンを取得する準備が整いました！

### msal.jsを利用してRest APIを利用するためのアクセストークンを取得する

さて次は、サービス側のアプリケーションコードで、アクセストークンを取得する処理を実装します。
このサービスでは、フロントエンド側でSingle Page ApplicationのフレームワークであるReact.jsを利用していましたので、
msal.jsというライブラリを用いてアクセストークンを取得します。

msal.jsライブラリでは、configurationにauth.authorityというプロパティがあり、
そこでテナントIDを指定することによって、アクセストークンを取得したいテナントをコントロールできます。
（以下、コードの一部抜粋）

```JavaScript

export function msal(tenantId, clientId) {
    return new UserAgentApplication({
        auth: {
            clientId: clientId,
            authority: `https://login.microsoftonline.com/${tenantId}`,
            validateAuthority: true,
            postLogoutRedirectUri: "http://localhost:3000",
            navigateToLoginRequestUrl: false
        }
});
}
```

msal.jsライブラリの具体的な使い方は、例えば以下のドキュメントが参考になると思います。

- [チュートリアル:ユーザーをサインインして、JavaScript シングルページ アプリケーション (SPA) から Microsoft Graph API を呼び出す](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/tutorial-v2-javascript-spa)

上記のチュートリアルでは、Graph APIへのアクセスを想定していますが、
今回のサービスではAzure Resource Management Rest APIにアクセスしますので、
ログインとアクセストークン取得時のscopesの値は以下になります。

```
scopes:[
'https://management.azure.com/user_impersonation'
]
```

ここは、利用したいAPIの種類に応じて変わります。

あとは、msal.jsを使って取得したアクセストークンを、Rest API呼び出し時に渡してあげるだけです。

## まとめ

この記事では、「マルチテナントアプリ」が利用できるケースの一つをご紹介いたしました。
同じような処理をしようとして悩んでいる方に参考にしていただけると幸いです。

この記事を書く際に改めてドキュメントを読み返してみたところ、この用途を知ってから読むと
いろいろと参考になるものがたくさんありましたので、いくつか参考ドキュメントとして挙げさせていただきます。
私もまだ理解しきれていない詳細をこれを読んで理解したいと思いました。

- [Microsoft ID プラットフォームと暗黙的な許可のフロー](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-implicit-grant-flow#protocol-diagram)
- [Microsoft ID プラットフォームと OAuth 2.0 認証コード フロー](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-oauth2-auth-code-flow)
- [単一テナントアプリとマルチテナントアプリ](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/single-and-multi-tenant-apps)
- [Microsoft ID プラットフォーム エンドポイントでのアクセス許可と同意](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/v2-permissions-and-consent)
- [Azure AD アプリケーションの同意エクスペリエンスについて](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/application-consent-experience)

## 余談

実は「今年 Advent Calendar 参加しませんか」と声をかけたのがかなり遅いタイミングだったのですが、
瞬く間に枠が埋まっていく光景を目にして、Nextscape エンジニアのバイタリティに感動しているところです。
（皆さん、ありがとうございます！）

NextscapeではシステムはAzure上にデリバリーすることがほとんどなので、
少し回りに聞くと大抵いろいろな人が「こうすればできるよ」と教えてくれます。
クラウドでの開発（特にPaaS）に興味がある方にとっては、おすすめできる環境だと思います！

最後まで読んでいただきありがとうございました！

明日は、@hf3443さんです！お楽しみに！