Building your Azure Policies (Azure DevOps) - Part 2
As written in Part 1, policies are used to maintain the state. The Azure portal itself contains a lot of OOTB policies, but you also want to create and deploy your own. This part will go over how to deploy custom policy definitions using Azure DevOps.
Script
The script shared in part 1 is adjusted to read out the policy definition files within a directory. This is done because you do not want to deploy just one definition per pipeline. Below the updated PowerShell script is added.
[CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateSet('Subscription', 'Managementgroup')] [string]$Scope, [Parameter(Mandatory = $true)] [string]$ScopeName, [Parameter(Mandatory = $true)] [string]$PolicyFolder, [Parameter(Mandatory = $false)] [string]$RoleIds ) $policyFiles = Get-ChildItem -Path $PolicyFolder -Recurse -Filter "*.json" foreach ($policyFile in $policyFiles) { Write-Output "Working on Policy: $($policyFile.Name)" $policyDefinitionFileContent = Get-Content -Raw -Path $PolicyFile $policyDefinitionFile = ConvertFrom-Json $policyDefinitionFileContent $policyDefinitionName = $policyDefinitionFile.properties.displayName $parameters = @{} $parameters.Add("Name", $policyDefinitionName) switch ($Scope) { "ManagementGroup" { $parameters.Add("ManagementGroupName", $ScopeName) } "Subscription" { $sub = Get-AzSubscription -SubscriptionName $ScopeName $parameters.Add("SubscriptionId", $sub.Id) } } $definition = Get-AzPolicyDefinition @parameters -ErrorAction SilentlyContinue $parameters.Add("Policy", $policyDefinitionFileContent) if ($definition) { Write-Output "Policy definition already exists, policy will be updated" } else { Write-Output "Policy does not exist" } New-AzPolicyDefinition @parameters }
Policy definitions can be saved in Azure DevOps as code. The definitions in source control can be deployed via Azure DevOps Pipelines.
Prerequisites
To be able to start deploying the definitions, we need to have the following in place:
- Service principal in Azure Active Directory
- Service Connection in Azure DevOps
- Access rights in Azure
- Pipeline
First couple of prerequisites
We will need a service principal to deploy policy definitions via Azure DevOps. Creating this can be done via the portal. When the principal is made, give that principal the correct permissions on the scope where you want to deploy the definitions. I will provide the principal access to the management group where the policies need to be deployed for this article.
Of course, we can give the principal owner permissions on the scope, but we want to stick to the least privileges. Therefore we provide the principal the 'Resource Policy Contributor' role, which is enough for deploying Azure Policies.
With the rights in place, the service connection in Azure DevOps can be configured. In Azure DevOps, create an "Azure Resource Manager" service connection and fill in the correct information regarding your platform.
Pipeline
Next up is the pipeline. For the pipeline, we will start with an empty one that we save in a GitHub repository, where we also save the policy definition files. From the empty pipeline, remove the default tasks and add an Azure PowerShell task that connects to the Service Connection we created.
Point this task to the correct script file in the repository and ensure the arguments are supplied using variables. The Yaml of the task will look like the snippet below.
- task: AzurePowerShell@5 inputs: azureSubscription: 'Root Management Group Connection' ScriptType: 'FilePath' ScriptPath: './scripts/azpolicy.ps1' ScriptArguments: '-Scope "$(scope)" -ScopeName "$(scopeName)" -PolicyFolder "$(folder)"' azurePowerShellVersion: 'LatestVersion' pwsh: true
Bringing this all together will result in a simple pipeline like below.
trigger: - main pool: vmImage: ubuntu-latest variables: - name: scope value: "ManagementGroup" - name: scopeName value: "mgName" - name: folder value: "./policies/deploy" steps: - task: AzurePowerShell@5 inputs: displayName: 'Deploy Azure Policy Definitions' azureSubscription: 'Root Management Group Connection' ScriptType: 'FilePath' ScriptPath: './scripts/azpolicy.ps1' ScriptArguments: '-Scope "$(scope)" -ScopeName "$(scopeName)" -PolicyFolder "$(folder)"' azurePowerShellVersion: 'LatestVersion' pwsh: true
Of course, this is not a production-grade solution, but it highlights how to manage your policy definitions in code and how to deploy them. In the following article, we will deploy definitions via GitHub Actions.