Many of us assume that because we call it a “CI-build” we actually have to build code as part of the continuous integration step.
Yes in 9 out of 10 use cases we might actually build code, run unit tests (hopefully) and then stage the artefacts, but CI builds are a great tool for other use cases as well.
One use case that nobody talks about is the roll out of governance and policy objects into Azure.
Azure uses JSON across the board for API requests, templates, custom roles, policies, and any other time an object needs to be described. Because JSON is universally parsable and readable we can easily implement a CI/CD flow that helps us keep our Azure-related governance objects in check and also makes it easy to keep our Azure estate up to date.
What can be done as part of the CI step?
- Make sure any JSON objects are well formed and can be read
- Use Azure PowerShell/CLI to test if the objects can be deployed (where possible)
- Verify that any required objects are present
Some objects (like tags or role assignments) do not have Azure templates. What about these?
For objects like these you can create your own template that can be read in by a PowerShell script.
RBAC assignments for example can be structured like so:
"object": "ObjectID in AzureAD",
As part of the continuous delivery script, we would then read this file and fire off Azure commands as necessary. (in a similar fashion to what happens behind the scenes if an object with a template is deployed)
Managing role assignments and role definitions in this fashion…
You will want to split definitions and assignments into two steps.
- One script should make sure that all required definitions are in place.
- You may choose to delete any definitions and assignments that are not declared in source control at this stage.
- The next script will rely on the definitions outlined in source control and make assignments based on information that is (again) stored in source control.
- Once again you could delete any assignments that are not in source control at this stage. This is up to you and your script.
The permission situation
It is recommended to run any continuous delivery scripts via a service principal. https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal
We would not usually recommend to make a service principal an “Owner” of a subscription, but instead to only give it the permissions that it needs.
Azure comes with a “User Access Administrator” role which can be used purely for role assignments. For other objects like policies or tags, the service principal should be given the appropriate role (Contributor) at the right scope. (subscription or per resource group, depending on your security requirement) Preferably, you would want to use a service principal per subscription and per role.
Some role assignment operations will trigger a Microsoft Graph query in the background. If you are running as a user account, you will usually not notice that at all. As a service principal – however – you do not automatically attain any directory read permissions via the Graph API. This will surface as a Cloud Exception and might cause strange behaviour when trying to query current role assignments.
To avoid these problems before they happen, grant any RBAC-related service principal directory read permissions in Azure AD. You need an Azure AD administrator to grant the permissions, if you are running as a standard user account. https://docs.microsoft.com/en-gb/azure/active-directory/develop/active-directory-integrating-applications#updating-an-application
This post is part of the Azure Governance series.