Deploy custom policies with Azure Pipelines

Azure Pipelines supports continuous integration (CI) and continuous delivery (CD) to constantly and consistently test, build, and ship a code to any target. This article describes how to automate the deployment process of the Azure Active Directory B2C (Azure AD B2C) custom policies using Azure Pipelines.

Important

Managing Azure AD B2C custom policies with Azure Pipelines currently uses preview operations available on the Microsoft Graph API /beta endpoint. Use of these APIs in production applications is not supported. For more information, see the Microsoft Graph REST API beta endpoint reference.

Prerequisites

Register an application for management tasks

You use PowerShell script to deploy the Azure AD B2C policies. Before the PowerShell script can interact with the Microsoft Graph API, create an application registration in your Azure AD B2C tenant. If you haven't already done so, register a Microsoft Graph application.

For the PowerShell script to access data in MS Graph, grant the registered application the relevant application permissions. Granted the Microsoft Graph > Policy > Policy.ReadWrite.TrustFramework permission within the API Permissions of the app registration.

Configure an Azure Repo

With a Microsoft Graph application registered, you're ready to configure a repository for your policy files.

  1. Sign in to your Azure DevOps organization.
  2. Create a new project, or select an existing project.
  3. In your project, navigate to Repos, and select Files.
  4. Select an existing repository or create one.
  5. In the root directory of your repository, create a folder named B2CAssets. Add your Azure AD B2C custom policy files to the B2CAssets folder.
  6. In the root directory of your repository, create a folder named Scripts. Create a PowerShell file DeployToB2C.ps1. Paste the following PowerShell script into DeployToB2C.ps1.
  7. Commit and Push the changes.

The following script acquires an access token from Microsoft Entra ID. With the token, the script calls the MS Graph API to upload the policies in the B2CAssets folder. You can also change the content of the policy before uploading it. For example, replace the tenant-name.onmicrosoft.com with your tenant name.

[Cmdletbinding()]
Param(
    [Parameter(Mandatory = $true)][string]$ClientID,
    [Parameter(Mandatory = $true)][string]$ClientSecret,
    [Parameter(Mandatory = $true)][string]$TenantId,
    [Parameter(Mandatory = $true)][string]$Folder,
    [Parameter(Mandatory = $true)][string]$Files
)

try {
    $body = @{grant_type = "client_credentials"; scope = "https://graph.microsoft.com/.default"; client_id = $ClientID; client_secret = $ClientSecret }

    $response = Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Method Post -Body $body
    $token = $response.access_token

    $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $headers.Add("Content-Type", 'application/xml')
    $headers.Add("Authorization", 'Bearer ' + $token)

    # Get the list of files to upload
    $filesArray = $Files.Split(",")

    Foreach ($file in $filesArray) {

        $filePath = $Folder + $file.Trim()

        # Check if file exists
        $FileExists = Test-Path -Path $filePath -PathType Leaf

        if ($FileExists) {
            $policycontent = Get-Content $filePath -Encoding UTF8

            # Optional: Change the content of the policy. For example, replace the tenant-name with your tenant name.
            # $policycontent = $policycontent.Replace("your-tenant.onmicrosoft.com", "contoso.onmicrosoft.com")     
    
    
            # Get the policy name from the XML document
            $match = Select-String -InputObject $policycontent  -Pattern '(?<=\bPolicyId=")[^"]*'
    
            If ($match.matches.groups.count -ge 1) {
                $PolicyId = $match.matches.groups[0].value
    
                Write-Host "Uploading the" $PolicyId "policy..."
    
                $graphuri = 'https://graph.microsoft.com/beta/trustframework/policies/' + $PolicyId + '/$value'
                $content = [System.Text.Encoding]::UTF8.GetBytes($policycontent)
                $response = Invoke-RestMethod -Uri $graphuri -Method Put -Body $content -Headers $headers -ContentType "application/xml; charset=utf-8"
    
                Write-Host "Policy" $PolicyId "uploaded successfully."
            }
        }
        else {
            $warning = "File " + $filePath + " couldn't be not found."
            Write-Warning -Message $warning
        }
    }
}
catch {
    Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__

    $_

    $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
    $streamReader.BaseStream.Position = 0
    $streamReader.DiscardBufferedData()
    $errResp = $streamReader.ReadToEnd()
    $streamReader.Close()

    $ErrResp

    exit 1
}

exit 0

Configure Azure Pipelines

With your repository initialized and populated with your custom policy files, you're ready to set up the release pipeline. To create a pipeline, follow these steps:

  1. In your project, select Pipelines > Releases > New pipeline.
  2. Under Select a template, select Empty job, and then select Apply.
  3. Enter a Stage name, for example DeployCustomPolicies, then close the pane.
  4. Select Add an artifact, and under Source type, select Azure Repository.
    1. For the Project, select your project.
    2. Select the Source (repository) that contains the Scripts folder.
    3. Select a Default branch, for example master.
    4. Leave the Default version setting of Latest from the default branch.
    5. Enter a Source alias for the repository. For example, policyRepo.
  5. Select Add
  6. Rename the pipeline to reflect its intent. For example, Deploy Custom Policy Pipeline.
  7. Select Save to save the pipeline configuration.

Configure pipeline variables

The pipeline variables give you a convenient way to get key bits of data into various parts of the pipeline. The following variables provide information about your Azure AD B2C environment.

Name Value
clientId Application (client) ID of the application you registered earlier.
clientSecret The value of the client secret that you created earlier.
Change the variable type to secret (select the lock icon).
tenantId your-b2c-tenant.onmicrosoft.com, where your-b2c-tenant is the name of your Azure AD B2C tenant.

To add pipeline variables, follow these steps:

  1. In your pipeline, select the Variables tab.
  2. Under Pipeline variables, add the above variable with their values.
  3. Select Save to save the variables.

Add pipeline tasks

A pipeline task is a pre-packaged script that performs an action. Add a task that calls the DeployToB2C.ps1 PowerShell script.

  1. In the pipeline you created, select the Tasks tab.

  2. Select Agent job, and then select the plus sign (+) to add a task to the Agent job.

  3. Search for and select PowerShell. Don't select "Azure PowerShell," "PowerShell on target machines," or another PowerShell entry.

  4. Select newly added PowerShell Script task.

  5. Enter following values for the PowerShell Script task:

    • Task version: 2.*

    • Display name: The name of the policy that this task should upload. For example, B2C_1A_TrustFrameworkBase.

    • Type: File Path

    • Script Path: Select the ellipsis (...), navigate to the Scripts folder, and then select the DeployToB2C.ps1 file.

    • Arguments: Enter the following PowerShell script.

      -ClientID $(clientId) -ClientSecret $(clientSecret) -TenantId $(tenantId) -Folder $(System.DefaultWorkingDirectory)/policyRepo/B2CAssets/ -Files "TrustFrameworkBase.xml,TrustFrameworkLocalization.xml,TrustFrameworkExtensions.xml,SignUpOrSignin.xml,ProfileEdit.xml,PasswordReset.xml"
      

      The -Files parameter is a comma delimiter list of policy files to deploy. Update the list with your policy files.

      Important

      Ensure the policies are uploaded in the correct order. First the base policy, the extensions policy, then the relying party policies. For example, TrustFrameworkBase.xml,TrustFrameworkLocalization.xml,TrustFrameworkExtensions.xml,SignUpOrSignin.xml.

  6. Select Save to save the Agent job.

Test your pipeline

To test your release pipeline:

  1. Select Pipelines and then Releases.
  2. Select the pipeline you created earlier, for example DeployCustomPolicies.
  3. Select Create release, then select Create to queue the release.

You should see a notification banner that says that a release has been queued. To view its status, select the link in the notification banner, or select it in the list on the Releases tab.

Next steps

Learn more about: