In the previous lesson you created a resource group and a storage account by clicking, and some questions were left hanging: what really is a resource group? Why does deleting it remove everything it contains? Why are tags not inherited? And how is it possible that the portal, the command line and a template all do exactly the same thing?
The answer to all of them is the same: Azure Resource Manager (ARM), Azure's control plane. Understanding ARM is what separates someone who "knows how to use the portal" from someone who understands Azure. This lesson is the most important one in the module conceptually, and its practical consequences — organization, governance, cost, protecting production — will stay with you right up to the last module.
Contents
- What Azure Resource Manager is
- The complete Azure hierarchy
- Resource providers and their registration
- The resource ID and how to read it
- Real criteria for grouping resources
- Resource locks: protecting production
- Moving resources between groups and subscriptions
- Tags: what they are really for
- Contoso Airlines' tagging scheme
- ARM templates and Bicep as a concept
- Subscription limits and quotas
- Common Mistakes and Tips
- Exercises
- Conclusion
- What Azure Resource Manager is
Azure Resource Manager is Azure's management service: the single door through which every operation to create, modify, read and delete resources passes. It is not a tool you use directly; it is the API behind all the tools.
graph TD
P[Azure portal] --> ARM
C[Azure CLI] --> ARM
PS[Azure PowerShell] --> ARM
S[SDKs: .NET, Python, Java...] --> ARM
T[ARM templates / Bicep / Terraform] --> ARM
R[Direct REST API] --> ARM
ARM["Azure Resource Manager<br/>authentication, RBAC authorization,<br/>validation, policy enforcement,<br/>orchestration and activity logging"]
ARM --> RP1[Provider: Microsoft.Compute]
ARM --> RP2[Provider: Microsoft.Storage]
ARM --> RP3[Provider: Microsoft.Web]
ARM --> RP4[Provider: Microsoft.Sql]
Practical consequences of this design, all of them verifiable:
- Complete consistency. Whatever you can do through the portal you can do through the CLI, and vice versa. If something appears in one tool and not in another, it is usually a matter of version, not capability.
- A single security point. RBAC permissions (module 4) and policies (module 4) are enforced in ARM, so they apply equally to every tool. You cannot "sidestep" a policy by using the CLI.
- Unified auditing. The activity log you saw in the previous lesson captures operations wherever they come from.
- Declarative deployments. ARM accepts templates that describe the desired state, works out the creation order and parallelizes whatever it can.
- Idempotent operations. Sending the same desired state twice does not create two resources: it converges on the same result. This property is the foundation of infrastructure as code.
One nuance that avoids future confusion: ARM governs the control plane (creating a storage account, changing its tier). The data plane (uploading a blob, running a SQL query) is served by each service through its own endpoint and its own permissions. Two planes, two access models.
- The complete Azure hierarchy
Four levels, from the broadest to the narrowest scope. Each one has a different purpose and is where certain things get applied.
graph TD
MG0[Root management group<br/>Contoso Airlines]
MG0 --> MG1[Management group<br/>Produccion]
MG0 --> MG2[Management group<br/>No produccion]
MG1 --> S1[Subscription<br/>Contoso Airlines - Produccion]
MG2 --> S2[Subscription<br/>Contoso Airlines - Desarrollo]
S1 --> RG1[rg-contoso-reservas-pro]
S1 --> RG2[rg-contoso-red-pro]
S2 --> RG3[rg-contoso-reservas-dev]
RG1 --> R1[app-contoso-reservas-pro]
RG1 --> R2[sttarjetascontosopro]
RG1 --> R3[sql-contoso-reservas-pro]
RG2 --> R4[vnet-contoso-pro]
| Level | What it is | What it is for | Applied here |
|---|---|---|---|
| Management group | A container of subscriptions, nestable up to 6 levels | Applying governance to many subscriptions at once | Policies, RBAC permissions inherited by every subscription it contains |
| Subscription | A billing unit and a container of resources | Separating environments, departments or clients; quota boundary | Billing, quotas, policies, permissions |
| Resource group | A logical container within a subscription | Organizing by lifecycle; joint deletion and deployment | Permissions, policies, locks, deployments |
| Resource | The actual instance (a VM, a database) | Doing the work | Permissions, locks, configuration |
Iron rules you have to memorize:
- Every resource belongs to exactly one resource group, and that group to exactly one subscription.
- Permissions and policies are inherited downwards. A permission granted at the subscription applies to all its groups and resources.
- Tags are NOT inherited. I insist because it is counterintuitive precisely because of the previous point. We will look at it in section 8.
- A resource group can contain resources from different regions. Its own region only says where the group's metadata is stored.
- Deleting a resource group deletes all its contents, with no exceptions and no recycle bin.
About management groups: Contoso does not need them yet with two subscriptions, but they are the right tool as soon as there are several. They let you, for example, apply a policy of "deployment is only allowed in European regions" to everything hanging off the Producción group, without repeating it subscription by subscription. They are developed in lesson 04-06 (Azure Policy).
- Resource providers and their registration
A resource provider is the ARM component that knows how to create and manage a family of resources. Its name takes the form Microsoft.<Family>:
| Provider | What it manages | Example resource types |
|---|---|---|
Microsoft.Compute |
IaaS compute | virtualMachines, disks, availabilitySets |
Microsoft.Storage |
Storage | storageAccounts |
Microsoft.Web |
App Service and Functions | sites, serverfarms |
Microsoft.Sql |
Azure SQL | servers, servers/databases |
Microsoft.Network |
Networking | virtualNetworks, networkSecurityGroups, publicIPAddresses |
Microsoft.KeyVault |
Key Vault | vaults |
Microsoft.Insights |
Monitoring | components, metricAlerts |
Each provider must be registered in the subscription before you can use it. The portal registers the provider automatically when you create the first resource of that family, which is why you normally never notice. But when you deploy through the CLI or with a template, an unregistered provider produces a cryptic error along the lines of "The subscription is not registered to use namespace 'Microsoft.X'".
# Lists the registration state of every provider in the active subscription.
az provider list --query "[].{Provider:namespace, State:registrationState}" --output table
# Manually registers a provider (an asynchronous operation: it can take a couple of minutes).
az provider register --namespace Microsoft.Storage
# Checks whether it is already registered.
az provider show --namespace Microsoft.Storage --query "registrationState" --output tsvProviders also determine which API versions are available and in which regions each resource type exists:
# Shows which regions the "storage account" type is available in.
# Useful for checking before deploying to an uncommon region.
az provider show --namespace Microsoft.Storage \
--query "resourceTypes[?resourceType=='storageAccounts'].locations" \
--output json
- The resource ID and how to read it
Every Azure resource has a unique, global identifier. It appears in logs, in error messages, in templates and in any automation, so you need to be able to read it at a glance.
/subscriptions/8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90/resourceGroups/rg-contoso-reservas-pro/providers/Microsoft.Storage/storageAccounts/sttarjetascontosopro
Broken down by segment:
| Segment | Value in the example | Meaning |
|---|---|---|
/subscriptions/ |
8f4c2b7a-...-4f90 |
The subscription identifier (GUID) |
/resourceGroups/ |
rg-contoso-reservas-pro |
The resource group that contains it |
/providers/ |
Microsoft.Storage |
The resource provider responsible for it |
| Resource type | storageAccounts |
The specific type within the provider |
| Name | sttarjetascontosopro |
The resource name |
Child resources nest the pattern. A database inside a SQL server:
/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-pro/providers/Microsoft.Sql/servers/sql-contoso-reservas-pro/databases/db-reservasAnd some resources live directly in the subscription, with no group (for example, a policy assignment at subscription level):
Getting a resource's ID from the CLI:
# Returns only the ID, in plain text, ready to use in another command or script.
az storage account show \
--name sttarjetascontosodev \
--resource-group rg-contoso-reservas-dev \
--query id --output tsv
- Real criteria for grouping resources
The question everybody asks: "how many resource groups do I create, and what do I put in each one?". The professional answer is not simply "one per project".
The main criterion: shared lifecycle
Put in the same group whatever is born and dies together. If you are going to delete the booking application, do you want the corporate virtual network to disappear too? No. Then they do not go in the same group.
Secondary criteria
| Criterion | Rule | Example at Contoso |
|---|---|---|
| Environment | Never mix production and development in the same group | rg-contoso-reservas-pro and rg-contoso-reservas-dev |
| Permissions | Resources that share who can administer them go together, because RBAC is conveniently assigned at group level | Only Marta administers the corporate network: it gets its own group |
| Lifecycle | The shared and long-lived, separate from the ephemeral | The network and Key Vault last for years; a campaign application, weeks |
| Billing | Groups are a natural axis for cost analysis, complemented by tags | Cost by group in Cost Management (module 8) |
Antipatterns you will see in real companies
- "Everything together": a single group with 300 resources. Impossible to grant permissions sensibly, impossible to delete anything safely.
- "One per resource": 300 groups with one resource each. All the management overhead and none of the benefits.
- "By resource type": one group for all the VMs, another for all the databases. It sounds tidy and it is a disaster: nothing shares a lifecycle and you cannot delete a project without going piece by piece.
Contoso Airlines' organization
| Resource group | Contents | Lifecycle |
|---|---|---|
rg-contoso-reservas-pro |
Website, API, database and storage of the platform in production | It lives as long as the product lives |
rg-contoso-reservas-dev |
The same components in their development version | It can be recreated in full whenever convenient |
rg-contoso-red-pro |
Virtual network, subnets, VPN gateway | Long-lived, shared by several applications |
rg-contoso-seguridad-pro |
Key Vault, Log Analytics workspace | Long-lived, with very restricted permissions |
The last three will be filled in during modules 2, 4 and 7. Notice that the network is separate: it is exactly the kind of shared, long-lived resource that must not die with an application.
- Resource locks: protecting production
A lock prevents destructive operations even for someone with owner permissions. It is the safety net against human error, not against attack.
| Lock type | What it prevents | What it allows |
|---|---|---|
| CanNotDelete | Deleting the resource | Reading and modifying |
| ReadOnly | Deleting and modifying | Reading only |
They can be applied to a subscription, a resource group or an individual resource, and they are inherited downwards: a lock on the group protects all its resources.
# CanNotDelete lock over the entire production group.
# --notes documents why it exists; your future self will thank you.
az lock create \
--name "no-borrar-produccion" \
--lock-type CanNotDelete \
--resource-group rg-contoso-reservas-pro \
--notes "Protects the sales platform. Ask Marta Rios to have it removed."
# List the existing locks in a group.
az lock list --resource-group rg-contoso-reservas-pro --output table
# Delete a lock (requires a specific permission over Microsoft.Authorization/locks).
az lock delete --name "no-borrar-produccion" --resource-group rg-contoso-reservas-proFrom the portal: resource or group → Locks → Add.
Side effects you need to know about
ReadOnly is more aggressive than it looks, and it causes surprises:
- A
ReadOnlylock on a group containing a storage account prevents listing the access keys, because that operation is technically a write (listKeys). Applications that were working stop working. - A
ReadOnlylock on a virtual machine prevents starting or stopping it. - Locks affect the control plane, not the data plane: with
CanNotDeleteon a storage account, nobody can delete the account, but anyone with data permissions can still delete the blobs inside it. There are other protections for that (retention, soft delete), covered in module 2.
Recommended practice at Contoso: CanNotDelete on all production groups and on stateful resources (databases, storage accounts, Key Vault). ReadOnly only in very well justified cases and after testing first.
- Moving resources between groups and subscriptions
Resources can be moved, but with rules. It is a common operation when you reorganize or when a project moves from development to production.
# Move a storage account to another resource group in the same subscription.
# --ids accepts one or more full IDs, separated by spaces.
az resource move \
--destination-group rg-contoso-reservas-pro \
--ids "/subscriptions/8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90/resourceGroups/rg-contoso-reservas-dev/providers/Microsoft.Storage/storageAccounts/sttarjetascontosodev"Limits you should know before you try it
- Not every resource can be moved. There is an official list by resource type; always check first. Common examples of restrictions: VPN gateways, some networking configurations and certain resources with regional dependencies.
- Moving does not change the region. A resource in West Europe stays in West Europe even if you move it to a group whose region is a different one. To "move it to another region" you have to recreate or replicate it.
- During the move, the source and destination groups are locked for writes until it finishes.
- Dependent resources have to be moved together. A VM needs to go with its network interface, its disks and its public IP.
- The identifiers change, because the ID includes the group. Any script, alert or policy assignment pointing at the old ID will stop working.
- Permission assignments and policies do not travel with the resource: those of the new scope apply instead. Review access after moving.
- Moving between subscriptions also requires both to be in the same Microsoft Entra ID tenant and the providers to be registered in the destination one.
Practical tip: for an important move, validate first:
# Simulates validation of the move without executing it (validateMoveResources endpoint).
# If it returns an error, it tells you exactly which resource cannot be moved and why.
az resource invoke-action \
--action validateMoveResources \
--ids "/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-dev" \
--request-body '{
"resources": ["/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-dev/providers/Microsoft.Storage/storageAccounts/sttarjetascontosodev"],
"targetResourceGroup": "/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-pro"
}'
- Tags: what they are really for
A tag is a name-value pair attached to a subscription, a resource group or a resource. Each resource accepts up to 50 tags.
Tags look like decoration right up until the company has 400 resources. Then they become the only way to answer critical questions:
| Real question | Tag that answers it |
|---|---|
| How much does the bookings project cost us compared with the loyalty one? | proyecto |
| Which cost center do we charge this invoice to? | centro-coste |
| Who do I call if this machine fails on a Sunday? | propietario |
| Which resources are production and must not be touched? | entorno |
| What can be switched off at night to save money? | horario or entorno |
| Which resources contain personal data? | clasificacion-datos |
In other words: cost, ownership and compliance. Without tags, the module 8 conversation with Nuria Peña ("why did we spend EUR 4,000 this month?") is impossible to hold, because Cost Management will be able to tell you that you are spending on virtual machines, but not whose they are.
The inheritance that does NOT exist
Tags are not inherited. A tag placed on a resource group does not appear on the resources it contains. It is Azure's number one trap for people coming from the portal, because everything else (permissions, policies, locks) is inherited.
Why does it matter so much? Because cost reports are aggregated by the tag on the resource that generates the charge. If you only tagged the group, your reports by centro-coste will come out empty.
The three real solutions:
- Tag at creation time, always, in the Tags tab or with the
--tagsparameter. It is the basic discipline. - Azure Policy with the
Modifyeffect: a policy that automatically adds to the resource the tag inherited from the group. This is the professional solution and it is covered in lesson 04-06. - Infrastructure as code: the tags are written into the template and applied automatically on every deployment (lesson 05-06).
Other operational details:
- Tag names are case-insensitive for operations, but values do preserve case exactly as you write them. Pick a format and stick to it:
produccionandProduccionwill be counted as different values in reports. - Do not put secrets in tags: they are visible to anyone with read permission.
- Some resource types do not accept tags; they are a minority, but they exist.
Working with tags from the CLI
# Apply (or replace) the complete set of tags on a resource group.
# Careful: "az tag create" REPLACES all existing tags.
az tag create \
--resource-id "/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-pro" \
--tags entorno=produccion proyecto=contoso-reservas centro-coste=CC-1042 [email protected]
# Add or update tags WITHOUT deleting the ones that already exist.
az tag update \
--resource-id "/subscriptions/{sub}/resourceGroups/rg-contoso-reservas-pro" \
--operation Merge \
--tags criticidad=alta
# List every resource carrying a specific tag, across the whole subscription.
az resource list --tag proyecto=contoso-reservas --output table
# View a specific resource's tags in JSON format.
az resource show \
--name sttarjetascontosodev \
--resource-group rg-contoso-reservas-dev \
--resource-type "Microsoft.Storage/storageAccounts" \
--query tagsTypical output of the last command:
{
"centro-coste": "CC-1042",
"entorno": "desarrollo",
"propietario": "[email protected]",
"proyecto": "contoso-reservas"
}And in the portal: resource or group → Tags section → add name/value pairs → Apply. To tag many resources at once, use All resources, tick the boxes and click Assign tags.
- Contoso Airlines' tagging scheme
Marta Ríos publishes this scheme as an internal standard. It is mandatory on every resource and we will use it throughout the course.
| Tag | Mandatory | Allowed values | What for |
|---|---|---|---|
entorno |
Yes | produccion, desarrollo, pruebas |
Distinguishing what can be touched and what cannot; the basis of the policies |
proyecto |
Yes | contoso-reservas (and future projects) |
Grouping cost by product |
centro-coste |
Yes | CC-1042 (bookings) |
Accounting allocation for Nuria Peña |
propietario |
Yes | A person's corporate email address | Knowing who to call |
criticidad |
Recommended | alta, media, baja |
Prioritizing during incidents and deciding on redundancy |
horario |
Optional | 24x7, laborable |
Enabling automatic overnight shutdown (module 7) |
Rules of the scheme:
- All values in lowercase and without accents, so that reports group properly.
propietariois always a person, not a department: departments do not answer the phone.- Resources missing the four mandatory tags are flagged as non-compliant and will appear in the compliance report once we roll out Azure Policy (lesson 04-06).
Applied to the resources you already know:
# Full tagging of Contoso's production group.
az group create \
--name rg-contoso-reservas-pro \
--location westeurope \
--tags entorno=produccion \
proyecto=contoso-reservas \
centro-coste=CC-1042 \
[email protected] \
criticidad=alta \
horario=24x7
- ARM templates and Bicep as a concept
You already know that ARM accepts declarative descriptions of the desired state. Those descriptions are ARM templates (JSON) and Bicep (a more readable language that compiles to JSON). Here we only cover the concept: the full treatment is in lesson 05-06.
An ARM template in JSON, reduced to the bare minimum:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"accountName": { "type": "string" }
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('accountName')]",
"location": "westeurope",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2",
"tags": {
"entorno": "desarrollo",
"proyecto": "contoso-reservas",
"centro-coste": "CC-1042",
"propietario": "[email protected]"
}
}
]
}The same thing in Bicep, far more readable:
param accountName string
resource account 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: accountName
location: 'westeurope'
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
tags: {
entorno: 'desarrollo'
proyecto: 'contoso-reservas'
'centro-coste': 'CC-1042'
propietario: '[email protected]'
}
}Three ideas to hold on to:
- It is declarative: you describe the result, not the steps. ARM works out what has to be created, modified or left alone.
- It is idempotent: deploying five times leaves the same result as deploying once.
- It is versionable: the template lives in Git, is reviewed in a pull request and is deployed from a pipeline (module 5).
A learning trick you already saw in the portal: on any resource, the Automation → Export template section gives you the JSON of the existing resource. It is the best way to learn the syntax from something that already works.
- Subscription limits and quotas
Azure is not infinite. Every subscription has limits (some fixed, some raisable by opening a support request). Finding out about a limit in the middle of an urgent deployment is an unpleasant and avoidable experience.
| Item | Indicative limit per subscription | Raisable |
|---|---|---|
| Resource groups | 980 | No |
| Resources per resource group | 800 per resource type (varies) | Some are |
| Virtual machine cores per region | A low initial quota (often 10-20 on new accounts), per VM family | Yes, it is the most common request |
| Storage accounts per region | 250 | Yes |
| Virtual networks | 1,000 | Yes |
| Standard public IP addresses | 1,000 | Yes |
| Tags per resource | 50 | No |
| Deployments per resource group (history) | 800 | No (the history gets pruned) |
The exact figures change; always check "Azure subscription and service limits, quotas, and constraints" in the official documentation.
The quota that causes beginners the most trouble is VM cores per region and family: a new free account may have room for very few machines, and the deployment error ("Operation could not be completed as it results in exceeding approved quota") is disconcerting because it looks like a deployment failure and it is not.
Checking your real quota:
# Compute core usage and limit in West Europe.
# CurrentValue = what you are already using; Limit = your current ceiling.
az vm list-usage --location westeurope --output table
# Filter to only the families where you are already consuming something.
az vm list-usage --location westeurope \
--query "[?currentValue > \`0\`].{Resource:localName, Used:currentValue, Limit:limit}" \
--output tableTo raise a quota: portal → Subscriptions → your subscription → Usage + quotas → Request increase. It is usually resolved in minutes or hours.
Common Mistakes and Tips
- Creating a catch-all resource group. If you do not know why a resource is in there, it is in the wrong place. Group by lifecycle.
- Believing tags are inherited. They are not. Tag each resource at creation time or automate it with Azure Policy.
- Tagging with inconsistent values.
Produccion,produccion,PROandprodare four different values in cost reports, and the report becomes useless. Fix the vocabulary and stick to it. - Applying
ReadOnlycasually. It can break applications by preventing operations that look like reads but are writes (such as listing keys). Test in development first. - Trusting
CanNotDeleteto protect data. It protects the resource, not its contents. The blobs inside can still be deleted. - Moving resources without checking the supported list or warning people that the IDs will change. Afterwards, review alerts, scripts and permissions.
- Ignoring provider registration when automating in a new subscription. Register the providers at the start of the script.
- Discovering the core quota in the middle of a deployment. Check it beforehand with
az vm list-usage. - Cost tip: resource groups, tags and locks are free. Use them without hesitation: they are the cheapest governance infrastructure there is, and the one that saves the most money in the medium term.
Exercises
Exercise 1: Designing the resource organization
Contoso Airlines is also going to launch a loyalty program ("Contoso Miles"), with its own website, its own database and its own cost center CC-2077, owned by Diego Salas. It will share the existing corporate virtual network and Key Vault.
- Propose the resource groups needed and what each one contains.
- Justify why the virtual network must not be in the project's group.
- Write the set of mandatory tags for the Contoso Miles production group.
Exercise 2: Reading and building identifiers
- Break this ID down into its five parts and state what each one is:
/subscriptions/8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90/resourceGroups/rg-contoso-red-pro/providers/Microsoft.Network/virtualNetworks/vnet-contoso-pro
- Build the ID a subnet called
snet-webinside that same virtual network would have. - Write the CLI command that returns only the ID of the
sttarjetascontosoprostorage account in therg-contoso-reservas-progroup.
Exercise 3: Protecting and querying
- Write the commands to create the
rg-contoso-reservas-progroup in West Europe with the four mandatory tags and apply aCanNotDeletelock to it. - Write the command that lists all the resources in the subscription tagged with
proyecto=contoso-reservas. - Marta tries to delete the group from the portal and it fails. Explain what is happening and what she must do to succeed legitimately.
- Diego applies
ReadOnlyto the development group and, the next day, the test application stops starting because it cannot read the storage account keys. Explain why.
Solutions
Solution 1:
- Proposed groups:
| Group | Contents |
|---|---|
rg-contoso-millas-pro |
Loyalty website, database and storage in production |
rg-contoso-millas-dev |
The same components in development |
rg-contoso-red-pro (already exists) |
Virtual network and subnets, shared |
rg-contoso-seguridad-pro (already exists) |
Key Vault and Log Analytics, shared |
-
The virtual network has a different lifecycle: it outlives projects and is shared by several applications. If it lived inside the project's group, retiring Contoso Miles would delete everyone's network, and on top of that you would have to give the developers permissions over an infrastructure resource they should not be administering.
-
Tags for the Miles production group:
az group create \
--name rg-contoso-millas-pro \
--location westeurope \
--tags entorno=produccion \
proyecto=contoso-millas \
centro-coste=CC-2077 \
[email protected]Solution 2:
- Breakdown:
| Part | Value |
|---|---|
| Subscription | 8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90 |
| Resource group | rg-contoso-red-pro |
| Provider | Microsoft.Network |
| Resource type | virtualNetworks |
| Name | vnet-contoso-pro |
- The subnet ID (a child resource, so type/name nest):
/subscriptions/8f4c2b7a-1d3e-4a55-9c11-0a7b6e2d4f90/resourceGroups/rg-contoso-red-pro/providers/Microsoft.Network/virtualNetworks/vnet-contoso-pro/subnets/snet-web
- Command:
az storage account show \
--name sttarjetascontosopro \
--resource-group rg-contoso-reservas-pro \
--query id --output tsvSolution 3:
- Creation and lock:
az group create \
--name rg-contoso-reservas-pro \
--location westeurope \
--tags entorno=produccion proyecto=contoso-reservas \
centro-coste=CC-1042 [email protected]
az lock create \
--name "no-borrar-produccion" \
--lock-type CanNotDelete \
--resource-group rg-contoso-reservas-pro \
--notes "Sales platform in production"- Listing by tag:
-
The
CanNotDeletelock prevents deletion even though Marta is the owner. To delete it legitimately she must first remove the lock (az lock deleteor portal → Locks), which requires permission overMicrosoft.Authorization/locks, and then delete the group. That extra, deliberately inconvenient step is exactly the point: it forces a conscious decision. -
Because
ReadOnlyblocks any write operation on the control plane, and retrieving a storage account's keys is implemented as thelistKeysaction, which ARM classifies as a write. The application cannot retrieve the keys and fails to start. The fix: remove theReadOnlylock (useCanNotDeleteinstead) and, better still, stop using keys and move to managed identities (lesson 04-02).
Conclusion
Azure Resource Manager is Azure's single control plane: the portal, the CLI, PowerShell, the SDKs and templates all talk to the same API, and that is why permissions, policies and auditing are consistent no matter where you come from. On top of it sits the hierarchy management group → subscription → resource group → resource, where permissions and policies are inherited downwards... and tags are not.
You have learned to read a resource ID, to register providers, to group resources by lifecycle and environment (avoiding "everything together" and "one per type"), to protect production with locks — knowing the traps of ReadOnly and the limits of CanNotDelete — to move resources in the knowledge that IDs change and permissions do not travel, and to design a serious tagging scheme: Contoso Airlines', with entorno, proyecto, centro-coste and propietario as mandatory. You have also seen what ARM templates and Bicep are about, and where the limits and quotas are that are worth checking before rather than after.
You will have noticed that in this lesson the examples were, almost all of them, commands. That is no accident: governing dozens of resources with tags and locks by clicking does not scale. In the last lesson of the module, Azure CLI, PowerShell and Cloud Shell, you will learn to drive Azure from the command line and you will write your first complete, parameterized and idempotent script, which creates the whole base of Contoso Airlines and knows how to clean up after itself.
Azure Course
Module 1: Introduction to Azure
- What Is Azure?
- Service Models, Regions and Availability Zones
- Creating and Setting Up Your Azure Account
- A Tour of the Azure Portal
- Azure Resource Manager: Subscriptions, Resource Groups and Tags
- Azure CLI, PowerShell and Cloud Shell
Module 2: Core Azure Services
- Azure Virtual Machines
- Compute Scaling and High Availability
- Azure App Service
- Azure Storage: Blobs, Files, Queues and Tables
- Azure Networking: Virtual Networks, Subnets and NSGs
- Hybrid Connectivity and Global Delivery
Module 3: Azure Databases
- Choosing the Right Data Service
- Azure SQL Database
- Azure Cosmos DB
- Azure Database for MySQL
- Azure Database for PostgreSQL
- Data Analytics: Data Lake, Data Factory and Synapse
Module 4: Security in Azure
- Microsoft Entra ID and Identity Management
- RBAC and Managed Identities
- Azure Key Vault
- DDoS Protection and Web Application Firewall
- Microsoft Defender for Cloud
- Governance and Compliance with Azure Policy
Module 5: Azure DevOps
- Introduction to Azure DevOps
- Azure Repos
- Azure Pipelines: Continuous Integration
- Continuous Deployment with Environments and Approvals
- Azure Artifacts
- Infrastructure as Code with Bicep
Module 6: Advanced Azure Services
- Containers in Azure: Container Registry and Container Apps
- Azure Kubernetes Service (AKS)
- Azure Functions
- Azure Logic Apps
- Messaging and Events: Service Bus, Event Grid and Event Hubs
- Azure AI Services
Module 7: Monitoring and Management
- Azure Monitor: Metrics, Alerts and Dashboards
- Log Analytics and KQL Queries
- Application Insights
- Azure Automation and Runbooks
- Backup and Disaster Recovery
Module 8: Cost Management and Optimization
- Pricing Calculator and Cost Estimation
- Azure Cost Management: Analysis, Budgets and Alerts
- Reservations, Savings Plans and Azure Hybrid Benefit
- Azure Advisor
- Optimization Strategies and FinOps Culture
