Victor applies a CanNotDelete lock, tries to delete the resource, watches Azure refuse — then the controlled override.
Locks vs. policy vs. RBAC
These three get confused constantly, and interviewers probe exactly here. RBAC decides who is allowed to perform an action. Azure Policy decides what configurations are allowed to be created or changed. A resource lock stops a specific destructive action — delete, or delete-and-modify — regardless of who you are. A lock is the seatbelt: it does not care that you are allowed to drive; it stops you going through the windscreen. There are two levels — CanNotDelete (read and modify, but not delete) and ReadOnly (no changes at all). This lab uses CanNotDelete, the common one.
RBAC says who. A lock says not this, not even you.
You need a free Azure account, the Azure CLI (az), then be signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs (winget / brew / apt), and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash), preinstalled and already signed in.
You need Owner or User Access Administrator (managing locks needs Microsoft.Authorization/locks/*). Run it in Azure Cloud Shell (Bash). Cost: one empty storage account (pennies), deleted at the end. Bicep version and this guide: github.com/kloudcaptain/campux-labs.
A group with something worth protecting
# Windows/Git Bash: stop it mangling /subscriptions/... arguments (harmless on macOS/Linux) export MSYS_NO_PATHCONV=1 SUFFIX=$RANDOM RG="campux-lab-locks-rg" LOCATION="eastus" SA="campuxlock$SUFFIX" # 3-24 lowercase alphanumeric LOCK_NAME="campux-prod-donotdelete" az group create --name "$RG" --location "$LOCATION" az storage account create --name "$SA" --resource-group "$RG" \ --location "$LOCATION" --sku Standard_LRS -o none
az storage account show --name "$SA" --resource-group "$RG" --query name -o tsv returns $SA.Lock the resource group
One lock on the group protects everything inside it.
az lock create \ --name "$LOCK_NAME" \ --lock-type CanNotDelete \ --resource-group "$RG" \ --notes "Protect Campux production resources from accidental deletion."
az lock list --resource-group "$RG" --query "[].{name:name, level:level}" -o table shows your lock with level CanNotDelete.Prove it: the delete is refused
Try to delete the storage account. The lock must block it. (Lock changes usually take effect within seconds; occasionally ARM takes a minute to recognize a new lock.)
if az storage account delete --name "$SA" --resource-group "$RG" --yes 2>/tmp/lockerr; then
echo "!! Delete SUCCEEDED — the lock isn't enforcing yet. Wait ~60s, recreate the account, retry."
else
if grep -qi "ScopeLocked\|is locked\|locked and can" /tmp/lockerr; then
echo ">>> BLOCKED by the lock — exactly right."
else
echo "Delete failed for a NON-lock reason (not the proof):"; cat /tmp/lockerr
fi
fi
>>> BLOCKED by the lock. An authorized delete was stopped cold — not by permissions, but by the lock. That message prints only when the failure reason is a lock (ScopeLocked), so a green result is real proof.Remove the lock, then the delete works
az lock delete --name "$LOCK_NAME" --resource-group "$RG"
# Lock removal can also take a few seconds to register. Retry until it goes through.
for i in 1 2 3 4 5; do
if az storage account delete --name "$SA" --resource-group "$RG" --yes 2>/tmp/lockerr2; then
echo ">>> Deleted on attempt $i — with the lock gone, the same command now works."
break
fi
echo "attempt $i: still blocked (lock removal registering) — waiting 20s"
sleep 20
done
>>> Deleted. The identical command that was refused in Step 3 now succeeds — the only thing that changed is the lock. That is the entire lesson: the lock, not your permissions, was the gate.Tear it down
The storage account is already gone. Remove the group — no lock remains to block it.
az group delete --name "$RG" --yes
az group exists --name "$RG" # -> false
A locked resource group cannot be deleted until the lock is removed — that is the whole point of the lock. If az group delete ever refuses with a lock error, list with az lock list -g "$RG" and delete the lock first.
What you can now honestly claim
You applied a resource lock to protect production from accidental deletion, proved it blocks a real delete regardless of permissions, and removed it to let the same command through. A lock stops a destructive operation even for an Owner; CanNotDelete blocks deletion while allowing changes, ReadOnly blocks changes too; and locks are inherited, so one on a group protects everything inside it. In production you declare them as code — the Bicep version ships the lock with the infrastructure it protects. This pairs with the Azure Policy lab: locks stop deletion, policy stops non-compliant creation — two different guardrails.