Theoretical Foundation: Interpret access assignments
1. Initial Intuitionβ
Imagine you are the new security administrator for a company and need to answer a simple question: "What exactly can Ana do on this server?". The answer is not obvious. Ana might have received a role directly, she might have inherited permissions because she is a member of a group, and that group might have received a role at a scope above the server. Additionally, she might have two roles that combine.
Interpreting access assignments is the ability to, given a user and a resource, accurately answer: what effective permissions does this user have, where do they come from, and why. It's not enough to know that an assignment exists; you need to know how to read the complete picture.
In previous modules, we learned to create users, groups, roles, and assignments. This module is about reading and interpreting what was created, understanding how the pieces combine to produce effective access.
2. Contextβ
The ability to interpret access assignments is central to three critical day-to-day activities of an Azure administrator:
AZ-104 specifically evaluates this ability because it integrates all previous concepts: understanding scope hierarchy, how roles are defined, how groups propagate permissions, and how multiple assignments combine.
3. Building the Conceptsβ
3.1 The Five Sources of Accessβ
When you look at a user's permissions on a resource, access can come from five distinct sources. Understanding each is the first step to interpreting correctly:
The effective permission is the union of F1 + F2 + F3 + F4, minus any Deny Assignment (F5).
3.2 The Three Types of Assignment in the Portalβ
When viewing role assignments on any resource in the portal, you will see assignments classified into three types:
| Type | What it means | How to identify |
|---|---|---|
| Direct | Assignment made directly at this scope for this user/group | User icon, scope = current resource |
| Inherited | Assignment made at a parent scope (subscription, MG, etc.) that propagates here | Down arrow icon, scope = parent scope |
| Group | The user is a member of a group that has an assignment at this or parent scope | Group icon |
In practice, an assignment can be both "Inherited" and "Group": the user inherits a permission because they are a member of a group that received a role at a parent scope.
3.3 The Additivity Ruleβ
As seen previously, permissions in Azure are additive: when a user has multiple role assignments (directly or via groups, at any scope), all permissions are combined.
Concrete example:
The user has Reader + Contributor. Since Contributor includes everything Reader includes and more, the effective result is Contributor. Additivity means the broader permission prevails, but only because permissions are combined, not because one "overrides" the other.
3.4 Deny Assignments: The Absolute Exceptionβ
Deny Assignments take precedence over any permission, regardless of how it was granted. Even if a user is Owner of a subscription, a Deny Assignment can block a specific action.
Deny Assignments are created by Azure Blueprints (when applying locks to managed resources), Azure Managed Applications, and Azure Lighthouse. The common administrator cannot create Deny Assignments directly but needs to know how to interpret them when they appear.
3.5 The Difference between Actions and DataActions in Interpretationβ
A frequent source of confusion: having permission to manage a resource does not mean having permission to access the data within the resource.
When interpreting a user's access to a Storage Account, you need to check separately:
- What they can do on the control plane (manage the resource, view configurations)
- What they can do on the data plane (read, write, or delete stored data)
Roles that start with names like Storage Blob Data Reader, Key Vault Secrets User, Queue Data Contributor are data plane roles (DataActions). Generic roles like Contributor or Storage Account Contributor are predominantly control plane.
4. Structural Viewβ
The Complete Interpretation Processβ
Complete Interpretation Scenarioβ
Let's build a real scenario and interpret access step by step.
Environment setup:
Group members:
- Grupo-Auditoria: Pedro, Carlos
- Grupo-DevOps: Ana, Bruno
- Grupo-Ops: Carlos
- Grupo-Analytics: Pedro
Question: What can Ana do on VM alpha-web-01 and on Storage alphalogs?
Interpretation for Ana on VM alpha-web-01:
| Permission source | Scope | Role | Type |
|---|---|---|---|
| Grupo-DevOps | Subscription | Contributor | Inherited via group |
| Direct to Ana | RG App-Alpha | Reader | Inherited from parent scope of VM |
| Direct to Ana | VM alpha-web-01 | Virtual Machine Admin Login | Direct |
Effective permission on VM alpha-web-01:
- Contributor (via Grupo-DevOps from Sub): can create, modify, delete any resource in RG, including the VM
- Reader (direct on RG): subset of Contributor, redundant
- Virtual Machine Admin Login (direct on VM): can login to the VM with administrator privileges via Azure AD
Result: Ana has Contributor permissions over the VM (management) AND can login as administrator to it.
Interpretation for Ana on Storage alphalogs:
| Permission source | Scope | Role | Type |
|---|---|---|---|
| Grupo-DevOps | Subscription | Contributor | Inherited via group |
| Direct to Ana | RG App-Alpha | Reader | Inherited from RG |
Effective permission on Storage alphalogs:
- Contributor (via Grupo-DevOps): can manage the Storage Account (create containers, view access keys, modify configurations), but CANNOT read blob data (Contributor does not include blob DataActions)
- Reader (from RG): subset, redundant
- Storage Blob Data Reader: this role is assigned to Grupo-Analytics, not Ana. Ana is not a member of Grupo-Analytics.
Result: Ana can manage the Storage Account but cannot read blob data directly via Azure RBAC. If she wants to read the blobs, she would need to use the storage access keys (which she can view as Contributor) or have the Storage Blob Data Reader role assigned to her or to a group she is part of.
5. Practical Operationβ
"Check Access" Tool in the Portalβ
The most direct way to interpret a specific user's access is the Check Access tool available in the IAM tab of any resource:
Path: [Resource] > Access control (IAM) > Check access > [search user or group]
The result shows:
- All assigned roles that affect that user at that scope
- The type of each assignment (direct, inherited, via group)
- The origin scope of each assignment
Important behavior: the Check Access tool shows the combination of all sources but does not automatically expand groups to show through which group the user inherits a permission. For that, it's necessary to investigate group membership separately.
Role Assignments Tab Viewβ
In the Role assignments tab of a resource, you can filter and interpret:
| Available filter | What it's for |
|---|---|
| Type: Direct | View only what was explicitly assigned at this scope |
| Type: Inherited | View only what comes from parent scopes |
| Scope: This resource | Direct assignments on this specific resource |
| Scope: Inherited | Assignments coming from sub, MG, etc. |
A common reading error: seeing only direct assignments and concluding "there is no access" when real access comes from inheritance.
6. Implementation Methodsβ
6.1 Azure Portal: Check Accessβ
When to use: quick diagnosis of a specific user's access to a specific resource.
[Resource] > Access control (IAM) > Check access
Advantages: visual and immediate, shows type and origin scope of each assignment.
Limitations: shows the user in relation to one resource at a time. For a view of all user access in the tenant, PowerShell or Graph API is needed.
6.2 Azure CLIβ
Check user access to a specific resource, including inheritance and groups:
az role assignment list \
--assignee joao.silva@contoso.com \
--scope "/subscriptions/<sub-id>/resourceGroups/rg-producao/providers/Microsoft.Compute/virtualMachines/vm-prod-01" \
--include-inherited \
--include-groups \
--output table
Check access to an entire Resource Group:
az role assignment list \
--assignee joao.silva@contoso.com \
--resource-group rg-producao \
--include-inherited \
--include-groups \
--output table
Check Deny Assignments:
az role assignment list \
--assignee joao.silva@contoso.com \
--scope "/subscriptions/<sub-id>/resourceGroups/rg-producao" \
--include-inherited \
--include-groups \
--include-deny-assignments \
--output table
Critical point: --include-groups is essential for complete interpretation. Without it, assignments that come via group membership don't appear, and the reading will be incomplete.
6.3 PowerShellβ
Complete view of a user's access to a scope:
$user = "ana@contoso.com"
$scope = "/subscriptions/<sub-id>/resourceGroups/rg-producao"
Get-AzRoleAssignment `
-SignInName $user `
-Scope $scope `
-ExpandPrincipalGroups |
Select-Object DisplayName, RoleDefinitionName, Scope, ObjectType |
Format-Table -AutoSize
The -ExpandPrincipalGroups parameter is the equivalent of CLI's --include-groups: it expands the user's groups to show assignments via group.
Check effective permissions on a set of RG resources:
$user = "ana@contoso.com"
$rg = "rg-producao"
# All RG resources
$resources = Get-AzResource -ResourceGroupName $rg
foreach ($resource in $resources) {
$assignments = Get-AzRoleAssignment `
-SignInName $user `
-Scope $resource.ResourceId `
-ExpandPrincipalGroups
if ($assignments) {
Write-Output "`n--- $($resource.Name) ($($resource.ResourceType)) ---"
$assignments | Select-Object RoleDefinitionName, Scope, ObjectType |
Format-Table -AutoSize
}
}
This script allows complete mapping of what a user can do on each resource in a Resource Group.
6.4 Microsoft Graph APIβ
For programmatic access interpretation via API:
GET https://management.azure.com/subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Compute/virtualMachines/{vmName}/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&$filter=atScope()
The $filter=atScope() parameter returns only assignments exactly at this scope. To include inherited ones, use $filter=principalId eq '{objectId}' at each parent scope and combine results.
To check effective permissions via Graph (requires Microsoft.Authorization/permissions/action permission):
POST https://management.azure.com/subscriptions/{subId}/resourceGroups/{rgName}/providers/Microsoft.Authorization/permissions?api-version=2015-07-01
7. Control and Securityβ
Interpretation for Security Auditing Purposesβ
Assignment interpretation is a central auditing activity. The critical questions to answer regularly are:
Who has Owner or Contributor at broad scopes (Sub or MG)?
# List all with Owner or Contributor on subscription
Get-AzRoleAssignment -Scope "/subscriptions/<sub-id>" |
Where-Object { $_.RoleDefinitionName -in @("Owner", "Contributor") } |
Select-Object DisplayName, SignInName, RoleDefinitionName, ObjectType, Scope |
Sort-Object RoleDefinitionName
Are there direct assignments to individual users (instead of groups)?
Get-AzRoleAssignment -Scope "/subscriptions/<sub-id>" -IncludeClassicAdministrators |
Where-Object { $_.ObjectType -eq "User" } |
Select-Object DisplayName, SignInName, RoleDefinitionName, Scope
Direct assignments to users (instead of via groups) are a sign of possible process circumvention, make management difficult, and compromise auditability.
Are there orphaned assignments (for deleted security principals)?
Get-AzRoleAssignment -Scope "/subscriptions/<sub-id>" |
Where-Object { $_.DisplayName -eq $null -and $_.SignInName -eq $null } |
Select-Object ObjectId, RoleDefinitionName, Scope
Assignments without DisplayName or SignInName indicate that the security principal was deleted from Entra ID but the role assignment was not removed. They consume slots from the 4,000 available and should be cleaned up.
Deny Assignment Interpretationβ
To see all Deny Assignments in a subscription:
# Via Az module
Get-AzDenyAssignment -Scope "/subscriptions/<sub-id>"
# Via CLI
az role assignment list \
--include-deny-assignments \
--scope "/subscriptions/<sub-id>" \
--output table
A Deny Assignment has the following relevant properties:
| Property | Meaning |
|---|---|
| DenyAssignmentName | Descriptive name of the deny |
| Actions | Operations being denied |
| NotActions | Exceptions to the deny (operations not denied even though in Actions) |
| Principals | Who the deny applies to |
| ExcludePrincipals | Who is excluded from the deny (typically the managed application publisher) |
| DoNotApplyToChildScopes | If false, the deny inherits to child scopes |
8. Decision Makingβ
Which tool to use for interpreting access?β
| Situation | Tool | Reason |
|---|---|---|
| Point diagnosis of a user on a resource | Portal (Check Access) | Visual, immediate, no configuration |
| Audit of who has access to a specific RG | Azure CLI with --include-inherited --include-groups | Complete, exportable |
| Mapping all user access in Sub | PowerShell with -ExpandPrincipalGroups | More flexible for analysis and reporting |
| Large-scale orphaned assignment detection | PowerShell with DisplayName null filter | Allows automation and programmatic cleanup |
| Integration with ITSM or SIEM system | Graph API / ARM API | Integration with external systems |
| Periodic review for compliance | Entra ID Access Reviews (P2) | Automates review with approval workflow |
How to interpret "access denied" in diagnosis?β
9. Best Practicesβ
Use "Check Access" before granting more permissions: before responding to a "I need access to resource X" request by adding a new role assignment, always first check if the user already has access via inheritance or groups. Many "access denied" requests are resolved by the user not knowing the correct URL, not due to lack of permission.
Document interpretation in audits: when conducting an access audit, don't just list assignments: document the interpretation. "DevOps-Group has Contributor on Sub, which means all its members can create and modify any resources in production subscriptions" is more useful than simply "DevOps-Group: Contributor, scope: /subscriptions/xxx".
Separate control plane interpretation from data plane interpretation: when documenting or reporting access, make it explicit whether the permission is for control plane (managing the resource), data plane (accessing data within the resource), or both. This avoids confusion where someone concludes "has Contributor, so can read Key Vault data", when actually they can't without a data plane role.
Always include groups in interpretation: any access analysis that doesn't consider the user's group associations is incomplete. Most access in well-managed environments comes via groups, not via direct assignments.
10. Common Errorsβ
Interpreting only direct assignments and concluding incorrectly
The most frequent error: an administrator opens a resource's IAM, filters by "Direct" or only sees assignments visible on the first screen, doesn't see the user listed and concludes "they don't have access". In reality, the user has inherited access via a group from a parent scope. The wrong conclusion leads to granting duplicate permission or incorrectly diagnosing an access problem.
Confusing role with effective permission
Seeing "Reader" assigned and concluding the user "can only read" without checking if there are other assignments. If the same user also has Contributor via a group, they can create and modify resources. The Reader role adds to Contributor, and the effective permission is Contributor.
Assuming Actions cover DataActions
"The user has Contributor on Key Vault, therefore can read secrets." Wrong. Contributor doesn't include Microsoft.KeyVault/vaults/secrets/read as a DataAction. To access secrets, the user needs Key Vault Secrets User (or Key Vault Administrator). This confusion is very common with Key Vault and Storage Account.
Not checking Deny Assignments when diagnosing access denied
A user has Owner on a subscription but can't delete resources in a specific Resource Group. Investigation shows all role assignments are correct. The diagnosis stops here and the case remains unsolved. The actual cause is a Deny Assignment created by a Blueprint that protects that RG. Deny Assignments don't appear in default role assignment listings; they need to be explicitly searched with --include-deny-assignments.
Ignoring the difference between assignment to group vs. assignment to user via group
An administrator sees that DevOps-Group has Contributor on the subscription and concludes "everyone in the company has Contributor" without checking who's in the group. Correct interpretation requires knowing the composition of relevant groups. Dynamic groups are especially tricky: a user can enter or leave the group automatically based on attributes, changing their access without any explicit administrative action.
11. Operation and Maintenanceβ
Periodic Privileged Access Reportβ
A recommended practice is to generate a monthly report of all users with high-privilege roles (Owner, Contributor, User Access Administrator) in broad scopes:
$privilegedRoles = @("Owner", "Contributor", "User Access Administrator")
$subscriptionId = "<sub-id>"
$report = Get-AzRoleAssignment `
-Scope "/subscriptions/$subscriptionId" |
Where-Object { $_.RoleDefinitionName -in $privilegedRoles } |
Select-Object @{Name="Principal";Expression={$_.DisplayName}},
@{Name="Email";Expression={$_.SignInName}},
@{Name="Type";Expression={$_.ObjectType}},
@{Name="Role";Expression={$_.RoleDefinitionName}},
@{Name="Scope";Expression={$_.Scope}} |
Sort-Object Role, Principal
$report | Export-Csv -Path "privileged-access-$(Get-Date -Format 'yyyy-MM').csv" -NoTypeInformation
$report | Format-Table -AutoSize
Role Assignment Change Monitoringβ
To detect when role assignments are created or removed, configure alerts on the Activity Log:
Via portal: Monitor > Alerts > New alert rule > Signal: Create role assignment or Delete role assignment.
# Create alert via CLI for role assignment creation
az monitor activity-log alert create \
--name "alert-new-role-assignment" \
--resource-group rg-monitoring \
--scopes "/subscriptions/<sub-id>" \
--condition category=Administrative and operationName=Microsoft.Authorization/roleAssignments/write \
--action-group "/subscriptions/<sub-id>/resourceGroups/rg-monitoring/providers/microsoft.insights/actionGroups/ag-security"
This alert notifies the security team whenever a new role assignment is created in the subscription, allowing immediate review of access grants.
Limits and Performance Considerationsβ
| Item | Detail |
|---|---|
| Change propagation | Up to 30 minutes after create/remove |
| Activity Log retention | 90 days (default) |
| Role assignments per subscription | 4,000 (hard limit) |
| Check Access latency in portal | May vary in very large tenants |
12. Integration and Automationβ
Export to Microsoft Sentinel or SIEMβ
Role assignment logs from Activity Log can be exported to Microsoft Sentinel or any SIEM via Diagnostic Settings:
az monitor diagnostic-settings create \
--name "export-activity-to-sentinel" \
--resource "/subscriptions/<sub-id>" \
--workspace "/subscriptions/<sub-id>/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/law-security" \
--logs '[{"category": "Administrative", "enabled": true}]'
With this, KQL queries in Sentinel can detect suspicious patterns, such as:
- A user receiving Owner on a subscription outside business hours
- Many role assignments created in rapid sequence
- Role assignments on production resources made by development users
KQL Query for Role Assignment Analysis in Log Analyticsβ
AzureActivity
| where OperationNameValue == "MICROSOFT.AUTHORIZATION/ROLEASSIGNMENTS/WRITE"
| where ActivityStatusValue == "Success"
| extend RoleDefinition = tostring(parse_json(Properties).requestbody)
| project TimeGenerated, Caller, ResourceGroup, SubscriptionId, RoleDefinition
| sort by TimeGenerated desc
Integration with Automated Access Reviewsβ
With Entra ID P2, Access Reviews can be configured to automatically review Azure RBAC role assignments:
Path: Microsoft Entra ID > Identity Governance > Access reviews > New access review
Configure reviews for:
- Frequency: Monthly or quarterly
- Scope: Privileged roles (Owner, Contributor) in specific subscriptions
- Reviewers: User managers or resource managers
- Upon completion: Automatically remove access if no response (or wait for manual removal)
This transforms the interpretation and cleanup of assignments from a periodic manual task into an automated and auditable process.
13. Final Summaryβ
Essential points:
- A user's effective permission is the union of all role assignments that affect them, direct and inherited, directly and via groups, in all applicable scopes.
- Deny Assignments have absolute priority and can block actions even for Owners. They don't appear in default listings; need to be searched with
--include-deny-assignments. - Actions (control plane) and DataActions (data plane) are evaluated separately. Contributor doesn't imply access to blob data or Key Vault secrets.
- Inherited assignments from parent scopes are as valid as direct assignments. An analysis without
--include-inheritedis incomplete.
Critical differences:
- Direct vs. Inherited: distinguishes where permission was granted, not what it allows. Both have the same practical effect.
- Actions vs. DataActions: critical for Storage, Key Vault and other services with data plane. Contributor doesn't include blob DataActions.
--include-groupsvs. without it: without--include-groups, assignments via groups remain invisible, making the analysis incomplete.- NotActions vs. Deny Assignment: NotActions subtracts from the current role; Deny Assignment explicitly denies and takes priority over any role assignment.
What needs to be remembered:
- Always use
--include-inherited,--include-groupsand--include-deny-assignmentstogether for complete interpretation via CLI/PowerShell. - The Check Access tool in the portal IAM is the fastest starting point for point diagnosis.
- Assignments without DisplayName or SignInName are orphaned (security principal deleted). Should be removed.
- Direct assignments to individual users (instead of groups) are a warning sign for audit.
- In "access denied" diagnosis, follow the flow: no assignment > action not covered by role > action in NotActions > Deny Assignment > wrong scope > DataActions vs. Actions > pending propagation.
- Access via dynamic groups can change automatically when user attributes change, without any explicit administrative action on access.