If you are familiar with Microsoft Azure, you may have heard the term “resource locks” before. Resource locks allow you to apply a lock to a specific resource, resource group or subscription.
Typically resource locks are applied at the resource group level or resource level. The most common scenario is to create a resource lock to avoid accidental deletion of resources.
In this post we’ll cover the automated creation of resource locks for specific resources using Azure Policy.
What Is It?
I covered Azure Policy in a previous post, if you are unfamiliar with it - check it out here.
We will be expanding on Azure Policy, touching on deployIfNotExists policy type. In simplistic terms deployIfNotExists policies can deploy a resource when a certain condition is met.
How It Works
Our policies check for all recovery services vaults and all key vaults in our subscription, it then determines if they have a resource lock. If they do not have a resource lock, they are flagged as being non compliant and require remediation.
deployIfNotExists policies use a managed identity to handle the deployment of the resource, in this case our resource is a resource lock. In our example we use a system assigned managed identity, which means Azure will handle the lifecycle of the managed identity.
The managed identity in our deployment requires the following RBAC permissions:
- Backup Operator
- /providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324
- User Access Administrator
- /providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9
The quickest way to look up the role definition IDs is to use Microsoft’s website or the Get-AzRoleDefinition cmdlet in PowerShell.
(Get-AzRoleDefinition | Where Name -in "Backup Operator","User Access Administrator")
After deployment you can verify the Managed Identity and it’s role assignment by going to Subscriptions > Access control (IAM).
Policy Deployment
There are many use cases for deployIfNotExists policies. In our scenario we will deploy a resource lock when there is not one currently for recovery services vaults and key vaults within our subscription.
ARM Template
You can source this template from my GitHub.
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Authorization/policyDefinitions",
"name": "policyDefinition-rvault-lock",
"apiVersion": "2019-09-01",
"properties": {
"displayName": "Deploy resource lock on Recovery services vaults",
"policyType": "Custom",
"mode": "All",
"description": "This policy will enforce Recovery services vault locks.",
"metadata": {
"category": "Backup"
},
"parameters": {},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.RecoveryServices/vaults"
}
]
},
"then": {
"effect": "deployIfNotExists",
"details": {
"type": "Microsoft.Authorization/locks",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324",
"/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9"
],
"existenceCondition": {
"field": "Microsoft.Authorization/locks/level",
"equals": "CanNotDelete"
},
"deployment": {
"properties": {
"mode": "Incremental",
"parameters": {
"recoveryVaultName": {
"value": "[[field('name')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"recoveryVaultName": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.RecoveryServices/vaults/providers/locks",
"apiVersion": "2016-09-01",
"name": "[[concat(parameters('recoveryVaultName'), '/Microsoft.Authorization/vaultLock')]",
"properties": {
"level": "CanNotDelete",
"notes": "Recovery services vault lock applied by Azure Policy"
}
}]
}
}
}
}
}
}
}
},
{
"type": "Microsoft.Authorization/policyDefinitions",
"name": "policyDefinition-kvault-lock",
"apiVersion": "2019-09-01",
"properties": {
"displayName": "Deploy resource lock on Key vaults",
"policyType": "Custom",
"mode": "All",
"description": "This policy will enforce Key vault locks.",
"metadata": {
"category": "Key Vault"
},
"parameters": {},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.KeyVault/vaults"
}
]
},
"then": {
"effect": "deployIfNotExists",
"details": {
"type": "Microsoft.Authorization/locks",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/00c29273-979b-4161-815c-10b084fb9324",
"/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9"
],
"existenceCondition": {
"field": "Microsoft.Authorization/locks/level",
"equals": "CanNotDelete"
},
"deployment": {
"properties": {
"mode": "Incremental",
"parameters": {
"recoveryVaultName": {
"value": "[[field('name')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"recoveryVaultName": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.KeyVault/vaults/providers/locks",
"apiVersion": "2016-09-01",
"name": "[[concat(parameters('recoveryVaultName'), '/Microsoft.Authorization/vaultLock')]",
"properties": {
"level": "CanNotDelete",
"notes": "Key vault lock applied by Azure Policy"
}
}]
}
}
}
}
}
}
}
},
{
"type": "Microsoft.Authorization/policySetDefinitions",
"name": "policySetDefinition-policies-locks",
"apiVersion": "2019-09-01",
"dependsOn": [
"policyDefinition-rvault-lock",
"policyDefinition-kvault-lock"
],
"properties": {
"displayName": "Resource locks",
"policyType": "Custom",
"description": "Policies for applying and reporting on resource lock compliance.",
"metadata": {
"category": "General"
},
"policyDefinitions": [
{
"policyDefinitionId": "[resourceId('Microsoft.Authorization/policyDefinitions', 'policyDefinition-rvault-lock')]"
},
{
"policyDefinitionId": "[resourceId('Microsoft.Authorization/policyDefinitions', 'policyDefinition-kvault-lock')]"
}
]
}
},
{
"type": "Microsoft.Authorization/policyAssignments",
"name": "policyAssignment-policies-locks",
"apiVersion": "2019-09-01",
"dependsOn": [
"policySetDefinition-policies-locks"
],
"properties": {
"displayName": "Resource locks",
"scope": "[subscription().id]",
"policyDefinitionId": "[resourceId('Microsoft.Authorization/policySetDefinitions', 'policySetDefinition-policies-locks')]"
},
"location": "canadacentral",
"identity": {
"type": "SystemAssigned"
}
}
]}
Usage
New-AzDeployment -Location "canadacentral" -TemplateFile resource-locks.json -Verbose
View Assignment
By default, this assignment will only take effect on newly created resources. You can see the assignment in the Azure portal.
Policy Remediation
After roughly fifteen minutes, you will see what resources Azure Policy has identified as requiring remediation, in our example just 1 recovery services vault and 1 key vault are missing a resource lock.
These preexisting resources can be updated via a remediation task.
Remediation Task
To create a remediation task do the following:
- Go to the policy assignment view and click on the Resource locks policy initiative.
- Click on the Remediation tab and click Review + save. Click Save on the next screen. This step creates the managed identity. If you don’t do this step, your remediation tasks will not work.
- Go to the policy remediation view and click on the Deploy resource lock on Recovery services vaults policy definition.
- Click Remediate to create a new remediation task.
- Repeat for process for Deploy resource lock on Key vaults.
View Progress
Go to the policy remediation view and click Remediation tasks. You will see the history of all the remediation tasks that have been run and can view their status.
Spot Check
Open a couple of resources and confirm that the resource lock was indeed created and applied appropriately.
Going Forward
Assuming your remediation tasks we’re successful, we have now level set the subscription by placing locks on existing resources.
Going forward when recovery services vaults and key vaults are created, a resource lock will be created along with it by default.
Be aware though the process is not instantaneous, it can take upwards of ten minutes for the resource lock to be created.