Symptoms
- A deployment, delete or portal action fails with
ScopeLocked: "The scope '/subscriptions/.../resourceGroups/rg-app' cannot perform write operation because following scope(s) are locked". - A pipeline that used to work now fails on a resource nobody touched.
- Odd failures with no lock-related wording: storage keys won't list, a VM won't start, Azure Backup reports errors.
Management locks apply to everyone, including Owners. They are inherited from the subscription and resource group down to every resource, so the lock blocking you is often not on the resource you are changing.
The two lock types
| Lock | Blocks | Allows |
|---|---|---|
CanNotDelete (Delete in the portal) | Deleting the resource and anything under the locked scope | Reads and updates |
ReadOnly | Every write and delete, plus some POST operations | GET operations |
Locks act on the management plane (Azure Resource Manager). They do not stop data-plane operations such as deleting a blob or dropping a table inside a database.
Find the lock
List locks at every level that applies to the resource:
# Locks directly on the resource and inherited from its parents
az lock list --resource-group rg-app-prod \
--resource-name stappprod01 --resource-type Microsoft.Storage/storageAccounts \
--namespace "" -o table
# Every lock in the resource group (including on child resources)
az lock list --resource-group rg-app-prod -o table
# Every lock in the subscription
az lock list --query "[].{name:name, level:level, scope:id}" -o table
The id shows where the lock lives. A lock whose ID ends in /resourceGroups/rg-app-prod/providers/Microsoft.Authorization/locks/... covers the whole group.
PowerShell equivalent:
Get-AzResourceLock -ResourceGroupName rg-app-prod |
Select-Object Name, Properties, ResourceId
ReadOnly side effects that catch people out
ReadOnly blocks more than you would expect, because some operations that only read data are implemented as POST or write requests.
| What fails under a ReadOnly lock | Why |
|---|---|
Listing storage account keys (portal, az storage account keys list, tools that fetch keys) | Listing keys is a POST action |
| Starting, restarting or resizing a VM | Each is a write on the VM |
| Viewing App Service files in some tools, or changing app settings | Writes to the site configuration |
| AKS scale-out or upgrades when the node resource group is locked | The cluster can't create or update VMSS instances |
| Adding a subnet, private endpoint or peering on a locked VNet | Writes to the VNet |
CanNotDelete has its own trap: Azure Backup cleans up old restore point collections in the VM's resource group. A delete lock on that group stops the clean-up, and once the restore point limit is reached, new backups fail. Microsoft documents this case; keep restore point collections in a resource group without a delete lock.
Warning
Don't put
ReadOnlylocks on resource groups that hold running workloads. It looks like strong protection, but it breaks scaling, patching, certificate renewal and backups. UseCanNotDeletefor production and RBAC to control who can change things.
Removing or changing a lock
You need Microsoft.Authorization/locks/* permissions. Among built-in roles, Owner and User Access Administrator have them; Contributor does not.
The safe pattern for a planned change:
# 1. Record the lock so you can put it back exactly
az lock show --name lock-prod-nodelete --resource-group rg-app-prod -o json > lock-backup.json
# 2. Remove it
az lock delete --name lock-prod-nodelete --resource-group rg-app-prod
# 3. Make the change
# ...
# 4. Recreate it with the same name, type and notes
az lock create --name lock-prod-nodelete --resource-group rg-app-prod \
--lock-type CanNotDelete --notes "Production - change via CAB only"
Caution
The gap between steps 2 and 4 is when accidents happen. Do it in one script or pipeline run, add the change number to the lock notes, and never leave "I'll put it back later" for the end of the day.
To lock a single resource instead of a group:
az lock create --name nodelete-sql --lock-type CanNotDelete \
--resource-group rg-data-prod --resource-name sql-prod-01 \
--resource-type Microsoft.Sql/servers
Deployments and locks
- Bicep/ARM deployments in Complete mode try to delete resources that are not in the template. A
CanNotDeletelock makes the whole deployment fail, which is exactly what you want for production. - Terraform
destroyor a resource replacement (delete then create) fails withScopeLocked. Plan output that says "must be replaced" on a locked resource needs a deliberate decision, not a lock removal by reflex. - Manage locks themselves as code (
Microsoft.Authorization/locksin Bicep,azurerm_management_lockin Terraform) so they can't silently disappear.
Quick triage table
| Message or symptom | Likely lock | Action |
|---|---|---|
ScopeLocked on delete | CanNotDelete on resource, RG or subscription | Confirm the delete is intended, then use the remove-change-recreate pattern |
ScopeLocked on update/scale | ReadOnly somewhere above | Replace with CanNotDelete unless read-only is really required |
| Storage keys can't be listed | ReadOnly | Switch the app to Entra ID auth, or change the lock type |
| Backups failing with restore point errors | CanNotDelete on the restore point resource group | Move restore points to an unlocked resource group |
Prevent it next time
- Standardise:
CanNotDeleteon production resource groups, noReadOnlyon running workloads. - Put an owner and reason in every lock's
--notes. - Report on locks monthly (
az lock listacross subscriptions) and compare against your expected list. - Use Azure Policy to audit production resource groups that have no lock.