しばやん雑記

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

Terraform Provider for Azure AD を使って Azure AD B2C で利用するアプリケーションを管理する

未だ個人的な Terraform Provider for Azure AD ブームが続いているので、今回は Azure AD B2C の管理を目的に利用してみます。Azure サブスクリプションが紐づく Azure AD テナントとは異なり、サブスクリプションは持たず独立したテナントとなるので、扱いは少し工夫する必要があります。

ちなみに Azure AD B2C 向けで使われる一部の Graph API がまだベータ版だからか、カスタムポリシーなどの Azure AD B2C のコア機能と呼べるものには未対応となっています。

Azure AD B2C への対応はかなり注目されていて、Azure AD B2C のように独立したテナントや設定項目が多いリソース程、Terraform などを使ってコードで管理してデプロイも自動化したいはずなので、対応を楽しみに待ち続けたいと思います。

ポリシーキーやカスタムポリシーを含め Terraform で一括管理出来るようになると、ポリシーのアップロードミスやキーの有効期限問題など一気に解決できるはずです。

現状では Terraform Provider for Azure AD で行えるのは Azure AD B2C のアプリケーション登録ぐらいになりますので、まずはこの辺りの設定方法から押さえておきたいと思います。方針としては Azure AD の時とほぼ同じなので、以前書いた以下のエントリを参考にしてください。

大きな違いとしては sign_in_audience として AzureADandPersonalMicrosoftAccount を指定することと、明示的に openidoffline_access のスコープを追加することぐらいになります。同時にサービスプリンシパルを作成しておくのを忘れないようにします。

実際に用意した Azure AD B2C に対して jwt.ms に Implicit flow を利用して、発行した id_token 付きでリダイレクトするアプリケーション定義が以下になります。

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

data "azuread_client_config" "current" {}

data "azuread_application_published_app_ids" "well_known" {}

data "azuread_service_principal" "msgraph" {
  application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
}

resource "azuread_application" "jwt_ms" {
  display_name     = "jwt.ms"
  sign_in_audience = "AzureADandPersonalMicrosoftAccount"
  owners           = [data.azuread_client_config.current.object_id]

  api {
    requested_access_token_version = 2
  }

  required_resource_access {
    resource_app_id = data.azuread_service_principal.msgraph.application_id

    resource_access {
      id   = data.azuread_service_principal.msgraph.oauth2_permission_scope_ids["openid"]
      type = "Scope"
    }

    resource_access {
      id   = data.azuread_service_principal.msgraph.oauth2_permission_scope_ids["offline_access"]
      type = "Scope"
    }
  }

  web {
    redirect_uris = ["https://jwt.ms/"]

    implicit_grant {
      access_token_issuance_enabled = true
      id_token_issuance_enabled     = true
    }
  }
}

resource "azuread_service_principal" "jwt_ms" {
  application_id = azuread_application.jwt_ms.application_id
}

この時 AzureADandPersonalMicrosoftAccount を設定すると、requested_access_token_version に対して 2 を設定する必要があるようです。未設定の場合は実行時にエラーとなってしまいます。

必要なリソース定義はこれだけなので、次は terraform apply を実行して Azure AD B2C 上にアプリケーションを作成してみるのですが、Azure CLI はデフォルトではアカウントの親テナントに対してログインしまいます。Azure Portal のようにログイン後にテナントを切り替える機能はありません。

そのため azure login 時に --tenant オプションと --allow-no-subscriptions オプションを渡すことで、Azure サブスクリプションを持たないテナントに対してログインを行います。

az login --tenant ***.onmicrosoft.com --allow-no-subscriptions

これで特定のテナントかつ Azure サブスクリプションを持たない場合にも Azure CLI でログインできるので、Terraform から何も設定する必要なく対象に出来ます。

ログイン後は terraform apply を実行すると Azure AD B2C に jwt_ms という名前のアプリケーションが作成されるので、そのまま適当に追加したユーザーフローでログインを試してみると、以下のようにあまり見覚えのないエラーになってしまいます。

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

エラーメッセージを見ると大体理由は分かると思いますが、これは openidoffline_access に対して管理者の同意が付与されていないことが原因です。

実際に作成されたアプリケーションを確認すると Status は空のままです。

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

Azure AD の場合はユーザー単位で許可を行えましたが、Azure AD B2C の場合はこれらのスコープに対して事前に管理者の同意を付与しておく必要があるようです。

Azure Portal では "Grant admin consent for ***" ボタンを押すだけで良かったですが、Terraform で行うためには azuread_service_principal_delegated_permission_grant リソースを追加し、必要な権限を持つユーザーとして実行する必要があります。

azuread_service_principal_delegated_permission_grant | Resources | hashicorp/azuread | Terraform Registry

このリソースはサービスプリンシパルに対して行うので作成は必須です。そして許可を付与するのもサービスプリンシパル単位になるので、場合によっては複数個定義する必要があります。

resource "azuread_service_principal_delegated_permission_grant" "jwt_ms" {
  service_principal_object_id          = azuread_service_principal.jwt_ms.object_id
  resource_service_principal_object_id = data.azuread_service_principal.msgraph.object_id
  claim_values                         = ["openid", "offline_access"]
}

今回のスコープは両方とも Microsoft Graph になるので、このように 1 つだけで済みます。

追加した定義に対して terraform apply を実行すると、以下のように管理者の同意が付与されます。

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

ユーザーとしてログインしている場合は高確率で Global Administrator なので問題ないですが、CI で実行する場合には追加する権限が変わってくるので注意が必要です。CI に関しては別のエントリでまとめます。

もう一度ユーザーフローから作成されたアプリケーションを使ってログインを行うと、今度は正しく id_token が発行されて jwt.ms で確認出来るようになります。

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

これでログインが行えるアプリケーションを Azure AD B2C に Terraform で作成できましたが、実際には App Service や Static Web Apps に設定してログインできる状態にしなくてはなりません。

この時の問題として Azure AD B2C は Azure サブスクリプションを持つ Azure AD とは別になるので、簡単には Terraform から Azure AD B2C と App Service の同時デプロイが行えないのですが、現実問題としてそこの統合は必須なので別のエントリで方法をまとめます。