Locks In Azure Bicep - On Resource(s), Resource Group, Subscription
Protecting production resources from an accidental deletion or modification can sometimes be a very important thing to do. This is where locks in Azure come in handy.
Lock in Azure is an extension resource which can be applied to different scopes (e.g. resource or resource group) and allows to prevent other users from deleting or modifying resource(s). Azure Bicep allows managing locks through a template similar to other Azure resources.
It’s not always the case that a lock can protect from a person with a malicious intent, but for sure it is useful at preventing human mistakes. For example, if someone has a Contributor role (or temporary elevated) and wants to delete an unused/test CosmosDB instance but by accident deletes a production one.
Contents:
- Overview
- Required Permissions
- Resource Lock
- Add Lock To an Existing Resource
- Locking Resource Group or Subscription
- Create a Resource Group and Apply a Lock It
- Multiple Locks For Multiple Resources
- Related Posts
- Useful Links
Overview
In this post, we will cover a number of topics related to Azure locks and how to manage them using Azure Bicep.
As a first step, we discuss which permissions one needs to have to add/modify/delete locks. Keep in mind, that these permissions should be assigned to the identity under which the deployment is performed (e.g. user or service principal).
Next, we look at how to deploy a lock for a new resource or an already existing resource.
Also, it is possible to add a lock for an entire resource group or subscription, this is illustrated in the Locking Resource Group or Subscription section. By taking it a bit further, we see a use case of how to Create a Resource Group and Apply a Lock It.
If one wants to create many similar resources in a loop and also add locks for each of those resources, then Multiple Locks For Multiple Resources section will show a way how to achieve it.
NOTES:
- Lock are applied to all users and principals irrespective of what RBAC permissions they have. RBAC and locks serve different purposes, read more about role-based access control.
- Make sure to read considerations before applying locks. In some cases, lock’s behavior could be quite different from what you would expect, for example, locks are only applied to control plane operations, and it matters whether it’s a GET or POST request, also, your ARM deployments might start failing because it cannot delete deployment history.
Required Permissions
Managing locks requires certain permissions since we want to limit number of users who can delete or modify locks. As mentioned in the documentation, to work with locks we need to have Microsoft.Authorization/locks/* permissions, the star *
includes read
, write
, and delete
actions.
By default, Owner
and User Access Administrator
built-in roles include permissions to manage locks, but you can also create your custom role and include the necessary permissions.
Resource Lock
To add a lock for a particular resource, we just need to specify scope
property when creating a lock. In the following example, we are using a storage account as a sample resource.
- Line 19-20: Specifying
CanNotDelete
lock along with some random notes, another possible type of lock isReadOnly
. - Line 22: Passing the symbolic name
storageAccount
to thescope
property of a lock resource to apply it only to this resource and not the entire resource group
By using a symbolic name when specifying scope, Bicep will automatically create dependency between resources so that lock is deployed after the storage account’s deployment is finished.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ========== resource-lock.bicep ==========
param storageAccountName string = 'stcontoso'
// Creating some storage account resource
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: storageAccountName
location: resourceGroup().location
sku: {
name: 'Standard_GRS'
}
kind: 'StorageV2'
}
// Setting a lock for the storage account
resource lock 'Microsoft.Authorization/locks@2017-04-01' = {
name: '${storageAccountName}-lock'
properties: {
level: 'CanNotDelete'
notes: 'Storage account should not be deleted'
}
scope: storageAccount // Apply lock to the storage account only
}
Add Lock To an Existing Resource
Locking an existing resource is very similar to the resource lock described previously in this post. The only difference is that we don’t create the resource in the template but instead just only create a symbolic name.
NOTE: See the use of the existing
keyword on line 6 - this means that we are referencing an already existing resource.
Also, read more about how to Reference New Or Existing Resource In Azure Bicep.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ========== existing-resource-lock.bicep ==========
param storageAccountName string = 'stcontoso'
// Creating symbolic name to an existing resource
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' existing = {
name: storageAccountName
}
// Declaring a lock
resource lock 'Microsoft.Authorization/locks@2017-04-01' = {
name: '${storageAccountName}-lock'
properties: {
level: 'CanNotDelete'
notes: 'Storage account should not be deleted'
}
scope: storageAccount // Passing the scope of the lock
}
Locking Resource Group or Subscription
In this section, we will see how to apply a lock at the resource group or subscription scope. The simplest way to do it is to create a Microsoft.Authorization/locks
resource inside of the template where the target scope matches our resource group or subscription.
The examples below show how to lock a resource group and a subscription respectively. A few notes:
scope
property for the lock is not specified, the value is derived from the deployment scope of the template - this is the default behavior for the scope propertytargetScope
value defines the deployment scope of the template, the default one is resourceGroupCanNotDelete
lock type is used in the examples below, butReadOnly
will also work
Resource Group Lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ========== resourcegroup-lock.bicep ==========
targetScope = 'resourceGroup' // Default value, so this line could be omitted
// Declare other resources in the template
// resource res1 ..
// resource res2 ..
resource lock 'Microsoft.Authorization/locks@2017-04-01' = {
name: 'myResourceGroupLock'
properties: {
level: 'CanNotDelete'
notes: 'Lock to prevent resource group and its resources from being deleted'
}
}
Subscription Lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ========== subscription-lock.bicep ==========
targetScope = 'subscription' // Setting template deployment scope to subscription
// Declare other resources in the template
// resource res1 ..
// resource res2 ..
resource lock 'Microsoft.Authorization/locks@2017-04-01' = {
name: 'mySubscriptionLock'
properties: {
level: 'CanNotDelete'
notes: 'Lock to prevent subscription and its resources from being deleted'
}
}
Create a Resource Group and Apply a Lock It
In the Locking Resource Group or Subscription section we covered how to apply a lock to an existing resource group, but what if we don’t have a resource group yet?
Consider the use case where we want to do the following:
- Create a resource group using Bicep
- Deploy some resources into this new resource group
- Add a lock on the resource group level
The code snippet below illustrates how to achieve it, a few notes:
- Line 3: Template is deployed at
subscription
deployment scope, this allows us to create a resource group in the template - Line 12:
resourcegroup-lock.bicep
file is used as a module - Line 14: Module is deployed under the scope of the new resource group
NOTE: Microsoft.Authorization/locks
resource itself is declared in the module resourcegroup-lock.bicep and not in the main template.
Read more about how to Create Resource Group With Azure Bicep and Deploy Resources In It.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ========== create-resourcegroup-and-lock.bicep ==========
targetScope = 'subscription'
// Creating a resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: 'rg-bicep'
location: 'westus2'
}
// This part deploys resources as well as a lock into the resource group
module rgDeployment 'resourcegroup-lock.bicep' = {
name: 'rgDeployment'
scope: rg // Passing newly created resource group as module's deployment scope
}
Multiple Locks For Multiple Resources
Now, let’s discuss how we can deploy multiple resources using for
-loop and then add a lock to each resource individually. An alternative approach is to lock the entire resource group which was discussed earlier in Locking Resource Group or Subscription section, though it will lock all resources in that resource group and this could be not what we want.
Let’s assume we want to deploy multiple storage account resources using a loop each with a lock applied to it. The code is annotated with comments to help understand what’s happening, and just a few notes here:
- Line 4:
storageAccounts
holds an array of storage account resources, this symbolic name is used later when declaring locks - Line 28: We use
for
-loop syntax which passes both item (sa
) and index (idx
) inside the iteration, see loop syntax - Line 34: Index
idx
is used to retrieve the corresponding storage account
NOTE: An alternative approach is to create a module which deploys one storage account and adds a lock to it. Then we just invoke this module in the main file in a loop. This could be even cleaner solution depending on the use case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// ========== lock-array-of-resources.bicep ==========
// Pass configuration in form of array of objects
param storageAccountConfigs array = [
{
name: 'stcontoso1'
skuName: 'Standard_LRS'
location: 'eastus'
}
{
name: 'stcontoso2'
skuName: 'Standard_GRS'
location: 'westus2'
}
]
// Create storage account resources using a for-loop
resource storageAccounts 'Microsoft.Storage/storageAccounts@2021-06-01' = [for sa in storageAccountConfigs: {
name: sa.name
location: sa.location
sku: {
name: sa.skuName
}
kind: 'StorageV2'
}]
// Add a lock for each storage account created above
resource locks 'Microsoft.Authorization/locks@2017-04-01' = [for (sa, idx) in storageAccountConfigs: {
name: '${sa.name}-lock'
properties: {
level: 'CanNotDelete'
notes: 'Storage account should not be deleted'
}
scope: storageAccounts[idx] // Passing the right storage account as a scope for the lock
}]
Related Posts
- Parameters In Azure Bicep - Ultimate Guide With Examples
- Variables In Azure Bicep - From Basics To Advanced
- Reference New Or Existing Resource In Azure Bicep
- Learn Modules In Azure Bicep - Basics To Advanced, How It Works, Nested Modules, Outputs, Scopes
- Create Resource Group With Azure Bicep and Deploy Resources In It
- 5 Ways To Deploy Bicep File With Parameters - Azure DevOps, PowerShell, CLI, Portal, Cloud Shell
- Deploy Azure Bicep In YAML and Classic Release Pipelines (CI/CD) - Azure DevOps
- Nested Loops In Azure Bicep - 4 Use Cases, For-Loop, Solutions & Workarounds
Useful Links
- Lock resources to prevent unexpected changes | Microsoft Docs
- Microsoft.Authorization locks - Bicep & ARM template reference | Microsoft Docs
- Microsoft.Authorization - Azure resource provider operations | Microsoft Docs
- Scope on extension resource types (Bicep) - Azure Resource Manager | Microsoft Docs