After publishing my posts about deploying Azure Policy and Azure Policy initiatives from ARM templates, I got a few questions about performing such deployments from Azure DevOps pipelines. Indeed, there are a few things to pay attention to. So, I will try to shed some light on them in this article.
Azure pipeline tasks
If you define your policies in ARM templates as I do, then you can include a step with the Azure Resource Manager (ARM) Template Deployment Task, which is the successor of the older Azure Resource Group Deployment Task, in your pipeline. This new version has many more deployment options, especially regarding targeting your deployments to a resource group, subscription or management group. It also supports Validate mode for your deployment, so you can check first whether your template is syntactically correct. Additionally, if you are targeting your deployments to multiple environments that require different configurations, you can override template parameters and keep your repository clean from multiple parameter files.
Recently, the Azure DevOps product team has updated the pipeline UI to support that new ARM template deployment task, but I suggest always check the Azure Pipelines Tasks repository on GitHub for recent changes and updates first.
If you have some build scripts in your project to compile and deploy Azure Policy artifacts from them in batch, then you can use well known ‘New-AzDeployment’ and ‘Test-AzDeployment’ PowerShell cmdlets for the subscription level deployments, which is usually the case for Azure Policies. Azure PowerShell tasks are at your service to run the scripts.
Pay attention to the fact that both cmdlets support input of ARM template parameters from a ‘TemplateParameterObject’ which is a simple hashtable. Using this option allows you to implement a similar override pattern for template parameters and perform conditional deployments.
For example, the following code reads a template parameter file for policy assignment, extracts the parameters into a hashtable and changes or adds additional deployment parameters based on input script parameters or its variables:
Apart from that, you can create a master (or main) template for deploying your custom policy and initiative definitions as well as their assignment from linked templates. If you are working in a private repository, you might consider creating a pipeline that copies your linked templates to a storage account during the deployment process.
As you can see, there are plenty of options to deploy Azure Policy from a pipeline, but the final choice is on you and it depends on your objectives, limitations and used build/test/deploy patterns.
Configuring permissions to deploy Azure Policy
When you try to run your pipeline that has deployment tasks for Azure Policy artifacts for the first time, you most likely will get the following error:
Authorization failed for template resource '<your_policy_name>’ of type 'Microsoft.Authorization/policyDefinitions'. The client '<GUID>’ with object id '<GUID>' does not have permission to perform action 'Microsoft.Authorization/policyDefinitions/write' at scope '/subscriptions/046a3b8b-ca72-46b7-8bd6-4a7cc5357741/providers/Microsoft.Authorization/policyDefinitions/<your_policy_name>’
That error message definitely indicates that there are some permission issues as some process cannot perform ‘write’ operation 'Microsoft.Authorization/policyDefinitions’ namespace. So, let’s sort it out piece by piece.
Firstly, the authorization has failed for a specific resource in your ARM template, and that resource type is Azure Policy. Secondly, the authorization had not succeeded for a client with a particular id, which is represented as GUID, when it tried to create a new resource in the target namespace and target scope. At the same time, if you try deploying the same ARM template manually, the deployment completes successfully provided that you are an administrator on the subscription and the template is valid.
As the deployment tasks run in the pipeline context, you might assume that there is something wrong with the permissions for the service connection to your Azure subscription which is used in your pipeline. So, let’s check what configuration we have for that service connection in your Azure DevOps project:
To get the extended information as on the screen above, you should click on ‘use the full version of the service connection dialog’ in the bottom of default connection properties windows.
Here you might notice that the service principal client ID and the GUID from the error message are the same, so our assumption about the pipeline context was right.
Next, let’s check Access control for your subscription on the Azure portal. You will see that there are a few role assignments that are named similar to your Azure DevOps projects:
When looking into the assignment properties related to your project, you also might notice the same ID:
By default, Azure DevOps grants ‘Contributor’ permissions, which are just fine for the majority of regular deployments, for the service principals used to authenticated pipelines to Azure. However, this built-in role doesn’t have permission to deal with Azure Policy resources.
Granting our service principal extensive administrative permissions in the subscription is not something that considered a good security practice, so, probably, there should be another option. Indeed, it exists – a built-in ‘Resource Policy Contributor’ role, which has the permissions we exactly need to work with Azure Policy.
You can use the following PowerShell command to assign that additional role to your service principal and re-run the pipeline:
New-AzRoleAssignment -ObjectId (Get-AzADServicePrincipal -ApplicationId <your_service_principal_client_ID>).Id -RoleDefinitionName 'Resource Policy Contributor'
Now, you shouldn’t see the permission error anymore.
I hope this information will help you to create an Azure DevOps pipeline for Azure Policy and deploy policies and policy initiatives as part of your deployment process.
If you have any questions or suggestions, go ahead and post them in the comments!
Member discussion: