When enterprises start their move to Azure, there is an endless list of things that need to be taken into consideration, from Security, Cost, BCDR, Identity and Access Management, Monitoring, Naming Standards, Automation, etc.
One topic that’s been the growing focus is Governance. And with it, comes a far-reaching list of elements that touch nearly everything. For example, how do you organize and manage your Azure subscriptions? How granular do you control the Identity and Access, down to the resource level? Not to mention the challenge of applying consistent naming conventions and tagging.
One of the best places to start is the Azure Governance documentation. Within this documentation, one of the best sections is the Azure enterprise scaffold: Prescriptive subscription governance area, which details a lot of these areas from an enterprise perspective.
Deep within this documentation, there is a small one-paragraph blurb about Azure Resource Locks. In essence, Resource Locks can be used to protect your high-value business impacting resources from potential modification or deletion.
Defining Resource Locks
In a Production environment, it is especially recommended to apply Resource Locks. I generally apply them to the Resource Group level.
But, in the spirit of governance, there should be a way to audit and report if any Production Resource Groups (or Resources) are missing this crucial Delete Lock.
At first, if you work with Azure Resource Manager (ARM) templates, you might expect there to be some type of property that represents this. And there is/isn’t.
There is a Resource/provider/locks component, which you can use in an ARM template at deployment time. But if you dig deeper, you’ll discover that it falls within the Microsoft.Authorization/locks resource. And even further, within the ManagementLockProperties object, there is a Level property that can be either NotSpecified, CanNotDelete, or ReadOnly.
Azure Policy
So now that we know the low-level components of the Resource Locks in ARM, how do we audit for it?
Firstly, the scenario I was trying to solve for, was to discover any Resource Group that had the tag name of “ENV” with a value of “PROD”, and audit if the Resource Group had a ‘Delete’ lock on it.
But I don’t want to hardcode the tag name or value so that the policy can be more flexible. So, let’s first look at the Parameters code block:
What this code block gives me, is the flexibility when assigning it, to define what Tag Name and corresponding Tag Value I want to use as part of the discovery. For example, perhaps I don’t want to check against “ENV” but “CostCenter”.
So now that we have flexibility, let’s look at the actual Policy Rule. In the IF block, we are saying “IF all of these conditions are true…then”. So, IF the ResourceGroups resource type has the TagName-Value pair we’ve specified in the assignment…
…THEN we will use the Audit If Not Exists policy feature, and we’ll audit if the “Microsoft.Authorization/locks/level” field value equals “CanNotDelete”.
All that is left is to assign the Policy and test it. To do that, I created 2 Resource Groups, both with the Tag of “ENV:PROD” but only applied a ‘delete’ Resource Lock on one.
After we apply our Policy to our target Subscription (so that it will audit all Resource Groups), we can see that it does successfully identify the one Resource Group as not being in compliance.
Wrap Up
To wrap this post up, I wanted to first express my appreciation to the Azure Policy team (specifically Chris Eggert), as they helped me in refining this policy to make it work.
Originally, I incorrectly had the following in the IF block, and just “effect: audit” in the THEN field.
Update
My Azure Policy is now officially a part of the Azure Policy samples GitHub repository. You can find it under:
azure-policy/samples/ResourceGroup/audit-resourceGroup-resourceLocks/