Build an Azure Governance Sandbox
Build an Azure governance hierarchy with management groups and protective locks.
Introduction
30 Second Summary
Cloud accounts become difficult to manage when every project lands in one unstructured pile. Clear boundaries make it easier to see where each environment belongs.
In this project, you will organize an active Microsoft Azure subscription into a hierarchy of management groups and resource groups. You will also prove how a management lock protects a production scope by blocking deletion.
What You'll Build
You will see a clear Azure governance tree that leads from the Tenant root group to a sandbox subscription above separate development and production groups.
By the end of this project, you'll have:
- A visible parent-child scope path that you can trace in the Azure portal from the Tenant root group through nw-hierarchy-lab and nw-sandbox to your active subscription.
- Clear development and production boundaries that you can demonstrate with exampledevgroup and exampleresourcegroup.
- A protected production scope that remains in place after an attempted deletion. Azure CLI output confirms its CanNotDelete lock.
- Secret Mission: Add a sibling production management group that is ready to receive a future production subscription.
Are there any prerequisites?
Bring a personal Microsoft account plus a phone number and a non-prepaid credit or debit card if Azure asks for verification. This walkthrough assumes Cursor and Azure CLI are already installed on macOS 13 or later.
Before We Start
This is your moment to commit to the governance sandbox you are about to build. Locking in how each scope contributes gives every later action a clear purpose.
Activate and Verify Your Azure Environment
Your governance sandbox needs an active Azure subscription before it can hold resource groups. The subscription supplies the billing boundary beneath the sandbox management group.
This step confirms that boundary in the portal. It also connects the Azure CLI in Cursor to the same subscription.
In this step, get ready to:
- Activate an Azure subscription for the lab.
- Confirm that Azure CLI version 2.91.0 is installed.
- Verify Azure CLI authentication against the active subscription.
Activate an Azure subscription
Resource groups require a subscription because every Azure resource belongs to one. An active subscription gives this lab its billing boundary.
Entering a card can feel like a commitment. This lab creates no compute, storage, networking, or other usage-based workload resources, so its expected Azure cost is $0.
- Navigate to the official Azure account page in your browser.
- Select Azure free account if the page says you are eligible.
- Select Pay as you go if Azure free account is unavailable.
- Complete the About you stage.
- Complete the Identity verification by phone stage.
- Complete the Identity verification by card stage.
- Complete the Agreement stage.
What does the subscription add?
A subscription is an agreement with Microsoft to use Azure cloud services. It acts as the boundary that contains resource groups.
Later steps place this subscription beneath your sandbox management group. That connection gives your resource groups a path through the governance hierarchy.
- Return to the Azure portal from earlier.
- Enter Subscriptions in the portal search bar.
- Select Subscriptions from the search results.
- Check the lab subscription's status on the Subscriptions page.
You'll see the subscription listed as active. That billing boundary is now ready to support your governance sandbox.
Subscription missing?
- Confirm that you completed every sign-up stage through the agreement.
- Check that the Azure portal uses the same Microsoft account that completed the sign-up.
Ask for help if the subscription still does not appear: Help me diagnose why my new Azure subscription is missing from the Subscriptions page.
Confirm the Azure CLI version
This project uses Azure CLI version 2.91.0 so later commands match the documented release. A version check makes your terminal environment predictable before authentication.
- Press Cmd+Space on macOS or the Windows key on Windows to open system search.
- Type Cursor in the search field.
- Press Enter to start Cursor.
- Open Cursor's integrated terminal from the top menu.
- Check the installed Azure CLI version by running this command:
az version
What does this command show?
The command reports the Azure CLI version installed in Cursor's terminal. Find the version value before choosing the matching path below.
✔️ The version matches
Azure CLI reports version 2.91.0. Your command-line tool now matches the version used throughout this project.
ⓧ The version differs
Your existing Azure CLI installation needs an in-tool update before you continue. The update also refreshes installed extensions by default.
- Upgrade Azure CLI and check the version again by running these commands:
az upgrade
az version
What do these commands do?
- The first command updates your existing Azure CLI installation.
- The second command reports the installed version after the update.
Respond to any upgrade confirmation in the terminal so the update can continue.
You'll see version 2.91.0 after the update completes.
Upgrade not completing?
- Check that the terminal still has an internet connection.
- Confirm that the upgrade process has finished before checking the version again.
Get help with the update: Help me troubleshoot my Azure CLI upgrade.
Sign in and verify the active account
Azure CLI needs an authenticated session before it can read your subscription context. Interactive sign-in opens browser authentication for the Azure account connected to the terminal.
- Start interactive Azure authentication by running this command:
az login
What does this command do?
The command connects Azure CLI to Azure through an interactive sign-in flow. Successful authentication returns account details to Cursor's terminal.
- Complete browser authentication with the Microsoft account that owns your active subscription.
- Return to Cursor's terminal after authentication completes.
You'll see account information in the terminal once Azure CLI accepts the sign-in.
Subscription missing after sign-in?
- Confirm that browser authentication used the same Microsoft account as the Azure portal.
- Check that the active subscription appears on the portal's Subscriptions page.
Ask for help with the account context: Help me troubleshoot my Azure CLI sign-in.
Before you run the final check, predict whether the terminal account matches the active subscription you confirmed in the portal.
- Reveal the current default subscription by running this command:
az account show --output table
What does this command confirm?
- The table shows the current default subscription for your authenticated Azure CLI session.
- The subscription ID identifies the active billing boundary.
- The tenant ID identifies the directory that contains the subscription.
You'll see one row for the current default subscription. The row includes its subscription ID and tenant ID.
- Record the subscription ID as <active-subscription-id>.
- Record the tenant ID as <tenant-id>.
- Switch back to the Subscriptions page from earlier.
- Match the portal subscription ID to <active-subscription-id>.
You've closed the loop between the portal and terminal. Your active boundary is subscription <active-subscription-id> in tenant <tenant-id>.
Your Azure environment now has a verified billing boundary with an authenticated CLI context. Next, you'll build the management group branch above this subscription.
Build the Management Group Branch
Your active subscription is ready. It still sits directly beneath the Tenant root group.
The sandbox needs a management group branch before the hierarchy can show separate governance scopes. In this step, you'll use the Azure portal to create that branch.
In this step, get ready to:
- Locate the Tenant root group that currently holds your active subscription.
- Create nw-hierarchy-lab with nw-sandbox beneath it.
- Test whether a resource group can be added directly beneath nw-sandbox.
Find the Tenant root group
Every Microsoft Entra tenant has one root management group. New subscriptions default to that root group.
- Switch back to the Azure portal tab from earlier.
- Select All services from the portal menu.
- Select Management + governance.
- Select Management Groups.
- Locate the Tenant root group at the top of the hierarchy.
- Confirm that <active-subscription-id> appears directly beneath it.
You'll see <active-subscription-id> directly beneath the Tenant root group. The learner-created branch is absent.
Why start in the portal?
Management groups are about placement. The hierarchy tree keeps every parent-child relationship visible.
The Azure CLI is useful for independent checks. The portal gives you the mental map first.
Create the hierarchy branch
Each management group has a directory-unique ID that cannot change after creation. Its display name can change later.
- Select + Add management group above the hierarchy.
You'll see a creation form for the first management group.
- Enter nw-hierarchy-lab in the management group ID field.
- Enter NextWork Hierarchy Lab in the display name field.
Azure can spend up to 15 minutes initializing its management-groups service during this first creation.
- Select Save to create the management group.
- Wait for the portal notification to confirm completion.
That first branch point is live. NextWork Hierarchy Lab now gives your sandbox a dedicated parent scope.
- Open nw-hierarchy-lab from the hierarchy.
The management group page now scopes every new child beneath nw-hierarchy-lab.
- Select Create.
- Select Create new.
You'll see a form for a child management group.
- Enter nw-sandbox in the management group ID field.
- Enter Sandbox in the display name field.
The form now identifies the sandbox scope that belongs beneath NextWork Hierarchy Lab.
- Select Save to create the child management group.
- Wait for the portal to show nw-sandbox beneath nw-hierarchy-lab.
You now have a two-level branch beneath the Tenant root group. The sandbox scope is ready for its boundary test.
Still waiting for the branch?
- Refresh the browser after the completion notification appears.
- Confirm that the portal tenant matches <tenant-id>.
- Ask your tenant administrator to check hierarchy protection if the creation control is unavailable.
Help me troubleshoot management group creation in the Azure portal.
Test the child boundary
The new branch is ready for a child-scope test. The available actions reveal which objects Azure permits at this level.
- Predict which child types Azure offers beneath nw-sandbox.
- Refresh the Management Groups view.
- Open nw-hierarchy-lab.
You'll see nw-sandbox beneath nw-hierarchy-lab.
- Open nw-sandbox.
- Select Create.
- Check the available choices for a way to add a resource group directly.
You'll see ways to create a management group or add a subscription. No resource-group action is available.
Why is the resource-group option missing?
Only management groups and subscriptions can become children of another management group. This boundary keeps governance scopes above workload containers.
Resource groups belong inside subscriptions. The subscription becomes the bridge between the management-group hierarchy and each workload lifecycle.
- Return to the Management Groups view.
- Confirm that <active-subscription-id> remains directly beneath the Tenant root group.
The final view proves that the branch exists without moving the subscription. It also proves that nw-sandbox cannot contain a resource group directly.
You've built the branch and proved its boundary. Next, you'll place the active subscription beneath nw-sandbox.
Place the Subscription at the Right Scope
Your management group branch is ready. However, the active subscription still sits directly beneath the Tenant root group.
A management group cannot contain a resource group directly. Placing the subscription beneath nw-sandbox creates the bridge to every future resource group.
In this step, get ready to:
- Move the active subscription beneath nw-sandbox.
- Trace the hierarchy from Tenant root group to the subscription.
- Verify the hierarchy from Cursor's terminal.
Move the subscription beneath the sandbox
A subscription can have only one parent. The Azure portal uses the recorded subscription ID to distinguish the boundary you need to move.
- Switch back to the Management Groups view in the Azure portal.
- Select nw-sandbox.
- Select Add subscription.
- Select the subscription with ID <active-subscription-id>.
- Select Save.
Azure can cache hierarchy changes for up to 30 minutes. A short delay here is expected.
- Refresh the Management Groups view.
You should see subscription <active-subscription-id> beneath nw-sandbox.
That is the bridge in place. The subscription now connects the sandbox scope to future resource groups.
Subscription still under the root group?
- Refresh the browser to clear the current hierarchy view.
- Wait for the hierarchy cache to update if the move has just completed.
- Confirm that you selected subscription <active-subscription-id>.
Help me check why my Azure subscription has not appeared beneath nw-sandbox.
Trace the hierarchy downward
A parent-child scope controls how governance flows through the hierarchy. Azure RBAC assignments at a parent scope can inherit downward.
Azure Policy assignments can follow the same hierarchy. Tracing the path shows which parent scopes can affect your subscription.
- Expand Tenant root group in the Management Groups view.
- Expand nw-hierarchy-lab.
- Expand nw-sandbox.
- Select the subscription with ID <active-subscription-id>.
- Say this path aloud: Tenant root group, nw-hierarchy-lab, nw-sandbox, subscription <active-subscription-id>.
You should be able to follow one continuous path from Tenant root group to the active subscription.
How does inheritance follow this path?
An assignment on nw-hierarchy-lab can flow through nw-sandbox to the subscription.
Future resource groups inside that subscription can receive inherited assignments from the same path.
Verify the tenant context in Cursor
The portal gives you a visual hierarchy. Azure CLI gives you an independent view from the authenticated tenant.
The Cursor terminal from Step 1 remains signed in. These commands let you compare the management-group tenant context with the active subscription.
- Switch back to Cursor's terminal from Step 1.
- List the management groups in the current tenant by running this command:
az account management-group list
What does this check prove?
- This command lists every management group in the current tenant. Seeing both lab IDs confirms that Azure CLI is reading the tenant where you built the hierarchy.
You should see entries for nw-hierarchy-lab and nw-sandbox. The management-group tenant context should show <tenant-id>.
Missing the lab management groups?
- Confirm that you are using the authenticated terminal session from Step 1.
- Refresh the portal hierarchy if the branch is visible there but absent from the command output.
Help me find my management groups in Azure CLI.
Before the final check, predict whether the active subscription's tenant ID matches the management-group tenant context.
- Show the active subscription in a table by running this command:
az account show --output table
What does this check prove?
- This command shows the current default subscription in table output. The subscription ID confirms which Azure boundary the terminal is using.
You should see subscription <active-subscription-id> as the active subscription. Its tenant ID should be <tenant-id>.
Do the tenant IDs differ?
- Confirm that the subscription row contains <active-subscription-id>.
- Return to the browser authentication flow from Step 1 if Cursor is connected to another tenant.
- Refresh the Management Groups view if the portal still shows an older hierarchy.
Help me reconcile my Azure tenant context.
You have completed the scope bridge. Azure CLI now confirms the same tenant context as the portal hierarchy.
Your subscription now connects governance scopes to workload lifecycles. Next up, you will add separate development and production resource groups to expose the risk of an unprotected lifecycle container.
Add Resource Groups and Expose the Risk
Your active subscription now sits beneath nw-sandbox. This gives your governance branch a boundary that can hold workload scopes.
An Owner can still delete an unprotected resource group inside that boundary. This step makes that lifecycle risk visible before you add protection.
In this step, get ready to:
- Create exampledevgroup for development.
- Create exampleresourcegroup for production.
- Compare each group's subscription scope before testing the deletion risk.
Create development and production resource groups
Resource groups collect Azure resources that share a lifecycle. This lab uses West US for each group's metadata.
What does the region control?
A resource group's region stores metadata about the group. Resources inside the group can use different regions.
- Return to the Microsoft Azure portal tab from earlier.
- Select Resource groups.
- Select Create.
- Choose <active-subscription-id> in the Subscription field.
- Enter exampledevgroup in the Resource group field.
- Select West US in the Region field.
- Select Review + Create.
- Select Create once validation completes.
You'll see a creation confirmation for exampledevgroup.
- Return to the Resource groups page.
- Select Create.
- Choose <active-subscription-id> in the Subscription field.
- Enter exampleresourcegroup in the Resource group field.
- Select West US in the Region field.
- Select Review + Create.
- Select Create once validation completes.
- Return to the Resource groups page.
Good progress. You'll see exampledevgroup plus exampleresourcegroup listed under your active subscription.
Compare resource group scopes
Azure gives every resource group a resource ID that describes its full scope. The subscription segment identifies the group's parent boundary.
- Return to the Resource groups page.
- Open exampledevgroup.
- Select Properties.
- Locate the resource ID in the Properties view.
- Confirm its value matches /subscriptions/<active-subscription-id>/resourceGroups/exampledevgroup.
The path places exampledevgroup directly beneath your active subscription.
- Return to the Resource groups page.
- Open exampleresourcegroup.
- Select Properties.
- Confirm its resource ID matches /subscriptions/<active-subscription-id>/resourceGroups/exampleresourcegroup.
Both IDs contain the same subscription segment. Each group still has its own name plus its own lifecycle.
Delete the unprotected production group
Both groups are empty lab containers under the same subscription. You can now test what your Owner access permits at the production scope.
Deleting this empty lab group is deliberate. It contains no workload resources.
Before you try the deletion, do you think the hierarchy above this group is enough to stop an Owner?
- Return to the Resource groups page.
- Open exampleresourcegroup.
- Select Delete resource group.
- Complete the confirmation shown by Azure for exampleresourcegroup.
- Wait for the deletion operation to finish.
- Return to the Resource groups page.
- Refresh the browser.
You'll see exampledevgroup still listed. The Resource groups list no longer includes exampleresourcegroup.
That disappearance is the intended shortfall. The hierarchy organizes placement. A separate protection control must guard deletion.
Still see the production group?
If exampleresourcegroup remains listed, wait until the current deletion operation finishes. Refresh the Resource groups page again.
If Azure blocks the action, confirm that your active account still has Owner access to <active-subscription-id>.
Help me troubleshoot the resource group deletion.
You've proved that hierarchy alone leaves an authorized deletion risk. Next, you'll recreate the production group and protect its lifecycle.
Protect the Production Resource Group
Your Azure governance hierarchy now organizes the subscription. The development resource group exampledevgroup still demonstrates an independent workload lifecycle.
The unprotected production group disappeared because hierarchy alone does not stop an authorized deletion. In this step, you will recreate it under an explicit lifecycle guard.
A management lock adds that control at the resource-group scope. Azure CLI creates the lock. The Azure portal gives you a visible deletion test.
In this step, get ready to:
- Recreate the production resource group in West US.
- Apply the production management lock with Azure CLI.
- Test the production group against deletion in the Azure portal.
Recreate the production resource group
The production group must exist before it can hold the new protection. Recreating the same empty group restores the lifecycle boundary you deliberately removed.
- Switch back to the Azure portal tab from earlier.
- Select Resource groups from the Azure portal menu.
- Select Create.
- Choose your active subscription in the Subscription field.
- Enter exampleresourcegroup in the Resource group field.
The form now ties exampleresourcegroup to your active subscription.
- Select West US in the Region field.
- Select Review + Create.
The review screen should show exampleresourcegroup under your active subscription. It should show West US as the region.
- Select Create after the validation completes.
Azure confirms that the resource group was created.
- Return to Resource groups.
- Refresh the resource groups list.
You will see exampleresourcegroup in West US. exampledevgroup remains available.
Cannot see the recreated group?
- Refresh the resource groups list once more.
- Check that the subscription filter matches your active subscription.
- Check the creation result for a message describing why Azure rejected the request.
Help me troubleshoot the missing resource group.
The production lifecycle boundary is back in place. You now have a scope to protect.
Apply and verify the management lock
The CanNotDelete lock level permits authorized changes while blocking deletion. Applying it to exampleresourcegroup protects everything at that resource-group scope.
- Switch back to Cursor's terminal from earlier.
- Create LockGroup at resource-group scope by running this command:
az lock create --name LockGroup --lock-type CanNotDelete --resource-group exampleresourcegroup
What does this command do?
- The --name LockGroup argument gives the management lock its project name.
- The --lock-type CanNotDelete argument prevents deletion at the protected scope.
- The --resource-group exampleresourcegroup argument applies the lock to the production group.
Before you check the lock, predict the name and lock level that the terminal will return.
- Confirm the management lock by running this command:
az lock list --resource-group exampleresourcegroup
What should I see?
You will see an entry named LockGroup. Its lock level is CanNotDelete.
That is the guardrail in place. Your production group now has an explicit deletion control.
Lock missing or creation rejected?
- Confirm that the authenticated CLI session still points to your active subscription.
- Confirm that your Owner access applies to the subscription containing exampleresourcegroup.
- Check that exampleresourcegroup appears in the Azure portal before retrying the lock creation.
Help me troubleshoot the production lock.
Test the deletion protection
The CLI output proves that the lock exists. A portal deletion attempt proves how that control changes an operation at the protected scope.
- Switch back to the Azure portal tab from earlier.
- Select Resource groups.
- Open exampleresourcegroup.
This deletion attempt is deliberate. You are testing the guardrail on an empty resource group.
Before you select the deletion control, predict whether Azure will remove the group or preserve it.
- Select Delete resource group.
- Complete the confirmation shown in the portal.
You will see Azure block the deletion. exampleresourcegroup remains available.
- Return to Resource groups.
- Refresh the resource groups list.
You will see both exampledevgroup and exampleresourcegroup in the list. The production group survived the same operation that deleted it earlier.
Did the portal allow deletion?
- Check that you opened exampleresourcegroup before starting the deletion attempt.
- Check the earlier CLI output for LockGroup with lock level CanNotDelete.
- Refresh the Azure portal to update its view of the resource-group controls.
Help me investigate the deletion result.
Your governance sandbox now protects its production lifecycle boundary. The lock turns your hierarchy from an organizational map into an enforceable safeguard.
Secret mission
Add a Production Management Group
Your sandbox hierarchy has one branch for the active subscription. Add an empty Production sibling so the organization has a clear home for a future production subscription.
Clean Up Your Resources
Clean Up Your Resources
Keeping this governance-only sandbox has no expected ongoing project cost. Choose the option that matches what you want to do next.
Resources you used:
- Management group nw-hierarchy-lab.
- Management group nw-sandbox.
- Management group nw-production.
- Resource group exampledevgroup.
- Resource group exampleresourcegroup.
- Management lock LockGroup on exampleresourcegroup.
Your active Azure subscription and billing relationship stay available after this cleanup.
Keep everything running
No action is needed. Choose this if you want to use the hierarchy as a foundation for later Azure work.
- Leave the current management-group hierarchy in place.
- Keep <active-subscription-id> beneath nw-sandbox.
- Leave exampledevgroup in place.
- Leave exampleresourcegroup protected by LockGroup.
- Check service pricing before deploying future workloads into the resource groups.
Pause - I'll come back to this later
These governance objects do not run an application process. Pausing means ending today's session while Azure keeps the hierarchy.
- Close the portal tab.
- Close Cursor.
- Return later to continue with the same hierarchy.
Delete - I don't want to use this again
Deletion removes the governance sandbox while leaving the active subscription available. The cleanup follows Azure's dependency order.
Remove the Lock
- Return to the Azure portal from earlier.
- Select Resource groups from the portal menu.
- Open exampleresourcegroup.
- Select Locks.
- Select LockGroup.
- Select Delete.
- Confirm the lock deletion.
You'll see LockGroup disappear from the lock list.
Delete the Resource Groups
- Return to the Resource groups page.
- Open exampleresourcegroup.
- Select Delete resource group.
- Complete the deletion confirmation.
- Return to the Resource groups page.
- Open exampledevgroup.
- Select Delete resource group.
- Complete the deletion confirmation.
Verify the Resource Group Cleanup
- Refresh the Resource groups page.
You'll no longer see exampledevgroup or exampleresourcegroup in the list.
Move the Subscription
The subscription must leave nw-sandbox before Azure can delete that management group.
- Select Management Groups in the portal.
- Open Tenant root group.
- Select Add subscription.
- Choose subscription <active-subscription-id>.
- Select Save.
- Refresh the Management Groups hierarchy.
You'll see <active-subscription-id> beneath the Tenant root group. It no longer appears beneath nw-sandbox.
Delete the Child Management Groups
Azure can delete a management group after its subscriptions and child groups are gone.
- Select nw-sandbox in the Management Groups view.
- Select Delete on the management-group page.
- Confirm the deletion of nw-sandbox.
- Return to the Management Groups view.
- Select nw-production.
- Select Delete on the management-group page.
- Confirm the deletion of nw-production.
- Return to the Management Groups view.
Delete the Lab Parent
- Select nw-hierarchy-lab.
- Select Delete on the management-group page.
- Confirm the deletion of nw-hierarchy-lab.
- Refresh the Management Groups hierarchy.
That's the cleanup done. Your active subscription remains available beneath the Tenant root group.
Nice Work!
Nice Work!
Your Azure governance sandbox is complete! Its hierarchy now carries the active subscription through the sandbox branch to protected resource groups.
What you learned:
- Built a visible governance hierarchy from the Tenant root group through nw-hierarchy-lab and nw-sandbox to the active subscription.
- Distinguished management groups from resource groups by testing which child scopes each one can contain. Placed the active subscription beneath nw-sandbox and confirmed its tenant context with Azure CLI.
- Created development and production resource groups for separate workload lifecycles. Exposed the production group's accidental-deletion risk. Protected the recreated group with a management lock at the CanNotDelete level.
- Secret Mission: Added a separate production management group at nw-production. The empty branch is ready to receive a future production subscription.
Ready to quiz yourself?