When working with Azure AD B2C Custom Policies, you are editing xml text files and uploading them to your B2C tenant. The actual uploading can be done with Microsoft Graph API, but since the B2C policy files contain data that is tenant specific, you need to manipulate them before uploading them to a specific environment. This blog will show you one way of doing it. The source code if this sample is available in this github repo https://github.com/cljung/b2c-pipeline-demo.
Making the B2C policies generic
When checking in your B2C xml text files to a source repository, you need to make them generic, ie clean them from B2C tenant specific values. Here, I’ve choosen a style with {config: …} style markup, but how you do it is up to you. It only needs to be unique for search & replace.
<?xml version="1.0" encoding="utf-8"?>
<TrustFrameworkPolicy ...
TenantId="{config:yourtenant.onmicrosoft.com}"
PolicyId="B2C_1A_{config:PolicyPrefix}TrustFrameworkExtensions"
PublicPolicyUri="http://{config:yourtenant.onmicrosoft.com}/B2C_1A_{config:PolicyPrefix}TrustFrameworkExtensions">
<BasePolicy>
<TenantId>{config:yourtenant.onmicrosoft.com}</TenantId>
<PolicyId>B2C_1A_{config:PolicyPrefix}TrustFrameworkBase</PolicyId>
</BasePolicy>
The markup keywords I use in this sample are the following:
- {config:PolicyPrefix}
- {config:yourtenant.onmicrosoft.com}
- {config:ProxyIdentityExperienceFrameworkAppId}
- {config:IdentityExperienceFrameworkAppId}”
- {config:b2c-extension-app:AppId}”
- {config:b2c-extension-app:objectId}
PolicyPrefix is a way of making the PolcyId’s unique so that you can have multiple policies in one B2C tenant without overwriting them. For instance, it makes B2C_1A_signup_signin show up like B2C_1A_ABC_signup_signin. The other one to call out are the two b2c-extension-apps, since they are the way to bind the policy to which application extension attributes are bound to (read more in the docs). In by sample github repo, you can view my B2C generic policies.
Adapting generic B2C policies to a tenant
In the Azure DevOps build pipeline, a major task is to update the B2C xml text files and take them from a generic state into targeting an environment. So, in essence, the build pipeline is a search & replace task. To do this, I have created a powershell script named prepare-b2c-policies.ps1 that gets some B2C tenant specific details from the B2C tenant via Microsoft Graph API and then also lets you add your dictionary with words to replace in the file.
The updated B2C policy files are then copied and uploaded as the published build artifacts. For compiled code, this would be copying the EXEs and DLLs and publishing them as output of the build, but for B2C we are just publishing the updated xml text files – together with the scripts used to deploy them to B2C (that’s why you have two copy steps).
The script prepare-b2c-policies.ps1 uses client credentials to query values needed for the apps IdentityExperienceFramework and ProxyIdentityExperienceFramework and for that reason you need to register an app in the B2C tenant, create a secret and give it permission Application.Read.All.
For the build pipeline, the following variables are defined in Azure DevOps:
- B2CTenantName – ie yourtenant.onmicrosoft.com
- B2CPolicyPrefix – ie the “ABC” in B2C_1A_ABC_…
- IdentityExperienceFrameworkAppName – name of that app
- ProxyIdentityExperienceFrameworkAppName – name of that app
- ClientCredAppID – the AppID guid to use for client credentials
- ClientCredAppKey – the client secret for the client credentials
- AppInsightInstrumentationKey – AppInsights InstrumentationKey (guid)
- FacebookClientId – facebook client_id (enter dummy 1234567 just to make the sample work)
Deploying the adapted B2C policies to a tenant
In the Azure DevOps release pipeline, the task is twofold; 1) figure out which order to deploy the policy files (base file first, etc) and 2) deploy them. The script file deploy-to-b2c.ps1 does this and part of solving task 1) is done via figuring out the inheritance chain of the policies. Files that don’t inherit from others (have no BasePolicyId) are considered to be roots and uploading starts with those files and then traversing their children, etc.
The actual uploading of a B2C policy is a Microsoft Graph API http PUT call with the XML as the http body. In order to do so, the app used for client credentials needs to have the permission Policy.ReadWrite.TrustFramework
This means that the release pipeline is also just a powershell step, this time uploading the xml text files to the B2C tenant.
Summary
This blog post described the concepts of storing generic B2C xml policy files in the source repo and using build and release pipelines to adapt them to the target environment and then deploy them. However, this is only intended to show you the basics and give you a working sample that you can play with. There is more work to do with Azure DevOps, like having more release pipelines and deploy to test, Q&A and production, etc, but that has less to do with B2C than normal Azure DevOps configuration.