Project: Building a Zero Trust Identity Architecture in Azure

Screenshot from Azure Portal
In the world of Cloud Engineering, clicking "Create User" is easy. But designing a secure, scalable identity system that survives a security audit? That is the real challenge.
In this project, I simulated a real-world scenario for a fictional FinTech client, FinCore Systems. The goal was to move away from the "Wild West" of manual permissions and implement a Zero Trust architecture using Microsoft Entra ID and Custom RBAC (Role-Based Access Control).
If you are studying for the AZ-104 or just want to learn how to secure Azure like a pro, follow this step-by-step guide.
This project combines and extends the concepts from the official Microsoft Learning labs. It transforms the isolated tasks into a cohesive, production-ready solution.
🏢 The Business Scenario
Client: FinCore Systems (FinTech)
The Problem: The "Help Desk" team has too much power. They currently have Contributor access, which allows them to delete production databases.
The Requirement: We need a custom security role that allows them to:
- ✅ Restart Virtual Machines (to fix issues).
- ✅ Open Support Tickets (to get help).
- ❌ Strictly Block them from creating new resources or registering expensive service providers (Shadow IT).
🏗️ The Architecture
Instead of assigning permissions to individuals (which is unscalable), we will build a hierarchy:
- Identity: Users are assigned to an Entra ID Group (
grp-fincore-support). - Scope: We create a Management Group (
mg-fincore-corp) to govern all subscriptions. - Permissions: We attach a Custom RBAC Role to the Management Group, which trickles down to everything below it.
Never trust, always verify. Permissions are explicitly granted, never assumed. Every action requires validation.
🚀 Step-by-Step Implementation
Phase 1: Identity Management (Entra ID)
First, we need to create the "Who." We never assign roles to users directly; we assign them to groups.
1. Create the Security Group
I started by creating a dedicated security group for the support team. This ensures that if we hire 50 new engineers, we just add them to this group, and they inherit the permissions automatically.
- Group Name:
grp-fincore-support - Type: Security

Always assign RBAC roles to groups, not individual users. This provides scalability and reduces administrative overhead when employees change roles or leave the organization.
2. Create the Test User
Next, I created a user named Alex Support to act as our "victim" for testing. Crucially, I set the Usage Location to the United States (a common oversight that blocks license assignments later).

Phase 2: Governance Scope (Management Groups)
To manage policies at scale, we don't want to touch every individual subscription. I created a Management Group to act as a "Root Folder" for the organization.
- ID:
mg-fincore-corp - Display Name: FinCore Corporate Root

Management Groups allow you to apply governance at scale. Instead of assigning a role to 50 individual subscriptions, you assign it once at the Management Group level, and it automatically cascades down to all child subscriptions and resources.
Phase 3: The "Least Privilege" Custom Role
This is the core of the project. The built-in "Support Request Contributor" role was too weak (can't restart VMs), but "Virtual Machine Contributor" was too dangerous (can delete VMs).
I created a Custom Role by cloning the Support role and modifying it.
1. Adding Permissions (The "Allow" List)
We need to create a role that specifically allows VM restart but denies everything else initially.

I explicitly added Microsoft.Compute/virtualMachines/restart/action. This gives Alex the power to reboot a frozen server without giving him the power to delete it.

2. Blocking Shadow IT (The "NotAction" List)
To prevent the Help Desk from accidentally enabling expensive services (like AI or Blockchain), I added Microsoft.Support/register/action to the NotActions list. This effectively "denies" the ability to register new resource providers.

NotActions is NOT a "Deny" statement. It simply subtracts permissions from the Actions list in that specific role. If a user has another role that grants the permission, they will still have access.
3. The Final Configuration Code
This JSON represents the final "Infrastructure as Code" artifact for the role.
{
"properties": {
"roleName": "FinCore Support Specialist",
"description": "Can open tickets and restart VMs. Cannot register new providers.",
"assignableScopes": [
"/providers/Microsoft.Management/managementGroups/mg-fincore-corp"
],
"permissions": [
{
"actions": [
"Microsoft.Authorization/*/read",
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Support/*",
"Microsoft.Compute/virtualMachines/restart/action"
],
"notActions": [
"Microsoft.Support/register/action"
],
"dataActions": [],
"notDataActions": []
}
]
}
}There are three critical components to this configuration:
- AssignableScopes: The role is scoped to
/mg-fincore-corp. This means it cannot be assigned at the Root Management Group (too broad) or a single Subscription (too narrow). It exists exactly where FinCore operates. - Actions: We explicitly grant
Microsoft.Support/*for ticketing and the specificrestart/actionfor VMs. This avoids the dangerouswriteordeletepermissions found in standard Contributor roles. - NotActions: This acts as a safety filter. Even if another policy or role tries to grant "Support" access, this role explicitly carves out the ability to
registernew resource providers, preventing accidental cost spikes from "Shadow IT" service headers.
Phase 4: Assigning the Role
With the role created, we now assign it to our security group at the Management Group scope. This ensures that the permissions trickle down to all subscriptions and resources within our organization.

Phase 5: The "Hacker" Verification
A security architecture is only good if it works. I logged in as Alex (using an Incognito window) to test the boundaries.
Test 1: VM Operations (The "Least Privilege" Test)
First, I attempted to Restart a Virtual Machine. Since this action was explicitly allowed in our custom role, it should work.

Success! The command executed without issues.
Next, I attempted to Delete the same VM. Since this action was NOT in our custom role (and Delete is a destructive action), it should be blocked.

Blocked! Azure's ARM layer correctly prevented the action.
Test 2: Support Operations
I attempted to open a Support Ticket. The custom role successfully allowed access to the support blade.

Alex can successfully perform legitimate support operations. The role grants exactly what is neededno more, no less.
Test 3: Governance Scope (Blocked)
I attempted to create a new Resource Group. As expected, the Zero Trust policy kicked in.
Authorization Failed: The user 'alex.support@fincore.com' does not have authorization to perform action 'Microsoft.Resources/resourceGroups/write' over scope '/subscriptions/xxxx'.
Perfect! The guardrails are working exactly as designed.
📊 Key Takeaways
Identity Design
Use Security Groups for role assignments, not individual users. This ensures scalability and reduces administrative burden.
Always assign RBAC roles to groups. When employees change roles or leave, simply update group membership instead of hunting down individual assignments.
Least Privilege
Built-in roles are often too broad. Use Custom RBAC Roles to grant exactly the permissions needednothing more.
The "Contributor" role allows VM deletion. Create a custom "VM Operator" role that only allows start/stop/restart actions.
Hierarchy Design
Use Management Groups to apply governance at scale. Permissions cascade down automatically to all child resources.
Assign a role once at the Management Group level instead of 50 times across individual subscriptions.
Testing
Always test your roles using a non-privileged account. Verify both positive cases (can do job) and negative cases (cannot break things).
Use an Incognito window to log in as the test user. Attempt both allowed actions and restricted actions to verify the role works as designed.
🎯 Conclusion
Building a Zero Trust Identity Architecture is not about making access harderit's about making access smarter. By combining:
- Entra ID Security Groups (for identity)
- Management Groups (for scope)
- Custom RBAC Roles (for least-privilege permissions)
...we created a system that is both secure and scalable. The Help Desk team can do their job effectively, but they cannot accidentally (or maliciously) destroy production resources.
This project covers multiple exam domains: Entra ID management, Custom RBAC role creation, Management Group hierarchies, and Zero Trust principles. Understanding these concepts will help you ace identity and governance questions on exam day.
If you're preparing for Azure certifications or building production-grade cloud environments, I hope this walkthrough helps you implement security with confidence.
Next Steps:
- Implement Conditional Access policies for device compliance
- Add Azure Policy to enforce naming conventions
- Deploy Azure AD Privileged Identity Management (PIM) for just-in-time access
Want to see more cloud architecture projects? Follow my blog for deep dives into Azure, AWS, and DevSecOps best practices.
Want to discuss this further?
I'm always happy to chat about cloud architecture and share experiences.