Azure Resource Locks

My colleague Yannick Dils, who specializes in digital transformation, cloud, and modern workplace adoption, and myself were invited to deliver a talk for “Azure Back to School 2021”. Our talk aimed to inform the viewer on some of the various sorts of Azure governance tools that they have at their disposal, many of which are built into the Azure platform. The talk is an amalgamation of many of the governance tips we’ve spoken (and written) about in the past, along with some new ones. As a result, the talk covers quite a lot of ground.

💡 The following blog takes inspiration from our talk “Jurassic Governance - 10 tips to save your resources from extinction”, presented at “Azure Back to School 2021”.

Why use locks?

As an Azure administrator, you can choose to lock subscriptions, resource groups, or resources to prevent other users in your organization from accidentally deleting or modifying important resources.

The resource lock is a relatively small feature that can still be quite useful as it can override permissions that a security principal might have received from Azure RBAC. (A security principal is an object that represents a user, group, service principal, or managed identity that is requesting access to Azure resources.)

Creating locks

Firstly, let’s create a new storage account, along with a new resource group. I will use the “Standard_LRS” SKU when deploying the storage account, this will keep the costs of the demo down. Open up the newly created resource group and look for the “Locks” setting.

Image of the resource group view in the Azure Portal, the locks setting is highlighted.

Selecting the option will take us to the locks settings view for the resource group. Once the tab has loaded we can click the “Add” button, which opens up the dialogue box to create a lock.

Image of the resource group locks settings view in the Azure Portal. The ‘Add’ button is highlighted, when clicked it opens a dialogue box. In the Add dialogue box you can enter a lock name, enter a note and select a lock type (available types are delete and read-only)

You can set the lock type to “CanNotDelete” or “ReadOnly”. In the Azure portal, the locks are called “Delete” and “Read-only” respectively:

  • CanNotDelete
    • Authorized users can read and modify a resource, but they cannot delete the resource.
  • ReadOnly
    • Authorized users can read a resource, but they cannot delete or update the resource.
    • Applying this lock is similar to restricting all authorized users to the RBAC permissions granted by the Reader role.

Image of the resource group locks settings view in the Azure Portal, a do not delete entry has been added.

As you can see, the lock has been created, that’s excellent! When we try to delete the resource group or any of its child resources, we will get an error.

Image of a notification toast, it reads: “Delete resource group tst-weu-storage-rg failed. The resource group tst-weu-storage-rg is locked and can’t be deleted. Click here to manage the locks for this resource group.

This is exactly what we wanted. Keep in mind that a “CanNotDelete” lock does give us the ability to make changes to the resource group and its children. Try modifying some tags and you will notice that those actions will still work.

Alternative ways to manage locks

You can also create resource locks with all the other Azure tools ARM Templates, Bicep, PowerShell, AZ CLI and the REST API. Let us perform the previous steps with PowerShell:

$location = "westeurope"
$resourceGroup = New-AzResourceGroup -Name "tst-weu-storage-rg" `
    -Location $location
$storageAccount = New-AzStorageAccount -Name "tstweucdptlogs4f49" `
    -Location $resourceGroup.Location `
    -SkuName Standard_LRS `
    -ResourceGroupName $resourceGroup.ResourceGroupName
$rgLock = New-AzResourceLock -LockName "donotdelete" `
    -LockNotes "🦖 Jurassic Governance - 10 tips to save your resources from extinction" `
    -LockLevel CanNotDelete `
    -ResourceGroupName $resourceGroup.ResourceGroupName `
    -Force

# Name              : donotdelete
# ResourceId        : /subscriptions/4f49eb65-b0b0-4884-876a-4c93ddb128a0/resourceGroups/tst-weu-storage-rg/providers/Microsoft.Authorization/locks/donotdelete
# ResourceName      : donotdelete
# ResourceType      : Microsoft.Authorization/locks
# ResourceGroupName : tst-weu-storage-rg
# SubscriptionId    : 4f49eb65-b0b0-4884-876a-4c93ddb128a0
# Properties        : @{level=CanNotDelete; notes=🦖 Jurassic Governance - 10 tips to save your resources from extinction}
# LockId            : /subscriptions/4f49eb65-b0b0-4884-876a-4c93ddb128a0/resourceGroups/tst-weu-storage-rg/providers/Microsoft.Authorization/locks/donotdelete

Just as before, when you try to remove the resource group, you will not be able to do so.

Remove-AzResourceGroup -Name $resourceGroup.ResourceGroupName -Force
# Remove-AzResourceGroup: The scope '/subscriptions/4f49eb65-b0b0-4884-876a-4c93ddb128a0/resourcegroups/tst-weu-storage-rg' cannot perform delete operation because following scope(s) are locked: '/subscriptions/4f49eb65-b0b0-4884-876a-4c93ddb128a0/resourceGroups/tst-weu-storage-rg'. Please remove the lock and try again.
# StatusCode: 409
# ReasonPhrase: Conflict
# OperationID : 724d9fec-1be9-410f-94b9-aac1439e7fe6

Locks and RBAC

Unlike role-based access control, you use management locks to apply a restriction across all users and roles. If I list the current role assignments for my user, I should have “Owner” permissions on the subscription:

Get-AzRoleAssignment -SignInName "[email protected]" -ExpandPrincipalGroups | Format-Table -AutoSize -Property DisplayName, RoleDefinitionName, Scope

DisplayName      RoleDefinitionName        Scope
-----------      ------------------        -----
AzureAdmins      Owner                     /subscriptions/4f49eb65-b0b0-4884-876a-4c93ddb128a0
AzureAdmins      Owner                     /providers/Microsoft.Management/managementGroups/dfe5d2bc-125a-4281-a6fe-bbfb4bd3a0f8
Thomas Van Laere User Access Administrator /

Even though I have the highest privileges available, my user is unable to delete the resource group! This is what I want to prevent accidental deletions, or modifications using a read-only lock.

Lock inheritance

The Microsoft docs on Azure Resource Locks inheritance is very clear on how inheritance works, so I will leave it to the article its authors to explain how inheritance works:

“When you apply a lock at a parent scope, all resources within that scope inherit the same lock. Even resources you add later inherit the lock from the parent. The most restrictive lock in the inheritance takes precedence.”

Let’s look at the locks that are currently configured on the Storage Account, you will notice that we get an entry that originates from our resource group.

Image of the storage account locks settings view in the Azure Portal, a do not delete entry is listed. This entry its scope set to ‘tst-weu-storage-rg’. A message is also visible above the locks list, it reads: “Parent resource locks can’t be edited here. Click on the locks scope to go to that scope.

Lock scoping

It’s important to highlight that locks do not apply to all types of operations. Azure operations are typically divided into two categories:

  • Data plane operations
    • Operations sent to an instance of a service, ie: “myaccount.blob.core.windows.net”.
  • Control plane operations
    • Operations sent to the “management.azure.com” domain.

Azure Resource Locks exclusively apply to control plane operations, this implies that every operation that does not pass through this endpoint will not be subject to the resources locks. Operations such as deleting a table in your Azure SQL database or a file in your Azure BLOB storage will still work because these resources target a different API endpoint. To demonstrate this, I decided to use my web browser but you could just as easily manually invoke these endpoints via PowerShell, AZ CLI or the REST API.

I created a new BLOB container called “test” in the Storage Account and uploaded a file called “Test.txt” to the container. I did all of this while having my browser’s “network tab” open in the “developer tools”, this allows me to capture in and outgoing requests.

Image of the browser’s development tools window, the networking tab is opened. There are multiple requests being sent to the data plane URL ‘myaccount.blob.core.windows.net’

When we upload a file we are submitting requests to the data plane: “myaccount.blob.core.windows.net”.

If we go ahead and delete that same file, you will notice the following:

Image of the browser’s development tools window, the networking tab is opened. There are is a delete request being sent to the data plane URL ‘myaccount.blob.core.windows.net’

A HTTP “DELETE” request was sent to the data plane and effectively deleted the BLOB, all while our resource locks did nothing to prevent the action from occurring.

💡 To prevent a service principal from removing BLOBs, I would suggest assigning it a read-only role via Azure RBAC. Should you want to create a custom role, try having a look at setting a specific “NotDataActions” entry, more specifically you will need to add the “Microsoft.Storage/storageAccounts/blobServices/containers/blobs/deletedata operation.

Though if we delete the BLOB container, not the file, through the Azure Portal that gets us a different result.

Image of the browser’s development tools window, the networking tab is opened. There are is a delete request being sent to the control plane URL ‘management.azure.com’. The request’s header contains a key ‘x-ms-command-name’ with value ‘Microsoft_Azure_Storage.StorageClient.DeleteContainer’. The request’s header contains another key ‘x-ms-path-query’ with the full URI to the BLOB container as a value.

This HTTP “DELETE” request was sent to the control plane, “management.azure.com”, where our resource locks will stop the delete action dead in its tracks and return a “409 - Conflict” result.

💡 If you’d like to learn about control and data plane operations in a little more depth, the Microsoft documentation is a very good place to start. To find out which operations use the control plane URL or data plane, Microsoft recommends taking a look at the Azure REST API.

In conclusion

Azure Resource Locks are a simple but powerful tool to have in your Azure governance arsenal. I like to think that even the simplest of tools can hide quite a bit of complexity, so it is always wise to take a closer look at the specifics before you begin adopting something.

I would recommend that you perform a test in a separate environment prior to setting up resource locks, as activating them can have unforeseen consequences. The Microsoft docs have an up-to-date list of considerations that you should be aware of before applying a resource lock.