Azure Resource Manager — Resource Groups, Tags, and Locks Explained
You just deployed 47 resources across three Azure subscriptions. Now your manager asks, "Which team owns that storage account?" and "Why is our bill $3,000 higher this month?" If you cannot answer those questions in under 30 seconds, you need Azure Resource Manager discipline.
What Is Azure Resource Manager?
Azure Resource Manager (ARM) is the management layer that sits between you and every Azure service. Every request — whether it comes from the Azure portal, Azure CLI, PowerShell, REST API, or an SDK — passes through ARM. Think of it as the front door that authenticates, authorizes, and routes every operation.
ARM gives you three superpowers:
- Resource Groups — logical containers to organize resources
- Tags — metadata labels for cost tracking and automation
- Locks — guardrails that prevent accidental deletion or modification
Let us break each one down with real commands you can run today.
Resource Groups — Your Organizational Foundation
A resource group is a logical container that holds related Azure resources. Every resource in Azure must live in exactly one resource group. Delete the group, and everything inside goes with it.
Creating Resource Groups
# Create a resource group for your production workload
az group create \
--name rg-prod-webapp-eastus \
--location eastus
# Create a resource group for development
az group create \
--name rg-dev-webapp-eastus \
--location eastus
Naming Conventions That Scale
A good naming convention prevents chaos at scale. Here is a pattern that works:
| Component | Format | Example |
|---|---|---|
| Resource Group | rg-{env}-{workload}-{region} | rg-prod-webapp-eastus |
| Virtual Machine | vm-{env}-{role}-{number} | vm-prod-web-001 |
| Storage Account | st{env}{workload}{region} | stprodwebappeastus |
| Virtual Network | vnet-{env}-{region} | vnet-prod-eastus |
Listing and Inspecting Groups
# List all resource groups
az group list --output table
# Show details of a specific group
az group show --name rg-prod-webapp-eastus
# List all resources inside a group
az resource list \
--resource-group rg-prod-webapp-eastus \
--output table
Deleting Resource Groups
This is the nuclear option — it deletes everything inside the group. Use with caution.
# Delete a resource group and all its resources (requires confirmation)
az group delete --name rg-dev-webapp-eastus
# Delete without confirmation (scripts and CI/CD)
az group delete --name rg-dev-webapp-eastus --yes --no-wait
Tags — The Secret to Cost Tracking and Automation
Tags are name-value pairs you attach to resources and resource groups. They do not affect resource behavior, but they transform your ability to track costs, enforce policies, and automate operations.
Applying Tags
# Tag a resource group
az group update \
--name rg-prod-webapp-eastus \
--tags Environment=Production Team=Platform CostCenter=CC-1234
# Tag an individual resource
az resource tag \
--tags Environment=Production Team=Platform \
--resource-group rg-prod-webapp-eastus \
--name stprodwebappeastus \
--resource-type Microsoft.Storage/storageAccounts
Querying Resources by Tag
This is where tags shine. Your finance team wants to know total spend per team? Tags make it possible.
# Find all resources tagged as Production
az resource list \
--tag Environment=Production \
--output table
# Find all resources belonging to the Platform team
az resource list \
--tag Team=Platform \
--query "[].{Name:name, Type:type, Location:location}" \
--output table
Recommended Tag Strategy
| Tag Name | Purpose | Example Values |
|---|---|---|
Environment | Deployment stage | Production, Staging, Development |
Team | Owning team | Platform, Backend, Data |
CostCenter | Billing code | CC-1234, CC-5678 |
Project | Project name | WebApp, DataPipeline |
ManagedBy | IaC tool | Terraform, Bicep, Manual |
Resource Locks — Prevent Accidental Disasters
Imagine a junior engineer deletes the production database resource group on a Friday afternoon. Resource locks exist to prevent exactly this scenario.
Azure supports two lock types:
| Lock Type | Effect |
|---|---|
| CanNotDelete | Resources can be read and modified, but not deleted |
| ReadOnly | Resources can only be read — no modifications, no deletions |
Creating Locks
# Prevent deletion of the production resource group
az lock create \
--name DoNotDelete \
--resource-group rg-prod-webapp-eastus \
--lock-type CanNotDelete \
--notes "Production workload - requires change ticket to remove"
# Make a storage account read-only (careful — this blocks writes!)
az lock create \
--name ReadOnlyLock \
--resource-group rg-prod-webapp-eastus \
--resource-name stprodwebappeastus \
--resource-type Microsoft.Storage/storageAccounts \
--lock-type ReadOnly
Listing and Removing Locks
# List all locks in a resource group
az lock list \
--resource-group rg-prod-webapp-eastus \
--output table
# Remove a lock (you need this before you can delete the group)
az lock delete \
--name DoNotDelete \
--resource-group rg-prod-webapp-eastus
Warning: A ReadOnly lock on a storage account blocks writing data to it — not just configuration changes. Always test lock behavior in dev first.
Moving Resources Between Groups
Sometimes you realize a resource landed in the wrong group. Azure lets you move resources without downtime in many cases.
# Get the resource ID of the resource you want to move
RESOURCE_ID=$(az resource show \
--resource-group rg-dev-webapp-eastus \
--name stdevwebappeastus \
--resource-type Microsoft.Storage/storageAccounts \
--query id --output tsv)
# Move it to a different resource group
az resource move \
--destination-group rg-prod-webapp-eastus \
--ids $RESOURCE_ID
Not all resource types support moves. Check the Microsoft documentation on move support before attempting it in production.
ARM Templates vs Bicep — Infrastructure as Code
ARM templates are JSON files that define your infrastructure declaratively. They work, but they are verbose. Bicep is Microsoft's answer — a domain-specific language that compiles down to ARM JSON.
Here is the same storage account in both formats:
ARM Template (JSON):
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "stprodwebappeastus",
"location": "eastus",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2",
"tags": {
"Environment": "Production",
"Team": "Platform"
}
}
]
}
Bicep:
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'stprodwebappeastus'
location: 'eastus'
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
tags: {
Environment: 'Production'
Team: 'Platform'
}
}
Bicep is roughly 60% less code for the same result. If you are starting fresh with Azure IaC, go with Bicep.
Wrapping Up
Azure Resource Manager is not glamorous, but it is the foundation that separates a well-run cloud environment from chaos. Start with a solid naming convention, tag everything from day one, and lock your production resources before someone learns the hard way.
Next up: We will launch our first Azure Virtual Machine — picking the right VM size, connecting via SSH, and managing disks and availability.
