Manage Azure Identities & Governance (AZ-104)
AZ-104 — Manage Azure Identities and Governance. Microsoft Entra ID (Azure AD), RBAC, Azure Policy, Management Groups, locks, and PIM — with step-by-step labs.
What you'll learn
- Create and manage users, groups, and guests in Microsoft Entra ID
- Assign Azure RBAC roles and create custom roles
- Design Management Group and subscription hierarchies
- Enforce compliance with Azure Policy
- Protect resources with resource locks
- Use Privileged Identity Management for just-in-time access
Prerequisites
Relevant for certifications
Microsoft Entra ID (formerly Azure Active Directory)
Microsoft Entra ID is Azure's identity and access management service — the central identity provider for Azure, Microsoft 365, and thousands of SaaS applications.
Microsoft Entra ID (Tenant)
├── Users (employees, admins, service accounts)
├── Groups (security, Microsoft 365)
├── Applications (enterprise apps, app registrations)
├── Devices (Entra joined, registered)
└── External identities (B2B guests, B2C customers)
Entra ID vs Active Directory Domain Services
| Microsoft Entra ID | AD DS (on-premises) | |
|---|---|---|
| Protocol | REST/OAuth/SAML | Kerberos/LDAP/NTLM |
| Structure | Flat (no OUs) | Hierarchical (OUs, GPOs) |
| Management | Cloud-native | On-premises or IaaS |
| MFA | Built-in | Third-party required |
| Device join | Entra Join | Domain join |
Users and Groups
Create a user
# Create a new user
az ad user create \
--display-name "Jane Smith" \
--user-principal-name jane.smith@company.onmicrosoft.com \
--password "TempP@ss123" \
--force-change-password-next-sign-in true
# Create a guest user (B2B invite)
az ad user invite \
--user-principal-name contractor@external.com \
--display-name "External Contractor" \
--invited-user-email contractor@external.com \
--invite-redirect-url https://portal.azure.com
Create and manage groups
# Create a security group
az ad group create \
--display-name "Network Admins" \
--mail-nickname NetworkAdmins \
--description "Team responsible for VNet management"
# Add a member
az ad group member add \
--group "Network Admins" \
--member-id $(az ad user show --id jane.smith@company.onmicrosoft.com --query id -o tsv)
# Create dynamic group (auto-membership based on attributes)
# Done in portal: Groups → New Group → Membership type: Dynamic User
# Rule: user.department -eq "Engineering"
Dynamic groups
Dynamic groups automatically add/remove members based on user attributes — no manual management:
Rule syntax:
user.department -eq "Engineering" → all Engineering dept users
user.jobTitle -contains "Manager" → all managers
user.userPrincipalName -endsWith "@corp.com" → all corp.com email users
(user.department -eq "IT") -and (user.accountEnabled -eq true)
Bulk operations
# Bulk create users from CSV
az ad user list --output csv > users-export.csv
# Edit CSV, then use portal: Users → Bulk create → Upload CSV
Azure Role-Based Access Control (RBAC)
RBAC controls who can do what on which Azure resource.
Role assignment = Security Principal + Role Definition + Scope
Who: Jane Smith (user)
Can do what: Virtual Machine Contributor (role)
On what: Resource Group "prod-rg" (scope)
Built-in roles
| Role | Description |
|---|---|
| Owner | Full access + manage access |
| Contributor | Full access, cannot manage access |
| Reader | Read-only |
| User Access Administrator | Manage user access (not resources) |
| Virtual Machine Contributor | Manage VMs (not networking/storage) |
| Network Contributor | Manage networking |
| Storage Account Contributor | Manage storage accounts |
Assign a role
# Assign Contributor to a user at resource group scope
az role assignment create \
--assignee jane.smith@company.onmicrosoft.com \
--role "Contributor" \
--scope /subscriptions/<sub-id>/resourceGroups/prod-rg
# Assign Virtual Machine Contributor at subscription scope
az role assignment create \
--assignee $(az ad group show --group "VM Admins" --query id -o tsv) \
--role "Virtual Machine Contributor" \
--scope /subscriptions/<sub-id>
# Assign Reader at management group scope
az role assignment create \
--assignee jane.smith@company.onmicrosoft.com \
--role "Reader" \
--scope /providers/Microsoft.Management/managementGroups/mg-prod
Scope hierarchy
Management Group
└── Subscription
└── Resource Group
└── Individual Resource
Roles assigned higher in hierarchy are inherited downward.
A role at subscription scope applies to ALL resource groups and resources within.
Custom roles
Create a role with exactly the permissions you need:
# Create a custom role (start from JSON definition)
cat > custom-vm-operator.json << 'EOF'
{
"Name": "VM Operator",
"Description": "Can start, stop, and restart VMs, read all resources",
"Actions": [
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/powerOff/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Resources/subscriptions/resourceGroups/read"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": ["/subscriptions/<sub-id>"]
}
EOF
az role definition create --role-definition @custom-vm-operator.json
# Assign the custom role
az role assignment create \
--assignee ops-team@company.com \
--role "VM Operator" \
--scope /subscriptions/<sub-id>/resourceGroups/prod-rg
Check effective permissions
# What can this user do?
az role assignment list --assignee jane.smith@company.onmicrosoft.com --all
# Check if a specific action is allowed
az role assignment list \
--scope /subscriptions/<sub-id>/resourceGroups/prod-rg \
--query "[?principalName=='jane.smith@company.onmicrosoft.com']"
Management Groups
Management groups sit above subscriptions — group subscriptions for policy and RBAC application at scale.
Tenant Root Group
├── Platform MG
│ ├── Connectivity subscription (Hub VNet, ExpressRoute)
│ └── Identity subscription (AD DS, Azure AD Connect)
├── Production MG
│ ├── Prod-App-1 subscription
│ └── Prod-App-2 subscription
├── Non-Production MG
│ ├── Dev subscription
│ └── Test subscription
└── Sandbox MG
└── Experimentation subscription
# Create a management group
az account management-group create \
--name "mg-production" \
--display-name "Production"
# Move a subscription to a management group
az account management-group subscription add \
--name "mg-production" \
--subscription <subscription-id>
Azure Policy
Azure Policy enforces organisational standards and assesses compliance across Azure resources.
Policy effects
| Effect | Description |
|---|---|
| Deny | Blocks non-compliant resource creation/update |
| Audit | Logs non-compliant resources, doesn't block |
| DeployIfNotExists | Deploys a resource if missing (e.g., add diagnostics) |
| AuditIfNotExists | Audits if a related resource doesn't exist |
| Append | Adds fields to the resource (e.g., tags) |
| Modify | Modify tags on resources |
| Disabled | Policy is inactive |
Apply a built-in policy
# Enforce allowed VM SKUs
POLICY_ID=$(az policy definition list \
--query "[?displayName=='Allowed virtual machine size SKUs'].id" -o tsv)
az policy assignment create \
--name "allowed-vm-skus" \
--policy $POLICY_ID \
--scope /subscriptions/<sub-id>/resourceGroups/prod-rg \
--params '{"listOfAllowedSKUs": {"value": ["Standard_D2s_v3", "Standard_D4s_v3"]}}'
Create a custom policy
# Policy: Require a CostCenter tag on all resources
cat > require-tag-policy.json << 'EOF'
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"notIn": ["Microsoft.Resources/subscriptions/resourceGroups"]
},
{
"field": "tags['CostCenter']",
"exists": "false"
}
]
},
"then": {
"effect": "Deny"
}
}
}
EOF
az policy definition create \
--name "require-costcenter-tag" \
--display-name "Require CostCenter tag" \
--rules @require-tag-policy.json
az policy assignment create \
--name "enforce-costcenter-tag" \
--policy require-costcenter-tag \
--scope /subscriptions/<sub-id>
Policy Initiatives (Policy Sets)
Group multiple policies into a single assignment:
# Create initiative from multiple policies
az policy set-definition create \
--name "security-baseline" \
--display-name "Security Baseline" \
--policy-definitions '[
{"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/1e30110a-5ceb-460c-a204-c1c3969c6d62"},
{"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/4da35fc9-c9e7-4960-aec9-797fe7d9051d"}
]'
# Assign the initiative
az policy assignment create \
--name "security-baseline-assignment" \
--policy-set-definition security-baseline \
--scope /subscriptions/<sub-id>
Remediation tasks
For DeployIfNotExists and Modify policies, create a remediation task to fix existing non-compliant resources:
az policy remediation create \
--name "add-diagnostics-remediation" \
--policy-assignment /subscriptions/<sub-id>/providers/Microsoft.Authorization/policyAssignments/add-vm-diagnostics \
--resource-discovery-mode ReEvaluateCompliance
Resource Locks
Resource locks prevent accidental deletion or modification — they apply to all users regardless of RBAC role.
| Lock type | Prevents |
|---|---|
| CanNotDelete | Delete (read and modify still allowed) |
| ReadOnly | Both modification AND deletion |
# Lock a resource group (children inherit the lock)
az lock create \
--name "prod-rg-lock" \
--resource-group prod-rg \
--lock-type CanNotDelete \
--notes "Production resource group — do not delete"
# Lock an individual resource
az lock create \
--name "vnet-lock" \
--resource-group prod-rg \
--resource-type Microsoft.Network/virtualNetworks \
--resource-name prod-vnet \
--lock-type CanNotDelete
# List locks
az lock list --resource-group prod-rg
# Delete a lock (must be done before deleting the resource)
az lock delete --name "vnet-lock" --resource-group prod-rg
Warning
ReadOnly locks can cause unexpected failures — for example, an App Service with ReadOnly lock may fail to start because starting requires a write operation to update the LastModified timestamp.
Managed Identities
Managed identities provide an Azure AD identity for Azure resources to authenticate to other Azure services — no credentials to manage.
| Type | Description |
|---|---|
| System-assigned | Tied to the resource lifecycle — deleted with the resource |
| User-assigned | Independent resource — can be assigned to multiple resources |
# Enable system-assigned managed identity on a VM
az vm identity assign \
--resource-group myRG \
--name myVM
# Create a user-assigned managed identity
az identity create \
--resource-group myRG \
--name myManagedIdentity
# Assign the identity to a VM
az vm identity assign \
--resource-group myRG \
--name myVM \
--identities /subscriptions/<sub-id>/resourceGroups/myRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity
# Grant the identity access to Key Vault secrets
az role assignment create \
--assignee $(az identity show --name myManagedIdentity --resource-group myRG --query principalId -o tsv) \
--role "Key Vault Secrets User" \
--scope /subscriptions/<sub-id>/resourceGroups/myRG/providers/Microsoft.KeyVault/vaults/myKV
On the VM, the app can now call Key Vault without any stored credentials — it gets a token from the IMDS endpoint automatically.
Privileged Identity Management (PIM)
PIM provides just-in-time (JIT) privileged access — users only have elevated roles when they need them.
Key concepts
| Concept | Description |
|---|---|
| Eligible assignment | User can activate the role, but it's not active by default |
| Active assignment | Role is permanently active |
| Activation | User requests access for a limited time (hours) |
| Approval | Activation can require manager approval |
| Access reviews | Periodic review of who has access |
Configure PIM for a role
Azure AD → Privileged Identity Management → Azure Resources → Subscriptions
→ Select subscription → Roles → Owner
→ Settings:
- Maximum activation duration: 4 hours
- Require MFA on activation: Yes
- Require justification: Yes
- Require approval: Yes (approver: security-team@company.com)
→ Assignments → Add assignments (Eligible):
- Member: Jane Smith
- Assignment type: Eligible
Activate a role (end user)
PIM → My roles → Azure Resource roles
→ Owner → Activate
→ Activation duration: 2 hours
→ Justification: "Deploying production infrastructure for project X"
→ Submit → Wait for approval → Role activates
Administrative Units
Administrative units (AUs) allow delegation of administration to subsets of users within a tenant — useful for large organisations with regional IT teams:
Example:
AU: UK-Staff
Members: all UK-based users
Scoped admin: UK IT team has User Administrator for UK-Staff AU only
(cannot see or manage users outside the AU)
# Create an administrative unit
az ad administrative-unit create --display-name "UK-Staff"
# Add a user to the AU
az ad administrative-unit member add \
--administrative-unit-id <au-id> \
--member-id <user-object-id>
# Assign scoped admin role (done in portal: AU → Roles and administrators)
Hands-on: Governance Setup for a New Project
Goal: Set up governance for a new production project with least-privilege access.
1. Create resource group
az group create -n "ecom-prod-rg" -l eastus
2. Apply resource lock
az lock create -n "no-delete" -g ecom-prod-rg --lock-type CanNotDelete
3. Create Azure Policy assignment
# Require Environment tag
az policy assignment create \
--name "require-environment-tag" \
--policy "require-tag-policy" \
--scope /subscriptions/<sub-id>/resourceGroups/ecom-prod-rg
4. Create Security Group
az ad group create --display-name "EcomProd-Developers"
5. Assign Contributor role (RBAC)
az role assignment create \
--assignee <group-object-id> \
--role "Contributor" \
--scope /subscriptions/<sub-id>/resourceGroups/ecom-prod-rg
6. Create managed identity for app
az identity create -n ecom-app-identity -g ecom-prod-rg
# Grant Key Vault Secrets User role to the identity
7. Enable PIM for Owner role on the subscription
# Set: eligible assignment for on-call team
# Require approval + MFA for activation
# Max 4-hour activation duration
8. Check compliance
az policy state list -g ecom-prod-rg
# All resources should be compliant with tag policy
Common AZ-104 Exam Questions
Q: A user has Contributor on a resource group but still can't delete a VM. Why? A resource lock (CanNotDelete or ReadOnly) is applied to the resource or resource group. Locks override RBAC — even Owners and Contributors cannot delete locked resources without first removing the lock.
Q: You need to allow a team to start/stop VMs but not create or delete them. What do you use?
Create a custom RBAC role with only Microsoft.Compute/virtualMachines/start/action, Microsoft.Compute/virtualMachines/powerOff/action, and read permissions. Assign it to the team's security group at the resource group scope.
Q: How do you prevent all subscriptions in an organisation from deploying resources to restricted regions? Apply an Azure Policy (Deny effect) at the Management Group level — it inherits to all subscriptions. Or apply an SCP if using Azure landing zones with specific governance frameworks.
Q: What's the difference between a system-assigned and user-assigned managed identity? System-assigned is created per-resource and deleted with it — one-to-one. User-assigned is a standalone resource that can be assigned to multiple resources — useful when multiple services need the same identity and permissions.
What to Learn Next
- Managing Subscriptions — subscription organisation and cost management
- Azure Monitor (AZ-104) — monitor compliance and resource health
- Azure Governance Cheatsheet — quick reference for policy and RBAC
