|
| 1 | +--- |
| 2 | +title: Deploy custom policies with Azure Pipelines |
| 3 | +titleSuffix: Azure AD B2C |
| 4 | +description: Learn how to deploy Azure AD B2C custom policies in a CI/CD pipeline by using Azure Pipelines in Azure DevOps Services. |
| 5 | +services: active-directory-b2c |
| 6 | +author: mmacy |
| 7 | +manager: celestedg |
| 8 | + |
| 9 | +ms.service: active-directory |
| 10 | +ms.workload: identity |
| 11 | +ms.topic: conceptual |
| 12 | +ms.date: 02/14/2020 |
| 13 | +ms.author: marsma |
| 14 | +ms.subservice: B2C |
| 15 | +--- |
| 16 | + |
| 17 | +# Deploy custom policies with Azure Pipelines |
| 18 | + |
| 19 | +By using a continuous integration and delivery (CI/CD) pipeline that you set up in [Azure Pipelines][devops-pipelines], you can include your Azure AD B2C custom policies in your software delivery and code control automation. As you deploy to different Azure AD B2C environments, for example dev, test, and production, we recommend that you remove manual processes and perform automated testing by using Azure Pipelines. |
| 20 | + |
| 21 | +There are three primary steps required for enabling Azure Pipelines to manage custom policies within Azure AD B2C: |
| 22 | + |
| 23 | +1. Create a web application registration in your Azure AD B2C tenant |
| 24 | +1. Configure an Azure Repo |
| 25 | +1. Configure an Azure Pipeline |
| 26 | + |
| 27 | +> [!IMPORTANT] |
| 28 | +> Managing Azure AD B2C custom policies with an Azure Pipeline 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](https://docs.microsoft.com/graph/api/overview?toc=./ref/toc.json&view=graph-rest-beta). |
| 29 | +
|
| 30 | +## Prerequisites |
| 31 | + |
| 32 | +* [Azure AD B2C tenant](tutorial-create-tenant.md), and credentials for a user in the directory with the [B2C IEF Policy Administrator](../active-directory/users-groups-roles/directory-assign-admin-roles.md#b2c-ief-policy-administrator) role |
| 33 | +* [Custom policies](custom-policy-get-started.md) uploaded to your tenant |
| 34 | +* [Azure Pipeline](https://azure.microsoft.com/services/devops/pipelines/), and access to an [Azure DevOps Services project][devops-create-project] |
| 35 | + |
| 36 | +## Client credentials grant flow |
| 37 | + |
| 38 | +The scenario described here makes use of service-to-service calls between Azure Pipelines and Azure AD B2C by using the OAuth 2.0 [client credentials grant flow](../active-directory/develop/v1-oauth2-client-creds-grant-flow.md). This grant flow permits a web service like Azure Pipelines (the confidential client) to use its own credentials instead of impersonating a user to authenticate when calling another web service (the Microsoft Graph API, in this case). Azure Pipelines obtains a token non-interactively, then makes requests to the Microsoft Graph API. |
| 39 | + |
| 40 | +## Register an application for management tasks |
| 41 | + |
| 42 | +Start by creating an application registration that your PowerShell scripts executed by Azure Pipelines will use to communicate with Azure AD B2C. If you already have an application registration that you use for automation tasks, you can skip to the [Grant permissions](#grant-permissions) section. |
| 43 | + |
| 44 | +### Register application |
| 45 | + |
| 46 | +[!INCLUDE [active-directory-b2c-appreg-mgmt](../../includes/active-directory-b2c-appreg-mgmt.md)] |
| 47 | + |
| 48 | +### Grant permissions |
| 49 | + |
| 50 | +Next, grant the application permission to use the Microsoft Graph API to read and write custom policies in your Azure AD B2C tenant. |
| 51 | + |
| 52 | +#### [Applications](#tab/applications/) |
| 53 | + |
| 54 | +1. On the **Registered app** overview page, select **Settings**. |
| 55 | +1. Under **API Access**, select **Required permissions**. |
| 56 | +1. Select **Add**, then **Select an API**. |
| 57 | +1. Select **Microsoft Graph**, then **Select**. |
| 58 | +1. Under **Application Permissions**, select **Read and write your organization's trust framework policies**. |
| 59 | +1. Select **Select**, then **Done**. |
| 60 | +1. Select **Grant permissions**, and then select **Yes**. It might take a few minutes to for the permissions to fully propagate. |
| 61 | + |
| 62 | +#### [App registrations (Preview)](#tab/app-reg-preview/) |
| 63 | + |
| 64 | +1. Select **App registrations (Preview)**, and then select the web application that should have access to the Microsoft Graph API. For example, *managementapp1*. |
| 65 | +1. Under **Manage**, select **API permissions**. |
| 66 | +1. Under **Configured permissions**, select **Add a permission**. |
| 67 | +1. Select the **Microsoft APIs** tab, then select **Microsoft Graph**. |
| 68 | +1. Select **Application permissions**. |
| 69 | +1. Expand **Policy** and select **Policy.ReadWrite.TrustFramework**. |
| 70 | +1. Select **Add permissions**. As directed, wait a few minutes before proceeding to the next step. |
| 71 | +1. Select **Grant admin consent for (your tenant name)**. |
| 72 | +1. Select your currently signed-in administrator account, or sign in with an account in your Azure AD B2C tenant that's been assigned at least the *Cloud application administrator* role. |
| 73 | +1. Select **Accept**. |
| 74 | +1. Select **Refresh**, and then verify that "Granted for ..." appears under **Status**. It might take a few minutes for the permissions to propagate. |
| 75 | + |
| 76 | +* * * |
| 77 | + |
| 78 | +### Create client secret |
| 79 | + |
| 80 | +To authenticate with Azure AD B2C, your PowerShell script needs to specify a client secret that you create for the application. |
| 81 | + |
| 82 | +[!INCLUDE [active-directory-b2c-client-secret](../../includes/active-directory-b2c-client-secret.md)] |
| 83 | + |
| 84 | +## Configure an Azure Repo |
| 85 | + |
| 86 | +With a management application registered, you're ready to configure a repository for your policy files. |
| 87 | + |
| 88 | +1. Sign in to your Azure DevOps Services organization. |
| 89 | +1. [Create a new project][devops-create-project] or select an existing project. |
| 90 | +1. In your project, navigate to **Repos** and select the **Files** page. Select an existing repository or create one for this exercise. |
| 91 | +1. Create a folder named *B2CAssets*. Name the required placeholder file *README.md* and **Commit** the file. You can remove this file later, if you like. |
| 92 | +1. Add your Azure AD B2C policy files to the *B2CAssets* folder. This includes the *TrustFrameworkBase.xml*, *TrustFrameWorkExtensions.xml*, *SignUpOrSignin.xml*, *ProfileEdit.xml*, *PasswordReset.xml*, and any other policies you've created. Record the filename of each Azure AD B2C policy file for use in a later step (they're used as PowerShell script arguments). |
| 93 | +1. Create a folder named *Scripts* in the root directory of the repository, name the placeholder file *DeployToB2c.ps1*. Don't commit the file at this point, you'll do so in a later step. |
| 94 | +1. Paste the following PowerShell script into *DeployToB2c.ps1*, then **Commit** the file. The script acquires a token from Azure AD and calls the Microsoft Graph API to upload the policies within the *B2CAssets* folder to your Azure AD B2C tenant. |
| 95 | + |
| 96 | + ```PowerShell |
| 97 | + [Cmdletbinding()] |
| 98 | + Param( |
| 99 | + [Parameter(Mandatory = $true)][string]$ClientID, |
| 100 | + [Parameter(Mandatory = $true)][string]$ClientSecret, |
| 101 | + [Parameter(Mandatory = $true)][string]$TenantId, |
| 102 | + [Parameter(Mandatory = $true)][string]$PolicyId, |
| 103 | + [Parameter(Mandatory = $true)][string]$PathToFile |
| 104 | + ) |
| 105 | +
|
| 106 | + try { |
| 107 | + $body = @{grant_type = "client_credentials"; scope = "https://graph.microsoft.com/.default"; client_id = $ClientID; client_secret = $ClientSecret } |
| 108 | +
|
| 109 | + $response = Invoke-RestMethod -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Method Post -Body $body |
| 110 | + $token = $response.access_token |
| 111 | +
|
| 112 | + $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" |
| 113 | + $headers.Add("Content-Type", 'application/xml') |
| 114 | + $headers.Add("Authorization", 'Bearer ' + $token) |
| 115 | +
|
| 116 | + $graphuri = 'https://graph.microsoft.com/beta/trustframework/policies/' + $PolicyId + '/$value' |
| 117 | + $policycontent = Get-Content $PathToFile |
| 118 | + $response = Invoke-RestMethod -Uri $graphuri -Method Put -Body $policycontent -Headers $headers |
| 119 | +
|
| 120 | + Write-Host "Policy" $PolicyId "uploaded successfully." |
| 121 | + } |
| 122 | + catch { |
| 123 | + Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ |
| 124 | +
|
| 125 | + $_ |
| 126 | +
|
| 127 | + $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream()) |
| 128 | + $streamReader.BaseStream.Position = 0 |
| 129 | + $streamReader.DiscardBufferedData() |
| 130 | + $errResp = $streamReader.ReadToEnd() |
| 131 | + $streamReader.Close() |
| 132 | +
|
| 133 | + $ErrResp |
| 134 | +
|
| 135 | + exit 1 |
| 136 | + } |
| 137 | +
|
| 138 | + exit 0 |
| 139 | + ``` |
| 140 | +
|
| 141 | +## Configure your Azure pipeline |
| 142 | +
|
| 143 | +With your repository initialized and populated with your custom policy files, you're ready to set up the release pipeline. |
| 144 | +
|
| 145 | +### Create pipeline |
| 146 | +
|
| 147 | +1. Sign in to your Azure DevOps Services organization and navigate to your project. |
| 148 | +1. In your project, select **Pipelines** > **Releases** > **New pipeline**. |
| 149 | +1. Under **Select a template**, select **Empty job**. |
| 150 | +1. Enter a **Stage name**, for example *DeployCustomPolicies*, then close the pane. |
| 151 | +1. Select **Add an artifact**, and under **Source type**, select **Azure Repository**. |
| 152 | + 1. Choose the source repository containing the *Scripts* folder that you populated with the PowerShell script. |
| 153 | + 1. Choose a **Default branch**. If you created a new repository in the previous section, the default branch is *master*. |
| 154 | + 1. Leave the **Default version** setting of *Latest from the default branch*. |
| 155 | + 1. Enter a **Source alias** for the repository. For example, *policyRepo*. Do not include any spaces in the alias name. |
| 156 | +1. Select **Add** |
| 157 | +1. Rename the pipeline to reflect its intent. For example, *Deploy Custom Policy Pipeline*. |
| 158 | +1. Select **Save** to save the pipeline configuration. |
| 159 | +
|
| 160 | +### Configure pipeline variables |
| 161 | +
|
| 162 | +1. Select the **Variables** tab. |
| 163 | +1. Add the following variables under **Pipeline variables** and set their values as specified: |
| 164 | +
|
| 165 | + | Name | Value | |
| 166 | + | ---- | ----- | |
| 167 | + | `clientId` | **Application (client) ID** of the application you registered earlier. | |
| 168 | + | `clientSecret` | The value of the **client secret** that you created earlier. <br /> Change the variable type to **secret** (select the lock icon). | |
| 169 | + | `tenantId` | `your-b2c-tenant.onmicrosoft.com`, where *your-b2c-tenant* is the name of your Azure AD B2C tenant. | |
| 170 | +
|
| 171 | +1. Select **Save** to save the variables. |
| 172 | +
|
| 173 | +### Add pipeline tasks |
| 174 | +
|
| 175 | +Next, add a task to deploy a policy file. |
| 176 | +
|
| 177 | +1. Select the **Tasks** tab. |
| 178 | +1. Select **Agent job**, and then select the plus sign (**+**) to add a task to the Agent job. |
| 179 | +1. Search for and select **PowerShell**. Do not select "Azure PowerShell," "PowerShell on target machines," or another PowerShell entry. |
| 180 | +1. Select newly added **PowerShell Script** task. |
| 181 | +1. Enter following values for the PowerShell Script task: |
| 182 | + * **Task version**: 2.* |
| 183 | + * **Display name**: The name of the policy that this task should upload. For example, *B2C_1A_TrustFrameworkBase*. |
| 184 | + * **Type**: File Path |
| 185 | + * **Script Path**: Select the ellipsis (***...***), navigate to the *Scripts* folder, and then select the *DeployToB2C.ps1* file. |
| 186 | + * **Arguments:** |
| 187 | +
|
| 188 | + Enter the following values for **Arguments**. Replace `{alias-name}` with the alias you specified in the previous section. |
| 189 | +
|
| 190 | + ```PowerShell |
| 191 | + # Before |
| 192 | + -ClientID $(clientId) -ClientSecret $(clientSecret) -TenantId $(tenantId) -PolicyId B2C_1A_TrustFrameworkBase -PathToFile $(System.DefaultWorkingDirectory)/{alias-name}/B2CAssets/TrustFrameworkBase.xml |
| 193 | + ``` |
| 194 | +
|
| 195 | + For example, if the alias you specified is *policyRepo*, the argument line should be: |
| 196 | +
|
| 197 | + ```PowerShell |
| 198 | + # After |
| 199 | + -ClientID $(clientId) -ClientSecret $(clientSecret) -TenantId $(tenantId) -PolicyId B2C_1A_TrustFrameworkBase -PathToFile $(System.DefaultWorkingDirectory)/contosob2cpolicies/B2CAssets/TrustFrameworkBase.xml |
| 200 | + ``` |
| 201 | +
|
| 202 | +1. Select **Save** to save the Agent job. |
| 203 | +
|
| 204 | +The task you just added uploads *one* policy file to Azure AD B2C. Before proceeding, manually trigger the job (**Create release**) to ensure that it completes successfully before creating additional tasks. |
| 205 | +
|
| 206 | +If the task completes successfully, add deployment tasks by performing the preceding steps for each of the custom policy files. Modify the `-PolicyId` and `-PathToFile` argument values for each policy. |
| 207 | +
|
| 208 | +The `PolicyId` is a value found at the start of an XML policy file within the TrustFrameworkPolicy node. For example, the `PolicyId` in the following policy XML is *B2C_1A_TrustFrameworkBase*: |
| 209 | +
|
| 210 | +```XML |
| 211 | +<TrustFrameworkPolicy |
| 212 | +xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| 213 | +xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
| 214 | +xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" |
| 215 | +PolicySchemaVersion="0.3.0.0" |
| 216 | +TenantId="contoso.onmicrosoft.com" |
| 217 | +PolicyId= "B2C_1A_TrustFrameworkBase" |
| 218 | +PublicPolicyUri="http://contoso.onmicrosoft.com/B2C_1A_TrustFrameworkBase"> |
| 219 | +``` |
| 220 | + |
| 221 | +When running the agents and uploading the policy files, ensure they're uploaded in this order: |
| 222 | + |
| 223 | +1. *TrustFrameworkBase.xml* |
| 224 | +1. *TrustFrameworkExtensions.xml* |
| 225 | +1. *SignUpOrSignin.xml* |
| 226 | +1. *ProfileEdit.xml* |
| 227 | +1. *PasswordReset.xml* |
| 228 | + |
| 229 | +The Identity Experience Framework enforces this order as the file structure is built on a hierarchical chain. |
| 230 | + |
| 231 | +## Test your pipeline |
| 232 | + |
| 233 | +To test your release pipeline: |
| 234 | + |
| 235 | +1. Select **Pipelines** and then **Releases**. |
| 236 | +1. Select the pipeline you created earlier, for example *DeployCustomPolicies*. |
| 237 | +1. Select **Create release**, then select **Create** to queue the release. |
| 238 | + |
| 239 | +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. |
| 240 | + |
| 241 | +## Next steps |
| 242 | + |
| 243 | +Learn more about: |
| 244 | + |
| 245 | +* [Service-to-service calls using client credentials](https://docs.microsoft.com/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow) |
| 246 | +* [Azure DevOps Services](https://docs.microsoft.com/azure/devops/user-guide/?view=azure-devops) |
| 247 | + |
| 248 | +<!-- LINKS - External --> |
| 249 | +[devops]: https://docs.microsoft.com/azure/devops/?view=azure-devops |
| 250 | +[devops-create-project]: https://docs.microsoft.com/azure/devops/organizations/projects/create-project?view=azure-devops |
| 251 | +[devops-pipelines]: https://docs.microsoft.com/azure/devops/pipelines |
0 commit comments