使用Terraform创建Azure AD B2C用户流时遇应用查找失败问题
问题现象
使用Terraform创建Azure AD B2C应用及登录/注册用户流时,执行null_resource.admin_consent中的本地脚本时,始终无法通过az ad app show找到已创建的应用(ID:d4be9c7c-acf4-4cc8-b27a-b18759d40034),导致后续管理员授权步骤终止,错误输出显示循环语法未被正确解析:
Attempt {1..80}: Checking if application is available...
Application not found. Retrying in 15 seconds...
Error: Application d4be9c7c-acf4-4cc8-b27a-b18759d40034 still not found after waiting. Exiting...
问题根源
- Shell语法兼容性问题:脚本中的
for i in {1..80}是Bash专属语法,如果Terraform的local-exec默认使用Windows CMD/PowerShell,该语法不会被解析,导致循环仅执行一次而非80次。 - 租户上下文错误:
az ad app show默认查询当前CLI上下文的Azure AD租户,若未切换到B2C租户,会在错误的租户中查找应用,自然无法找到。 - 依赖关系不严谨:当前
admin_consent的依赖未包含权限配置步骤,可能在权限未完成注册时就尝试授权。
解决方案
1. 修复Shell循环语法
根据运行环境选择兼容的循环语法,或强制local-exec使用Bash:
- 若使用Windows,确保安装Git Bash并指定
interpreter = ["bash", "-c"]; - 若使用PowerShell,改用
for ($i=1; $i -le 80; $i++)语法。
2. 指定B2C租户ID
在所有az命令中添加--tenant <B2C_TENANT_ID>参数,确保查询和操作的是正确的B2C租户。
3. 优化依赖关系
将admin_consent的依赖添加azuread_service_principal_delegated_permission_grant.b2c_permission,确保权限配置完成后再执行授权。
4. 移除冗余的应用ID提取
直接使用azuread_application.b2c_app.client_id作为应用ID,无需通过regex从id属性提取(id属性是Terraform资源路径,client_id才是Azure AD应用的实际ID)。
修改后的完整代码
# Microsoft Graph Service Principal data "azuread_application_published_app_ids" "well_known" {} resource "azuread_service_principal" "msgraph" { client_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph use_existing = true } # Create Azure AD B2C Application resource "azuread_application" "b2c_app" { display_name = "Terraform-B2C-App" required_resource_access { resource_app_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph resource_access { id = azuread_service_principal.msgraph.app_role_ids["User.Read.All"] type = "Role" } resource_access { id = azuread_service_principal.msgraph.oauth2_permission_scope_ids["User.ReadWrite"] type = "Scope" } } } # Ensure App Creation Is Recognized in Azure resource "time_sleep" "wait_for_app" { depends_on = [azuread_application.b2c_app] create_duration = "120s" # Increased to allow Azure API propagation } # Create Service Principal for the B2C App resource "azuread_service_principal" "b2c_sp" { client_id = azuread_application.b2c_app.client_id depends_on = [time_sleep.wait_for_app] } # Assign Delegated Permissions to Service Principal resource "azuread_service_principal_delegated_permission_grant" "b2c_permission" { service_principal_object_id = azuread_service_principal.b2c_sp.object_id resource_service_principal_object_id = azuread_service_principal.msgraph.object_id claim_values = [ "openid", "offline_access", "User.Read", "User.ReadWrite", "email", "profile" ] depends_on = [azuread_service_principal.b2c_sp] } # Grant Admin Consent with Fixed Wait & Verification resource "null_resource" "admin_consent" { provisioner "local-exec" { interpreter = ["bash", "-c"] # 强制使用Bash,Windows需安装Git Bash/WSL command = <<EOT APP_ID="${azuread_application.b2c_app.client_id}" B2C_TENANT_ID="<你的B2C租户ID>" # 替换为实际的B2C租户ID echo "Waiting for the application ($APP_ID) to be fully available in Azure AD B2C..." # 修复后的Bash循环 for ((i=1; i<=80; i++)); do echo "Attempt $i: Checking if application is available..." if az ad app show --id $APP_ID --tenant $B2C_TENANT_ID > /dev/null 2>&1; then echo "Application is now available." break fi echo "Application not found. Retrying in 15 seconds..." sleep 15 done # 验证应用存在 if ! az ad app show --id $APP_ID --tenant $B2C_TENANT_ID > /dev/null 2>&1; then echo "Error: Application $APP_ID still not found after waiting. Exiting..." exit 1 fi echo "Granting admin consent..." for ((attempt=1; attempt<=3; attempt++)); do if az ad app permission admin-consent --id $APP_ID --tenant $B2C_TENANT_ID; then echo "Admin consent granted successfully." exit 0 fi echo "Admin consent failed. Retrying in $((attempt * 15)) seconds..." sleep $((attempt * 15)) done echo "Admin consent failed after multiple attempts. Exiting." exit 1 EOT } depends_on = [azuread_service_principal_delegated_permission_grant.b2c_permission] } # Create Sign-In User Flow resource "null_resource" "create_signin_userflow" { provisioner "local-exec" { interpreter = ["bash", "-c"] command = <<EOT B2C_TENANT_ID="<你的B2C租户ID>" # 替换为实际的B2C租户ID az rest --method POST \ --url "https://graph.microsoft.com/beta/identity/b2cUserFlows" \ --headers "Content-Type=application/json" \ --body '{ "id": "B2C_1A_SignIn", "userFlowType": "signIn", "userFlowTypeVersion": 1 }' \ --tenant $B2C_TENANT_ID EOT } depends_on = [null_resource.admin_consent] } # Create Sign-Up User Flow resource "null_resource" "create_signup_userflow" { provisioner "local-exec" { interpreter = ["bash", "-c"] command = <<EOT B2C_TENANT_ID="<你的B2C租户ID>" # 替换为实际的B2C租户ID az rest --method POST \ --url "https://graph.microsoft.com/beta/identity/b2cUserFlows" \ --headers "Content-Type=application/json" \ --body '{ "id": "B2C_1A_SignUp", "userFlowType": "signUp", "userFlowTypeVersion": 1 }' \ --tenant $B2C_TENANT_ID EOT } depends_on = [null_resource.create_signin_userflow] }
额外注意事项
- 若使用Windows环境,需确保已安装Git Bash或WSL,并配置Terraform能找到bash路径;
- 替换代码中的
<你的B2C租户ID>为实际的B2C租户ID(格式如xxx.onmicrosoft.com或租户GUID); - 确保当前az cli已登录且具备B2C租户的管理员权限。
内容的提问来源于stack exchange,提问作者Ibrar Khan

