Unable to deploy custom Policies in Azure B2C tenant

Pawan Modi 0 Reputation points
2025-04-01T00:27:21.3966667+00:00

#azure-ad-b2c

Hi All,

I have created a powershell script that I'm using in the release pipeline to do the custom policies deployment. The script is as below -

param (
    [string]$b2cTenant,
    [string]$clientId,
    [string]$clientSecret,
    [string]$repoPath
)

# Function to deploy policy using Microsoft Graph API
function Deploy-Policy {
    param (
        [string]$PolicyPath,
        [string]$AccessToken
    )

    $policyName = [System.IO.Path]::GetFileNameWithoutExtension($PolicyPath)
    Write-Host "Uploading policy: $policyName"

    # Microsoft Graph API URL for custom policy upload
    $url = "https://graph.microsoft.com/beta/trustFramework/policies/$policyName/`$value"

    # Read XML file content
    try {
        $xmlContent = Get-Content -Path $PolicyPath -Raw
    } catch {
        Write-Host "Failed to read policy file: $PolicyPath"
        Write-Host "Error: $_"
        return
    }

    # Set HTTP headers
    $headers = @{
        Authorization  = "Bearer $AccessToken"
        "Content-Type" = "application/xml"
    }

    # Upload policy using PUT request
    try {
        Invoke-RestMethod -Uri $url -Headers $headers -Method PUT -Body $xmlContent
        Write-Host "Successfully deployed policy: $policyName"
    } catch {
        Write-Host "Failed to deploy policy: $policyName"
        Write-Host "Error: $_"
    }
}



# Debug: Output the received variables for validation
Write-Host "B2C_TENANT_NAME: $b2cTenant"
Write-Host "CLIENT_ID: $clientId"
Write-Host "CLIENT_SECRET: $clientSecret"
Write-Host "Repository Path: $repoPath"

# Ensure B2C_TENANT_NAME is correct (adding .onmicrosoft.com suffix)
$tenantDomain = "$b2cTenant"
Write-Host "Tenant Domain: $tenantDomain"

# Ensure Az Module is available
Write-Host "Checking Az PowerShell Module..."
$AzModule = Get-Module -ListAvailable Az
if (-not $AzModule) {
    Write-Host "Az module not found. Installing Az module..."
    Install-Module -Name Az -Scope CurrentUser -Force -AllowClobber
    if ($?) {
        Write-Host "Az module installed successfully."
    } else {
        Write-Host "Failed to install Az module."
        exit 1
    }
}

# Import Az module to the session
Import-Module Az -Global

# Authentication to Azure using Service Principal (Ensure this is correct)
Write-Host "Authenticating to Azure AD..."

# Login to Azure using Service Principal and Secret
$securePassword = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential($clientId, $securePassword)
try {
Connect-AzAccount -ServicePrincipal -Credential $psCredential -TenantId $tenantDomain
Write-Host "Service Principal Login is successful"
} catch {
    Write-Host "❌ Service Principal Login failed."
    Write-Host "Error: $_"
    exit 1
}

##Connect-AzAccount -ServicePrincipal -Tenant $tenantDomain -ApplicationId $clientId -Credential $clientSecret

# Define the path to your custom policies
$basePoliciesPath = "$repoPath\Base"
$customPoliciesPath = "$repoPath\Custom"

# Check if base and custom policy directories exist
if (!(Test-Path $basePoliciesPath)) {
    Write-Host "Error: Base policies folder does not exist at: $basePoliciesPath"
    exit 1
}
if (!(Test-Path $customPoliciesPath)) {
    Write-Host "Error: Custom policies folder does not exist at: $customPoliciesPath"
    exit 1
}

# Authenticate to Azure AD & Fetch Access Token
Write-Host "Fetching Access Token..."
try {
$tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$b2cTenant/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body @{
    client_id     = $clientId
    client_secret = $clientSecret
    scope        = "https://graph.microsoft.com/.default"
    grant_type   = "client_credentials"
}
$accessToken = $tokenResponse.access_token
if (-not $accessToken) {
    Write-Host "Error: Failed to obtain access token."
    exit 1
}
Write-Host "Access Token Retrieved Successfully."
} catch {
    Write-Host "❌ Error while fetching access token."
    Write-Host "Error: $_"
    exit 1
}


# Define base policy files in required order
$basePolicies = @(
    "TrustFrameworkBase.xml",
    "TrustFrameworkLocalization.xml",
    "TrustFrameworkExtensions.xml"
)

# Deploy Base Policies in sequence
Write-Host "Deploying Base Policies..."
foreach ($policy in $basePolicies) {
    $policyPath = "$basePoliciesPath\$policy"
    if (Test-Path $policyPath) {
        Write-Host "Deploying Base Policy: $policy"
        Deploy-Policy -PolicyPath $policyPath -AccessToken $accessToken
    } else {
        Write-Host "Warning: Base policy '$policy' not found, skipping."
    }
}

# Track changes for custom policies
$trackingFile = "$customPoliciesPath\deployedPolicies.json"
$previouslyDeployed = @{}
if (Test-Path $trackingFile) {
    $previouslyDeployed = Get-Content -Raw -Path $trackingFile | ConvertFrom-Json
}

Write-Host "Deploying Custom Policies..."
$customPolicies = Get-ChildItem -Path $customPoliciesPath -Filter "*.xml"
$deployedPolicies = @{}

foreach ($policy in $customPolicies) {
    $policyName = $policy.Name
    $policyPath = "$customPoliciesPath\$policyName"
    
    # Compute file hash to detect changes
try {    
    $hash = Get-FileHash -Path $policyPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash
} catch {
        Write-Host "Error: Failed to calculate hash for policy: $policyName"
        Write-Host "Error: $_"
        continue
    }
    # Deploy only if new or modified
    if (-not $previouslyDeployed.ContainsKey($policyName) -or $previouslyDeployed[$policyName] -ne $hash) {
        Write-Host "Deploying Custom Policy: $policyName (New/Updated)"
        Deploy-Policy -PolicyPath $policyPath -AccessToken $accessToken
        $deployedPolicies[$policyName] = $hash
    } else {
        Write-Host "Skipping: $policyName (No changes detected)"
    }
}

# Save deployed policies state
$deployedPolicies | ConvertTo-Json | Set-Content -Path $trackingFile

Write-Host "Azure B2C policy deployment completed successfully."

However, when I am executing the pipeline I'm getting below error message -

2025-04-01T00:08:58.4338769Z Checking Az PowerShell Module...
2025-04-01T00:08:58.4695442Z Az module not found. Installing Az module...
2025-04-01T00:11:51.0404314Z Az module installed successfully.
2025-04-01T00:12:24.7101821Z WARNING: The names of some imported commands from the module 'Az.DevCenterdata.custom' include unapproved verbs that 
2025-04-01T00:12:24.7102756Z might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with
2025-04-01T00:12:24.7103555Z  the Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:12:25.0317150Z WARNING: The names of some imported commands from the module 'Az.DevCenter' include unapproved verbs that might make 
2025-04-01T00:12:25.0317920Z them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the 
2025-04-01T00:12:25.0318644Z Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:32.5256509Z WARNING: The names of some imported commands from the module 'Microsoft.Azure.PowerShell.Cmdlets.Network' include 
2025-04-01T00:13:32.5260642Z unapproved verbs that might make them less discoverable. To find the commands with unapproved verbs, run the 
2025-04-01T00:13:32.5261556Z Import-Module command again with the Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:33.1477638Z WARNING: The names of some imported commands from the module 'Az.Network' include unapproved verbs that might make them
2025-04-01T00:13:33.1478573Z  less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the Verbose 
2025-04-01T00:13:33.1478834Z parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:34.5479832Z WARNING: The names of some imported commands from the module 'Az.NetworkCloud.private' include unapproved verbs that 
2025-04-01T00:13:34.5500617Z might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with
2025-04-01T00:13:34.5525199Z  the Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:34.5868581Z WARNING: The names of some imported commands from the module 'Az.NetworkCloud.private' include unapproved verbs that 
2025-04-01T00:13:34.5869175Z might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with
2025-04-01T00:13:34.5906439Z  the Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:34.6062144Z WARNING: The names of some imported commands from the module 'Az.NetworkCloud.private' include unapproved verbs that 
2025-04-01T00:13:34.6063251Z might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with
2025-04-01T00:13:34.6107418Z  the Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:13:37.3030557Z WARNING: The names of some imported commands from the module 'Az.NetworkCloud' include unapproved verbs that might make
2025-04-01T00:13:37.3031024Z  them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the 
2025-04-01T00:13:37.3076581Z Verbose parameter. For a list of approved verbs, type Get-Verb.
2025-04-01T00:15:51.9247607Z Authenticating to Azure AD...
2025-04-01T00:15:53.8892439Z 
2025-04-01T00:15:54.0597949Z Service Principal Login is successful
2025-04-01T00:15:54.0613405Z Fetching Access Token...
2025-04-01T00:15:54.2606203Z Access Token Retrieved Successfully.
2025-04-01T00:15:54.2627127Z Deploying Base Policies...
2025-04-01T00:15:54.2670202Z Deploying Base Policy: TrustFrameworkBase.xml
2025-04-01T00:15:54.2815028Z Uploading policy: TrustFrameworkBase
2025-04-01T00:15:54.6743958Z Failed to deploy policy: TrustFrameworkBase
2025-04-01T00:15:54.6769211Z Error: The remote server returned an error: (400) Bad Request.
2025-04-01T00:15:54.6844004Z Deploying Base Policy: TrustFrameworkLocalization.xml
2025-04-01T00:15:54.6878298Z Uploading policy: TrustFrameworkLocalization
2025-04-01T00:15:54.7732254Z Failed to deploy policy: TrustFrameworkLocalization
2025-04-01T00:15:54.7769176Z Error: The remote server returned an error: (400) Bad Request.
2025-04-01T00:15:54.7793920Z Deploying Base Policy: TrustFrameworkExtensions.xml
2025-04-01T00:15:54.7886487Z Uploading policy: TrustFrameworkExtensions
2025-04-01T00:15:54.8916777Z Failed to deploy policy: TrustFrameworkExtensions
2025-04-01T00:15:54.8942658Z Error: The remote server returned an error: (400) Bad Request.
2025-04-01T00:15:54.9641256Z Deploying Custom Policies...
2025-04-01T00:15:54.9913974Z Deploying Custom Policy: PasswordReset.xml (New/Updated)
2025-04-01T00:15:54.9985185Z Uploading policy: PasswordReset
2025-04-01T00:15:55.0902182Z Failed to deploy policy: PasswordReset
2025-04-01T00:15:55.1012247Z Error: The remote server returned an error: (400) Bad Request.
2025-04-01T00:15:55.1034452Z Deploying Custom Policy: SignUpOrSignin.xml (New/Updated)
2025-04-01T00:15:55.1170474Z Uploading policy: SignUpOrSignin
2025-04-01T00:15:55.1711556Z Failed to deploy policy: SignUpOrSignin
2025-04-01T00:15:55.1822532Z Error: The remote server returned an error: (400) Bad Request.
2025-04-01T00:15:55.2597288Z Azure B2C policy deployment completed successfully.
2025-04-01T00:15:55.2757613Z Subscription name Tenant                              
2025-04-01T00:15:55.2758408Z ----------------- ------                              
2025-04-01T00:15:55.2759048Z                   xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
2025-04-01T00:15:55.2759306Z 
2025-04-01T00:15:55.2759565Z 

Note: I have created the Service Principal in Azure B2C and have given all the required permissions as -

offline_accessDelegatedMaintain access to data you have given it access toNoGranted for modioffline_accessDelegatedMaintain access to data you have given it access toNoGranted for modiopenidDelegatedSign users inNoGranted for modiPolicy.ReadWrite.IdentityProtectionApplicationRead and write your organization’s identity protection policyYesGranted for modiPolicy.ReadWrite.TrustFrameworkDelegatedRead and write your organization's trust framework policiesYesGranted for modiPolicy.ReadWrite.TrustFrameworkApplicationRead and write your organization's trust framework policiesYesGranted for modiUser.ReadWrite.AllApplicationRead and write all users' full profilesYesGranted for modiKindly suggest what should I do to fix this error message?

Any help is much appreciated,

Thank you,

Regards,

Microsoft Entra External ID
Microsoft Entra External ID
A modern identity solution for securing access to customer, citizen and partner-facing apps and services. It is the converged platform of Azure AD External Identities B2B and B2C. Replaces Azure Active Directory External Identities.
3,142 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Kancharla Saiteja 3,405 Reputation points Microsoft External Staff
    2025-04-02T20:19:11.1833333+00:00

    Hi @Pawan Modi,

    Based on your query, here is my understanding: you have created a PowerShell script to deploy the custom policies.

    Azure AD B2C have a very limited services in terms of automating the deployment of custom policies. Azure B2C is relayed on a PowerShell script which retrieves an access token, with that token Microsoft Graph API perform the calls to automate the deployment.

    Here is the Microsoft document which consists of PowerShell script to retrieve a token: Configure an Azure Repo.

    By using Azure pipelines configuration using this document and would help in achieving your end goal.

    Note: We do not support any customized scripts in deploying the custom policies for Azure. The above information shared is to retrieve an access token using PowerShell and use it to perform Graph API calls to deploy the policies with the help of the configuration available in the provided documents.

    Additional information:

    Manage policies using Microsoft Graph API: Manage Azure AD B2C with Microsoft Graph

    Manage using PowerShell: Manage Azure AD B2C custom policies with Microsoft Graph PowerShell

    I hope this information is helpful. Please feel free to reach out if you have any further questions.

    If the answer is helpful, please click "Accept Answer" and kindly "upvote it". If you have extra questions about this answer, please click "Comment".


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.