しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

複数の Azure AD テナントに対応した Service Principal を作成して Terraform から利用する

App Service と Azure AD B2C というように Azure AD テナントが分かれている場合には、いつも通りの Service Principal を使った方法だと、当然ながらどちらかのテナントしか操作できません。

以前書いたように Azure AD B2C 側のアプリケーションを管理しつつ、App Service Authentication の設定を追加する際には同じ Terraform 定義内で行う必要があります。

MSA や組織アカウントで Azure CLI にログインしている場合は、特に気にすることなく Terraform Provider がテナント単位でいい感じに扱ってくれますが、Service Principal ではそうもいきません。

ちなみに同一 Azure AD 内の複数サブスクリプションへの対応は module を使って分離する、サブスクリプション別に Provider を定義して別名を付けて管理するなど、いくつか対応方法があります。以前書いたこともあるので、興味がある方はこちらを参照してください。

Azure AD テナントが複数になるということは、必要となる Service Principal が大きな問題になって来ます。単純に考えれば、テナント別に Service Principal を作成して設定すれば解決しますが、管理が複雑になりますし値を環境変数で渡せなくなるので不便です。

実は知らなかったのですが、1 つの Service Principal を複数テナント向けに構成することが出来るようです。これを使って複数の Azure AD テナントに対応しつつ、シンプルな認証情報の管理を実現したいと思います。

複数テナントに対応した Service Principal の作成方法はドキュメントが異常に少ないですが、以下の AKS から ACR にアクセスするドキュメントが最適でした。手順はほぼこのドキュメントの通りです。

正直なところ割と手間がかかる作業になります。Azure AD の知識が無いと苦労しそうです。

自動化が難しい処理もあるので、最初だけと思って手動で頑張るしかないと思っていますが、実はもっと簡単に実現できる気もしています。

複数テナントに対応した Service Principal を作成する

これまで Service Principal というか Azure AD アプリケーションを作成する際は、殆どのケースで Single tenant を選んでいると思いますが、複数テナント向けの場合は Multitenant を選んで作成します。

この Account types の設定以外は Single tenant 向けと同じです。複数テナントで利用する際に別テナント側で許可を与えるという作業が追加で必要になります。

f:id:shiba-yan:20220321193805p:plain

ドキュメントには Redirect URIs に https://www.microsoft.com を指定するように書いてありますが、特に意味があるものではなく OAuth 2 の同意画面を出すために必要というだけです。認可コードを使ってトークンを取る必要すらないので、いつも通り jwt.ms でも問題ないです。

Azure AD アプリケーションを作成したら、以下の URL にアクセスして同意画面を出します。

https://login.microsoftonline.com/<B2C Tenant ID>/oauth2/authorize?client_id=<Application ID>&response_type=code&redirect_uri=https://www.microsoft.com

この時に組織の代理として同意する必要があるので、Azure AD B2C のテナントに Global Administrator として登録されたアカウントを使うのが無難です。

Azure Portal から作成すると自動的に API Permissions に User.Read が追加されるので問題ないですが、同意画面がエラーで表示されない場合は User.Read が Delegated で追加されているか確認します。

f:id:shiba-yan:20220321190147p:plain

承諾ボタンを押すと指定した URL に認可コード付きでリダイレクトされますが、前述したようにトークンが必要なわけではないので同意が終わればブラウザごと閉じて良いです。

同意後に Azure AD B2C テナントの Enterprise Applications を確認すると、Azure AD テナント側で作成したアプリケーションが追加されていることが確認できます。

f:id:shiba-yan:20220321214835p:plain

後はこの Service Principal に対して Azure AD B2C を操作するための権限を追加すれば良いです。

ただし Service Principal に対しては Azure Portal から権限を追加することが出来ないので、Azure CLI や PowerShell を使う必要があります。

追加した Service Principal に Graph API の権限を追加する

当然ながら、この時点では Service Principal には権限がほぼ付いていません。Azure リソースの場合は IAM 設定から簡単に追加できますが、Azure AD の権限追加は少し手間がかかります。

具体的な設定方法は以下のドキュメントの通りなので省略しますが、Managed Identity に対して Graph API のアクセス権限を追加するのと同じ手順です。

Azure CLI や PowerShell を使って必要な Application.ReadWrite.AllDirectory.ReadWrite.All を追加すると、Azure Portal から以下のように確認出来ます。

既に管理者として同意が与えられた状態なので、更にボタンを押す必要はありません。

f:id:shiba-yan:20220321190159p:plain

これで Azure AD テナント以下にあるサブスクリプションを操作しつつ、Azure AD B2C のアプリケーションも操作可能な Service Principal が完成です。

後は実際に試すわけですが、以下のような簡単な Terraform 定義を使って確認します。注意点としては Tenant ID をそれぞれの Provider 単位で明示的に指定する必要があることです。

terraform {
  required_providers {
    azuread = "~> 2.0"
    azurerm = "~> 2.0"
  }
}

provider "azuread" {
  tenant_id = "<Azure AD B2C Tenant ID>"
}

provider "azurerm" {
  tenant_id = "<Azure AD Tenant ID>"

  features {}
}

resource "azuread_application" "default" {
  display_name     = "Cross Tenant Test"
  sign_in_audience = "AzureADandPersonalMicrosoftAccount"

  api {
    requested_access_token_version = 2
  }
}

resource "azurerm_resource_group" "default" {
  name     = "rg-crosstenant-test"
  location = "westus2"
}

作成した Service Principal の情報はこれまで通りで、環境変数 ARM_CLIENT_ID / ARM_CLIENT_SECRET / ARM_SUBSCRIPTION_ID として渡してあげるだけです。当然ながら ARM_TENANT_ID は必要ないです。

環境変数を追加した後に terraform apply を実行すると、Azure AD と Azure AD B2C それぞれに対してリソース作成に成功するのが確認出来るはずです。

f:id:shiba-yan:20220322180130p:plain

GitHub Actions や Azure Pipelines で実行する場合もローカルで動かす場合と同じです。環境変数に作成した Service Principal の情報を追加すれば問題なく動作します。

特に GitHub Actions で利用する場合は Client Secret ではなく Federated credentials を使いたいところですが、Terraform Provider 側の対応が必要になるので時間がかかりそうです。