Back to CourseBeginner4 min read
Azure /

Resource Groups and Azure Resource Manager

Learn how Azure Resource Manager deploys everything, what a resource group is really for, and how resource IDs work.

Bishal Biswas

Bishal Biswas

Self Learner

Published on: October 1, 2026
Last updated: September 7, 2026
Beginner Topic

Everything goes through Resource Manager

Azure Resource Manager, usually just ARM, is the control plane. The portal, the CLI, PowerShell, Terraform and the SDKs all end up calling the same REST API.

Portal, CLI, PowerShell, SDK, Terraform
                 |
        Azure Resource Manager
                 |
        Resource providers
                 |
            Resources

That single path is why permissions, policy, locks and activity logs work consistently no matter how you make a change.

Resource providers

Each family of services is a provider, and a provider must be registered in a subscription before you can use it.

az provider list --query "[?registrationState=='Registered'].namespace" --output table
az provider show --namespace Microsoft.ContainerService --query registrationState
az provider register --namespace Microsoft.ContainerService

A first deployment of an unfamiliar service failing with “the subscription is not registered to use namespace” is common, and this is the fix. Registration is per subscription and takes a minute or two.

Resource groups

A resource group is a container. It is not a network boundary, not a security boundary by default, and not a billing boundary. It is a lifecycle and management boundary.

az group create --name rg-shop-prod-uks --location uksouth
az group list --output table
az group show --name rg-shop-prod-uks

The location of a resource group is where its metadata lives, not where its resources run. A resource group in uksouth can hold a virtual machine in westeurope. Keeping them aligned is tidy but not required.

What belongs in one

The useful rule: things that are deployed, updated and deleted together.

Good     one application's web app, database, storage and Key Vault
Good     a shared networking group with the virtual network and firewall
Bad      everything the company owns
Bad      one resource group per resource

Deleting a resource group deletes everything in it. That is either extremely convenient or a disaster, depending on whether you meant it.

az group delete --name rg-shop-dev-uks --yes --no-wait

This is the fastest way to clean up an experiment, and the fastest way to destroy a production environment. Which is why locks exist.

Locks

az lock create --name prevent-delete --lock-type CanNotDelete \
  --resource-group rg-shop-prod-uks

az lock create --name read-only --lock-type ReadOnly \
  --resource-group rg-shop-prod-uks

CanNotDelete allows changes but blocks deletion. ReadOnly blocks changes too, which sounds appealing and breaks more than expected: many services need to write to their own configuration to work.

Put a CanNotDelete lock on production resource groups. It has stopped more incidents than most controls.

Locks are inherited by child resources, and they apply to everyone including subscription owners. Removing one is deliberate, which is the point.

Resource IDs

Every resource has a unique path, and you will use these constantly.

/subscriptions/{subscriptionId}
  /resourceGroups/{resourceGroupName}
  /providers/{providerNamespace}/{resourceType}/{resourceName}
/subscriptions/0000.../resourceGroups/rg-shop-prod-uks/providers/Microsoft.Web/sites/shop-api
az webapp show --name shop-api --resource-group rg-shop-prod-uks --query id --output tsv
az resource show --ids /subscriptions/.../sites/shop-api

Role assignments, policy scopes, diagnostic settings and private endpoint targets all take a resource ID. Getting comfortable reading them pays off quickly.

Deployments

Every ARM operation is recorded as a deployment against a scope, and the history is genuinely useful when something changed unexpectedly.

az deployment group list --resource-group rg-shop-prod-uks --output table
az deployment group show --resource-group rg-shop-prod-uks --name main

Deployments can target four scopes:

Resource group   the usual case
Subscription     resource groups, policies, role assignments
Management group policy and access across many subscriptions
Tenant           rare, tenant wide settings

Deployment modes

Incremental  add and update what the template says, leave everything else. The default.
Complete     make the resource group match the template exactly, DELETING anything not in it

Complete mode is powerful and dangerous. It is how you guarantee no drift, and it is also how people delete a database that was created by hand and never added to the template.

Use --what-if before either, and always before Complete.

az deployment group what-if \
  --resource-group rg-shop-prod-uks \
  --template-file main.bicep \
  --parameters main.bicepparam

Tags

az group create --name rg-shop-prod-uks --location uksouth \
  --tags environment=production owner=platform-team costCentre=CC1234

az resource tag --tags environment=production \
  --ids /subscriptions/.../sites/shop-api

Tags are how you attribute cost, find things and drive automation. Resources do not inherit tags from their resource group automatically, which surprises people. Azure Policy can enforce inheritance, and that has its own topic.

Activity log

az monitor activity-log list \
  --resource-group rg-shop-prod-uks \
  --start-time 2026-09-01T00:00:00Z \
  --query "[].{Time:eventTimestamp, Op:operationName.value, By:caller, Status:status.value}" \
  --output table

Every control plane operation is logged for 90 days: who did what, when, and whether it succeeded. This is the first place to look when something changed and nobody admits to changing it.

Note it records control plane operations. Reading a blob or querying a database is data plane, and needs diagnostic settings to capture.

Quick recap

  • Every tool goes through Resource Manager, so permissions and logging are consistent.
  • A resource provider must be registered before its first use in a subscription.
  • A resource group is a lifecycle boundary: things deployed and deleted together.
  • A resource group’s location is where its metadata lives, not its resources.
  • Put a CanNotDelete lock on production resource groups.
  • Complete deployment mode deletes anything not in the template, so use --what-if.
  • The activity log answers “who changed this” for the last 90 days.
deploy.sh

# Every tool (portal, CLI, PowerShell, SDK, Terraform) goes through Azure
# Resource Manager, which is why permissions, policy, locks and the
# activity log behave consistently whatever you use.
# A RESOURCE PROVIDER must be registered before its first use. "The
# subscription is not registered to use namespace..." is fixed here.
az provider show --namespace Microsoft.ContainerService --query registrationState -o tsv
az provider register --namespace Microsoft.ContainerService
# A resource group is a LIFECYCLE boundary: things deployed, updated and
# deleted together. Its location is where the METADATA lives, not the
# resources, so a uksouth group can hold a westeurope VM.
az group create --name rg-shop-prod-uks --location uksouth \
  --tags environment=production owner=platform-team costCentre=CC1234
# Deleting a group deletes EVERYTHING in it. Fastest cleanup, and fastest
# way to destroy production. Which is why locks exist.
az lock create --name prevent-delete --lock-type CanNotDelete \
  --resource-group rg-shop-prod-uks
# CanNotDelete allows changes, blocks deletion. ReadOnly also blocks
# changes and breaks more than expected: many services write to their own
# configuration to function. Locks apply to owners too, by design.
# RESOURCE IDS are used by role assignments, policy scopes, diagnostic
# settings and private endpoints. Get comfortable reading them.
#   /subscriptions/{id}/resourceGroups/{rg}/providers/{ns}/{type}/{name}
az webapp show --name shop-api --resource-group rg-shop-prod-uks --query id -o tsv
# ALWAYS what-if before deploying, and especially before Complete mode.
#   Incremental (default): add and update, leave everything else alone
#   Complete: make the group MATCH the template, DELETING anything not in it
az deployment group what-if --resource-group rg-shop-prod-uks \
  --template-file main.bicep --parameters main.bicepparam
az deployment group list --resource-group rg-shop-prod-uks --output table
# "Who changed this?" for the last 90 days. Control plane only: reading a
# blob is data plane and needs diagnostic settings.
az monitor activity-log list --resource-group rg-shop-prod-uks \
  --start-time 2026-09-01T00:00:00Z \
  --query "[].{Time:eventTimestamp, Op:operationName.value, By:caller}" -o table
# Resources do NOT inherit tags from their resource group. Azure Policy
# can enforce that, and usually should.

Was this explanation helpful?

Your feedback helps us improve the content for everyone.