Theoretical Foundation: Configure Resource Locks
1. Initial Intuitionβ
Imagine you have a critical production database server. You know that any accidental deletion of this server would cause a business catastrophe. RBAC controls who can act, but a legitimate administrator with Contributor permissions could still delete this server by mistake.
What you need is a physical lock on the resource, regardless of who is trying to act on it. Something that says: "no matter who you are or what permissions you have, this resource cannot be deleted."
This is a Resource Lock: a protection that acts at the resource level itself, blocking deletion or modification operations, regardless of the user's RBAC permissions. It's the equivalent of putting a security padlock on critical equipment, where even the department manager needs a special key to remove it.
2. Contextβ
Where Resource Locks fit in Azure governanceβ
Resource Locks are a distinct protection layer from the others:
Resource Locks exist because the previous layers are not sufficient for protection against accidents or malicious actions by privileged users. An Owner can delete any resource by RBAC definition. A Resource Lock prevents this without needing to revoke Owner permissions.
What Resource Locks protectβ
- Critical production resources against accidental deletion
- Central network infrastructure (VNets, gateways) against modification
- Storage accounts with critical data
- Key Vaults with production secrets
- Any resource whose unavailability or loss would cause severe impact
3. Building the Conceptsβ
3.1 The two types of locksβ
There are exactly two types of Resource Lock, and understanding the difference between them is fundamental:
| Type | Portal name | Blocked operations | Allowed operations |
|---|---|---|---|
| CanNotDelete | Delete | DELETE | Read and modification |
| ReadOnly | Read-only | DELETE and PUT/PATCH | Only GET (read) |
CanNotDelete protects against deletion, but still allows the resource to be modified. A user can change configurations, but not delete.
ReadOnly is much more restrictive: it blocks any operation that changes the resource state, including creation of child resources. It transforms the resource into read-only for everyone, regardless of permissions.
3.2 Who can create and remove locksβ
Managing Resource Locks requires one of the following roles:
| Role | Can create locks | Can remove locks |
|---|---|---|
| Owner | Yes | Yes |
| User Access Administrator | Yes | Yes |
| Contributor | No | No |
| Reader | No | No |
This is a critical and counterintuitive point: Contributor cannot manage locks. A Contributor has broad power to create and modify resources, but cannot place or remove a lock. This is intentional: the protection cannot be removed by someone with common operational access.
To manage locks specifically without granting full Owner, there's the built-in role Microsoft.Authorization/locks/* that covers lock operations.
3.3 Lock inheritanceβ
Locks behave similarly to RBAC regarding inheritance: a lock applied at a higher level affects all resources below.
If you apply ReadOnly to the Resource Group, all resources within it become ReadOnly, regardless of individual locks on the resources. A lock on the RG is more comprehensive and harder to "forget" than locks on individual resources.
3.4 Locks and child resourcesβ
A fundamental and non-obvious behavior: a lock on a Resource Group does not appear as a lock on individual resources within the RG. If you access Access Control or the Locks tab of a VM within an RG with a lock, the VM might not show any lock in its own list. The lock is on the RG, not on the VM.
To see all effective locks on a resource, you need to check the resource itself and all its ancestor scopes (RG and Subscription).
4. Structural Viewβ
Flow of evaluating an operation with locksβ
When an operation is blocked by a lock, ARM returns the HTTP error 409 Conflict with the code ScopeLocked. This error is distinct from a 403 RBAC error (which indicates lack of permission) and clearly informs that the blocking is due to a lock, not insufficient permission.
Locks vs. RBAC vs. Policy: structural comparisonβ
5. Practical Operationβ
Non-obvious ReadOnly behaviorsβ
The ReadOnly lock has implications that go far beyond "cannot modify". Some surprising behaviors:
1. Storage Account with ReadOnly doesn't allow listing access keys.
The list keys operation (listKeys) is classified as a POST action that modifies state (generates a credential resource). With ReadOnly, it's blocked. Applications that depend on listing keys to access storage will stop working.
2. VMs with ReadOnly cannot be started or stopped. Starting a VM is a write operation on the resource state. With a ReadOnly lock on the Resource Group containing the VM, no start/stop/restart operations will work.
3. Cannot create resources within a Resource Group with ReadOnly. If the RG has ReadOnly, no new resources can be created in it, as creation is a write operation.
4. App Services with ReadOnly on the RG may have execution problems. Some internal App Service runtime operations write state. A ReadOnly on the RG can break running services, not just management operations.
Practical conclusion: Use ReadOnly with extreme caution on resources that need to operate normally. It's suitable primarily for static infrastructure that doesn't need day-to-day management.
CanNotDelete behavior with child resourcesβ
When you try to delete a Resource Group with a CanNotDelete lock, the operation fails even if there are no locks on individual resources within the RG. The lock on the RG protects against deleting the RG itself and implicitly protects all resources within (since deleting the RG would delete everything).
However, a CanNotDelete lock on the RG does not prevent deletion of individual resources within it. For complete protection of individual resources, you need to apply locks on the resources or on the RG with ReadOnly.
Multiple locks on the same scopeβ
It's possible to have multiple locks on the same resource or scope. The most restrictive lock prevails. If a resource has both a CanNotDelete and a ReadOnly (unlikely, but possible), ReadOnly prevails as it's more restrictive.
6. Implementation Methodsβ
Azure Portalβ
When to use: manual protection of individual critical resources, quick visual verification
To create a lock:
- Navigate to the resource, Resource Group, or Subscription
- In the side menu, click Locks (under Settings)
- Click + Add
- Define the name, type (CanNotDelete or ReadOnly), and optional notes
- Click OK
To remove a lock:
- Access the same Locks screen
- Click the delete icon next to the lock
- Confirm removal
Advantage: quick, visual, easy for verification Limitation: doesn't scale, not reproducible, no version control
Azure CLIβ
# Create CanNotDelete lock on a Resource Group
az lock create \
--name "rg-prod-nodelete" \
--resource-group "rg-producao" \
--lock-type CanNotDelete \
--notes "Protection against accidental deletion - Approved by: CTO"
# Create ReadOnly lock on a Resource Group
az lock create \
--name "rg-network-readonly" \
--resource-group "rg-networking" \
--lock-type ReadOnly \
--notes "Production network - do not modify without Change Request"
# Create lock on a specific resource
az lock create \
--name "keyvault-prod-lock" \
--resource-group "rg-seguranca" \
--resource-type "Microsoft.KeyVault/vaults" \
--resource "kv-prod-001" \
--lock-type CanNotDelete \
--notes "Production Key Vault - CRITICAL"
# Create lock on an entire Subscription
az lock create \
--name "sub-prod-nodelete" \
--lock-type CanNotDelete \
--scope "/subscriptions/<sub-id>" \
--notes "Production subscription"
# List locks in a Resource Group (including inherited)
az lock list \
--resource-group "rg-producao" \
--output table
# List ALL locks in the subscription
az lock list \
--output table
# Remove a lock (requires Owner or User Access Administrator)
az lock delete \
--name "rg-prod-nodelete" \
--resource-group "rg-producao"
# Check locks on a specific resource
az lock list \
--resource-group "rg-seguranca" \
--resource-type "Microsoft.KeyVault/vaults" \
--resource "kv-prod-001" \
--output table
Warning: the
az lock listcommand with--resource-groupshows locks applied to that RG and resources within it, but doesn't show locks inherited from higher levels (Subscription). For complete auditing, also includeaz lock listwithout filters to see all.
Azure PowerShellβ
# Create CanNotDelete lock on RG
New-AzResourceLock `
-LockName "rg-prod-nodelete" `
-LockLevel CanNotDelete `
-ResourceGroupName "rg-producao" `
-Notes "Protection against accidental deletion"
# Create ReadOnly lock on RG
New-AzResourceLock `
-LockName "rg-network-readonly" `
-LockLevel ReadOnly `
-ResourceGroupName "rg-networking" `
-Notes "Production network - read only"
# Create lock on specific resource
New-AzResourceLock `
-LockName "storage-critical-lock" `
-LockLevel CanNotDelete `
-ResourceGroupName "rg-dados" `
-ResourceName "stgcritico01" `
-ResourceType "Microsoft.Storage/storageAccounts" `
-Notes "Storage with backup data"
# List all locks in an RG
Get-AzResourceLock -ResourceGroupName "rg-producao"
# List locks on a specific resource
Get-AzResourceLock `
-ResourceGroupName "rg-seguranca" `
-ResourceName "kv-prod-001" `
-ResourceType "Microsoft.KeyVault/vaults"
# Remove lock
Remove-AzResourceLock `
-LockName "rg-prod-nodelete" `
-ResourceGroupName "rg-producao" `
-Force
ARM Templates / Bicepβ
Locks can be included in templates to ensure that every time an environment is provisioned, locks are created automatically.
Bicep for lock on a resource within the same template:
// Create the Key Vault
resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = {
name: 'kv-prod-001'
location: resourceGroup().location
properties: {
sku: { family: 'A', name: 'standard' }
tenantId: subscription().tenantId
enableSoftDelete: true
}
}
// Create lock on the Key Vault
resource kvLock 'Microsoft.Authorization/locks@2020-05-01' = {
name: 'kv-prod-nodelete'
scope: keyVault
properties: {
level: 'CanNotDelete'
notes: 'Production Key Vault - do not delete'
}
}
Bicep for lock on Resource Group (using module with targetScope):
// file: rg-lock.bicep
targetScope = 'resourceGroup'
resource rgLock 'Microsoft.Authorization/locks@2020-05-01' = {
name: 'rg-prod-nodelete'
properties: {
level: 'CanNotDelete'
notes: 'Production Resource Group - protected'
}
}
When to use templates: environments provisioned via IaC where the lock should exist from the beginning and be recreated if the environment is redeployed.
Terraformβ
# CanNotDelete lock on Resource Group
resource "azurerm_management_lock" "rg_lock" {
name = "rg-prod-nodelete"
scope = azurerm_resource_group.prod.id
lock_level = "CanNotDelete"
notes = "Protection against accidental deletion"
}
# ReadOnly lock on specific resource
resource "azurerm_management_lock" "kv_lock" {
name = "kv-prod-readonly"
scope = azurerm_key_vault.prod.id
lock_level = "ReadOnly"
notes = "Production Key Vault - read only"
}
Warning with Terraform and locks: if you apply a CanNotDelete lock to an RG via Terraform and then try to do
terraform destroy, the destroy will fail because Terraform will try to delete the resources, including the RG itself. You'll need to remove the lock first. This is expected and is exactly the protection the lock provides.
7. Control and Securityβ
Who can manage locks: permission detailsβ
Lock operations are:
| Operation | Required permission |
|---|---|
| Create lock | Microsoft.Authorization/locks/write |
| Delete lock | Microsoft.Authorization/locks/delete |
| Read locks | Microsoft.Authorization/locks/read |
The built-in roles that include these permissions are Owner and User Access Administrator. Contributor explicitly does not include Microsoft.Authorization/locks/write.
If you need to delegate only lock management without giving full Owner, you can create a Custom Role with only lock permissions:
{
"Name": "Lock Manager",
"Actions": [
"Microsoft.Authorization/locks/read",
"Microsoft.Authorization/locks/write",
"Microsoft.Authorization/locks/delete"
],
"AssignableScopes": ["/subscriptions/<sub-id>"]
}
Locks and Azure Policyβ
Locks and Policy are complementary. A robust strategy uses Policy to ensure locks exist:
There's a built-in initiative in Azure Policy called "Append a lock of type CanNotDelete to resource groups" that uses the Append effect to ensure every new Resource Group created automatically receives a CanNotDelete lock.
This solves the problem of forgetting to apply locks manually to new RGs.
8. Decision Makingβ
CanNotDelete vs. ReadOnlyβ
| Situation | Recommended lock | Reason |
|---|---|---|
| Production database that needs to operate | CanNotDelete | ReadOnly would block the service's own operations |
| VNet and network resources that rarely change | CanNotDelete | Allows network maintenance, prevents deletion |
| Complete production Resource Group | CanNotDelete on RG | Comprehensive protection without impacting operations |
| Identity infrastructure (DNS, AD) | ReadOnly | Should not be modified; any change must be deliberate and require removing the lock |
| Key Vault with critical secrets | CanNotDelete | ReadOnly would prevent secret rotation |
| Entire production subscription | CanNotDelete on Sub | Additional layer for entire subscription |
| Lab/dev environment | None | Locks prevent agility in test environments |
Where to apply the lock: resource vs. Resource Groupβ
| Scenario | Lock level | Reason |
|---|---|---|
| Protect a specific resource within an RG with other mutable resources | Resource | Necessary granularity |
| Protect all production resources in an RG | Resource Group | One lock covers everything, simpler to manage |
| Protect entire network infrastructure | Network Resource Group | Everything in the network RG is critical |
| Maximum organizational protection | Subscription | Covers all RGs, but use with care |
9. Best Practicesβ
Apply locks on production Resource Groups as a default rule. Every production Resource Group should have at least a CanNotDelete lock. This should be a mandatory checklist item in the go-live process for any environment.
Use IaC to ensure locks from provisioning. Include lock creation in your Terraform or Bicep templates. This way, the lock is created together with the environment and doesn't depend on a manual step that can be forgotten.
Document locks with the "Notes" field. The lock notes field should contain: who approved it, why the lock exists, and what should be done before removing it. For example: "Production - Remove only with CTO approval via change ticket".
Combine locks with Azure Policy for automatic coverage. Use Append Policy to ensure new RGs always receive locks. This eliminates the human risk of forgetting.
Prefer CanNotDelete over ReadOnly for operational resources. ReadOnly should be reserved for truly static infrastructure. For most production resources that need to operate normally, CanNotDelete is the right choice.
Review locks periodically. Regularly audit which locks exist, if they still make sense, and if the notes field is updated. "Forgotten" locks without documentation cause operational confusion.
Never use ReadOnly on an entire active production Subscription. The impact is catastrophic: it blocks practically all management operations for all resources. If you need to protect the Subscription, use CanNotDelete.
10. Common Errorsβ
| Error | Why it happens | How to avoid |
|---|---|---|
| Apply ReadOnly on RG with running VMs | Not testing impact beforehand | Test in dev environment, document impacts per resource type |
| Try to remove lock with Contributor account | Not knowing that Contributor doesn't manage locks | Use Owner account or create specific lock manager role |
| Believe that Contributor cannot use resources with CanNotDelete lock | Confuse lock with usage restriction | CanNotDelete only prevents DELETE; Contributor can still create, read and modify |
| Not document the lock reason | Rush at creation time | Make "Notes" field mandatory in the process |
| Create lock on resource without noticing RG already has lock | Fragmented management | Always check locks at all ancestor scopes |
| Forget to create lock on new production RGs | Manual process subject to human error | Use Azure Policy with Append to automate |
| Confuse lock with access protection | Lock and RBAC are different layers | Remember: lock blocks specific operations; RBAC controls who can operate |
| Try to delete RG via portal and not understand 409 error | Not associating the error with the lock | Always check Locks tab before reporting permission issue |
The most costly error: ReadOnly in active environmentβ
Applying ReadOnly to a Resource Group with running services can bring down services immediately. VMs don't respond to commands, App Services may fail in internal operations, Automation runbooks can stop. The fix (remove the lock) is immediate, but downtime will have already occurred.
11. Operation and Maintenanceβ
Auditing existing locksβ
# List all locks in entire subscription (complete view)
az lock list --output table
# Filter only locks of specific type
az lock list \
--query "[?properties.level=='ReadOnly']" \
--output table
# Check locks in specific RG
az lock list \
--resource-group "rg-producao" \
--output table
# Export all locks to JSON (for auditing)
az lock list --output json > locks-audit-$(date +%Y%m%d).json
Monitor lock creation and removalβ
Lock changes are recorded in Activity Log:
# See lock creation and removal in Activity Log
az monitor activity-log list \
--resource-provider "Microsoft.Authorization" \
--query "[?operationName.value=='Microsoft.Authorization/locks/write' || operationName.value=='Microsoft.Authorization/locks/delete']" \
--output table
Configure an Activity Log Alert to be notified when locks are removed on critical scopes:
az monitor activity-log alert create \
--name "Alerta-Lock-Removido-Prod" \
--resource-group "rg-monitoramento" \
--condition category=Administrative \
and operationName=Microsoft.Authorization/locks/delete \
--scope "/subscriptions/<sub-id>/resourceGroups/rg-producao" \
--action-group "/subscriptions/<sub-id>/resourceGroups/rg-monitoramento/providers/microsoft.insights/actionGroups/ag-alertas-criticos"
This alert is especially important: if someone removes a production lock without following the change management process, you'll be notified immediately.
Important limitsβ
| Limit | Value |
|---|---|
| Locks per resource | 20 |
| Locks per Resource Group | 20 |
| Locks per Subscription | 20 |
In practice, these limits are rarely reached. A well-designed lock strategy uses a single well-documented lock per scope.
12. Integration and Automationβ
Production environment protection pipelineβ
A common pattern is to include lock creation as the final step of a production deploy pipeline:
Change Management process with locksβ
In organizations with mature change management processes, removing a production lock is part of the change process:
The lock removal alert configured in Activity Log serves as evidence and auditing for this process.
Integration with Azure Blueprintsβ
Azure Blueprints (despite being in deprecation process in favor of Deployment Stacks and Bicep) used locks to protect resources deployed via Blueprint. The concept of Blueprint lock was more restrictive than normal locks: not even Owner could remove it without first removing the Blueprint assignment.
The modern successor is Deployment Stacks, which offers similar capability to lock resources deployed together.
13. Final Summaryβ
Essential points:
- Resource Locks are a protection layer independent of RBAC, applied at the resource level by ARM
- There are exactly two types: CanNotDelete (blocks DELETE) and ReadOnly (blocks DELETE and PUT/PATCH)
- Contributor cannot create or remove locks; only Owner and User Access Administrator can
- Locks are inherited top-down: a lock on RG affects all resources within
- Locks block operations returning HTTP 409 Conflict with code
ScopeLocked - A lock on an RG doesn't appear in the locks list of individual resources within the RG
Critical differences:
- CanNotDelete vs. ReadOnly: CanNotDelete allows normal operations except deletion; ReadOnly blocks any modification, including VM start/stop and storage key listing
- Lock vs. RBAC: Lock acts independent of permissions; even Owner has operations blocked by a lock (but can remove it)
- Lock on resource vs. on RG: lock on RG protects everything inside, but doesn't appear in the Locks tab of individual resources; need to check ancestor scopes
- Remove lock vs. not having lock: removing a lock requires Owner/UAA; a Contributor can use a resource with CanNotDelete lock normally (except delete)
What needs to be remembered for AZ-104:
- The lock limit per scope is 20
- The Locks tab is in Settings of any resource, RG or Subscription in the portal
- Locks are resources of type
Microsoft.Authorization/locksmanaged by ARM - ReadOnly on a Storage Account blocks the
listKeysoperation, breaking applications that depend on listing keys - The recommended combination for production is: CanNotDelete on RG + Activity Log Alert for lock removal
- Azure Policy can use Append effect to ensure locks are automatically created on new Resource Groups