最近の Azure AD B2C は組み込みユーザーフローの機能がかなり増えているので、ある程度のことなら Azure Portal からの設定で実現できますが、結局は IEF を使ったカスタムポリシーが必要になるケースも多いです。
Azure AD B2C のカスタムポリシーは触ったことがある人ならわかると思いますが、XML ベースの難解な定義の集合体になっています。確実にバージョン管理と CI/CD を行っておきたいですが、Azure Portal からアップロードする運用になっていることも多いのではないかと思います。
将来的にそのあたりを Terraform Provider for Azure AD が綺麗に解決してくれそうなので、今回はカスタムポリシーを利用する際に必要な IdentityExperienceFramework
と ProxyIdentityExperienceFramework
という 2 つのアプリケーションを登録し、自動化の準備をしておきます。
カスタムポリシーを使う上で必要になるのでセットアップツールも用意されていますが、全体を Terraform で管理することを踏まえると上手くモジュール化し、再利用可能な形にしておくのが正解だと思います。
IEF 用アプリケーションと言っても、登録方法は通常のアプリケーションとほぼ同じです。前回書いたエントリでもほぼ同じことをしているので参照してください。
単純に Azure AD B2C を利用するアプリケーションとは異なり、API の公開とその API に対する許可の付与が絡み合っているので、Azure Portal で作業する場合は結構ミスが多くなります。
Terraform 化すると Application ID を反対にしてしまうといったありがちなミスを減らすことが出来ます。
共通で利用するリソースを定義
まずは 2 つのアプリケーションの両方で利用するリソースを定義しておきます。
Microsoft Graph のサービスプリンシパルは毎回使うのでお馴染みですが、今回は Azure AD B2C のテナント名をローカル変数として用意しておきます。
terraform { required_providers { azuread = "~> 2.0" } } data "azuread_domains" "default" { only_initial = true } 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 } locals { domain_name = trimsuffix(data.azuread_domains.default.domains.0.domain_name, ".onmicrosoft.com") }
本来ならテナント名が azuread_domains
のプロパティで取れると楽なのですが、ドメイン名という形でのみ取得可能なので trimsuffix
を使ってテナント名を切り出しています。
確認はしていないのですが、カスタムドメインを当てた場合には挙動が変わる可能性もあります。
IdentityExperienceFramework を定義
2 つのアプリケーション間には API の依存関係が存在するため、先に IdentityExperienceFramework
から定義していきます。こちらは API を公開する側になります。
注意点としては sign_in_audience
に AzureADMyOrg
を指定することと、identifier_uris
に指定する値です。Azure Portal から設定する場合は Application ID が使われますが、Terraform では事前にテナント内でユニークな値を指定する必要があります。今回は適当な値を指定しています。
resource "random_uuid" "user_impersonation" {} resource "azuread_application" "identity_experience_framework" { display_name = "IdentityExperienceFramework" sign_in_audience = "AzureADMyOrg" identifier_uris = ["https://${local.domain_name}.onmicrosoft.com/identity-experience-framework"] api { requested_access_token_version = 2 oauth2_permission_scope { admin_consent_description = "Allow the application to access IdentityExperienceFramework on behalf of the signed-in user." admin_consent_display_name = "Access IdentityExperienceFramework" enabled = true id = random_uuid.user_impersonation.result type = "User" value = "user_impersonation" } } 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://${local.domain_name}.b2clogin.com/${local.domain_name}.onmicrosoft.com"] } } resource "azuread_service_principal" "identity_experience_framework" { application_id = azuread_application.identity_experience_framework.application_id } resource "azuread_service_principal_delegated_permission_grant" "identity_experience_framework" { service_principal_object_id = azuread_service_principal.identity_experience_framework.object_id resource_service_principal_object_id = data.azuread_service_principal.msgraph.object_id claim_values = ["openid", "offline_access"] }
それ以外は前回作成した Azure AD B2C のアプリケーションとほぼ変わりません。
redirect_uris
はチュートリアルで指定されたフォーマットで指定すれば良いです。実際のところ URI であればなんでも良さそうですが、ドキュメントの通りに従っておくことにします。
この定義を使って terraform apply
を実行すると、以下のようにアプリケーションが作成されます。
必要な API が公開されていることや、管理者の許可が付与されていることも確認出来るはずです。
ProxyIdentityExperienceFramework を定義
次に API を利用する側となる ProxyIdentityExperienceFramework
を定義していきます。こちらはパブリッククライアントとして定義するので、これまでとは少し違います。
ポイントは fallback_public_client_enabled
に true
を指定するのと、public_client
ブロックを追加して redirect_uris
に myapp://auth
を指定することです。
resource "azuread_application" "proxy_identity_experience_framework" { display_name = "ProxyIdentityExperienceFramework" sign_in_audience = "AzureADMyOrg" fallback_public_client_enabled = true 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" } } required_resource_access { resource_app_id = azuread_application.identity_experience_framework.application_id resource_access { id = azuread_application.identity_experience_framework.oauth2_permission_scope_ids["user_impersonation"] type = "Scope" } } public_client { redirect_uris = ["myapp://auth"] } } resource "azuread_service_principal" "proxy_identity_experience_framework" { application_id = azuread_application.proxy_identity_experience_framework.application_id } resource "azuread_service_principal_delegated_permission_grant" "proxy_identity_experience_framework_msgraph" { service_principal_object_id = azuread_service_principal.proxy_identity_experience_framework.object_id resource_service_principal_object_id = data.azuread_service_principal.msgraph.object_id claim_values = ["openid", "offline_access"] } resource "azuread_service_principal_delegated_permission_grant" "proxy_identity_experience_framework_ief" { service_principal_object_id = azuread_service_principal.proxy_identity_experience_framework.object_id resource_service_principal_object_id = azuread_service_principal.identity_experience_framework.object_id claim_values = ["user_impersonation"] }
このアプリケーションは IdentityExperienceFramework
が公開している API を必要とするので、required_resource_access
ブロックと azuread_service_principal_delegated_permission_grant
リソースの追加が必要になります。
そしてこの定義に対して terraform apply
を実行すると、管理者の許可が付与された状態で作成されます。
定義は少し長くなり、リソースも複数出てきますがカスタムポリシーを利用する場合の全てで同じ定義が利用できるので、正確な定義をモジュール化して使いまわしが出来るというメリットは大きいです。
カスタムポリシーから利用する
最後に作成した 2 つのアプリケーションを利用して、実際にカスタムポリシーを作成して動作するか確認しておきます。カスタムポリシー自体はチュートリアルにもある starterpack を使いました。
ポリシーキーの作成は行えないので TokenSigningKeyContainer
と TokenEncryptionKeyContainer
は手動で作成しましたが、将来的には Terraform から作成できるはずです。
アップロード前に starterpack に用意された XML に対して、Application ID とテナント名の修正を行っておきます。正直なところ手動だとミスが起こりがち作業ですが、ポリシーのアップロードに Terraform が対応すれば templatefile
関数を使って、この辺りの修正も自動化出来るようになります。
修正した XML ファイルを Azure Portal からアップロードしていきます。エラーが出なければ問題ないです。
設定に問題が無ければ、アップロードした B2C_1A_SIGNUP_SIGNIN
を選んでユーザーフローを実行すると、ログインから id_token
の取得まで成功するはずです。
割と設定項目が多いので、手動で設定すると大体 1 度ははまるのですが、Terraform を使ってコード化することで確実にセットアップできるようになりました。Azure AD は IaC との相性が凄く良いと感じています。