Private/Get-IntuneAccessTenantRbac.ps1
|
function Get-IntuneAccessTenantRbac { [CmdletBinding()] param( [AllowNull()] [string] $InitialUserPrincipalName, [switch] $IncludeWorkloadAssignments, [switch] $IncludeOperationalEvidence, [switch] $IncludePolicyAnalysis, [switch] $IncludeAuditEvidence ) $analysisScopes = @('User.Read', 'User.Read.All', 'GroupMember.Read.All', 'DeviceManagementRBAC.Read.All') $context = Assert-IntuneAccessConnection -RequiredScope $analysisScopes $tenant = Get-IntuneAccessTenant $allDefinitions = @(Get-IntuneAccessRoleDefinitions) $assignments = @(Get-IntuneAccessRoleAssignments -RoleDefinition $allDefinitions -ResolveNames) $adminGroups = @($assignments | ForEach-Object AdminGroups | Where-Object { $null -ne $_ } | Group-Object Id | ForEach-Object { $_.Group[0] }) $scopeGroups = @($assignments | ForEach-Object ScopeGroups | Where-Object { $null -ne $_ } | Group-Object Id | ForEach-Object { $_.Group[0] }) $scopeTags = @($assignments | ForEach-Object ScopeTags | Where-Object { $null -ne $_ } | Group-Object Id | ForEach-Object { $_.Group[0] }) $connectedRoleIds = @($assignments | ForEach-Object { [string] $_.RoleDefinition.Id } | Where-Object { $_ } | Select-Object -Unique) $roleDefinitions = @($allDefinitions | Where-Object Id -In $connectedRoleIds) $memberships = @(Get-IntuneAccessAdminGroupUsers -Group $adminGroups) $administrators = [System.Collections.Generic.List[object]]::new() $warnings = [System.Collections.Generic.List[string]]::new() $workloadInventory = $null $operationalEvidence = $null $policyAnalysis = $null $auditEvidence = $null foreach ($userGroup in @($memberships | Group-Object { $_.User.Id })) { $userMemberships = @($userGroup.Group) $user = $userMemberships[0].User $membershipByGroupId = @{} foreach ($membership in $userMemberships) { $membershipByGroupId[$membership.GroupId] = $membership } $userAssignments = [System.Collections.Generic.List[object]]::new() foreach ($assignment in $assignments) { $matchingMemberships = @($assignment.RawIds.AdminGroupIds | Where-Object { $membershipByGroupId.ContainsKey([string] $_) } | ForEach-Object { $membershipByGroupId[[string] $_] }) if ($matchingMemberships.Count -eq 0) { continue } $assignmentView = $assignment | Select-Object * $hasDirectMembership = @($matchingMemberships | Where-Object MembershipType -EQ 'Direct').Count -gt 0 $assignmentView | Add-Member -NotePropertyName Applicability -NotePropertyValue $(if ($hasDirectMembership) { 'Confirmed' } else { 'NotEvaluated' }) -Force $assignmentView | Add-Member -NotePropertyName AdminGroupEvidence -NotePropertyValue @($matchingMemberships | ForEach-Object { [PSCustomObject] @{ GroupId = $_.GroupId GroupName = $_.GroupName MembershipType = $_.MembershipType EvidenceState = if ($_.MembershipType -eq 'Direct') { 'Observed' } else { 'ObservedNested' } } }) -Force $assignmentView.PSObject.TypeNames.Insert(0, 'IntuneAccess.RoleAssignment') $userAssignments.Add($assignmentView) } $effectivePermissions = @(Resolve-IntuneAccessPermissions -RoleAssignment $userAssignments.ToArray()) $administrators.Add([PSCustomObject] @{ PSTypeName = 'IntuneAccess.TenantAdministrator' User = $user AdminGroupMemberships = $userMemberships RoleAssignments = $userAssignments.ToArray() EffectivePermissions = $effectivePermissions ConfirmedAssignments = @($userAssignments | Where-Object Applicability -EQ 'Confirmed').Count ReviewAssignments = @($userAssignments | Where-Object Applicability -NE 'Confirmed').Count }) } $permissionRows = @($assignments | ForEach-Object { $assignment = $_ foreach ($action in @($assignment.Permissions)) { $friendly = ConvertFrom-IntuneAccessActionName -Action ([string] $action) [PSCustomObject] @{ RawAction = [string] $action Resource = $friendly.Resource Operation = $friendly.Operation RoleAssignmentId = $assignment.Id RoleAssignmentName = $assignment.Name RoleDefinitionId = $assignment.RoleDefinition.Id RoleDefinitionName = $assignment.RoleDefinition.DisplayName } } }) $permissions = @($permissionRows | Group-Object RawAction | ForEach-Object { $sources = @($_.Group) $assignmentIds = @($sources.RoleAssignmentId | Select-Object -Unique) $administratorCount = @($administrators | Where-Object { @($_.RoleAssignments.Id | Where-Object { $_ -in $assignmentIds }).Count -gt 0 }).Count [PSCustomObject] @{ PSTypeName = 'IntuneAccess.TenantPermission' RawAction = $_.Name Resource = $sources[0].Resource Operation = $sources[0].Operation RoleAssignments = @($sources | Sort-Object RoleAssignmentName -Unique) RoleDefinitions = @($sources | Select-Object RoleDefinitionId, RoleDefinitionName -Unique) AdministratorCount = $administratorCount } } | Sort-Object Resource, Operation) if ($assignments.Count -eq 0) { $warnings.Add('No Intune RBAC role assignments were returned by Microsoft Graph.') } elseif ($administrators.Count -eq 0) { $warnings.Add('Intune role assignments were found, but no users were returned from their resolved Admin Groups.') } if (@($memberships | Where-Object MembershipType -EQ 'Nested').Count -gt 0) { $warnings.Add('Nested Admin Group memberships are shown as NotEvaluated because Intune behaviour can depend on tenant configuration and licensing.') } if (@($adminGroups | Where-Object ResolutionState -EQ 'Unresolved').Count -gt 0) { $warnings.Add('One or more Admin Groups could not be resolved. Their raw IDs remain available in role assignment evidence.') } $warnings.Add('Hidden Microsoft Entra group membership is not evaluated because Member.Read.Hidden is not requested.') $warnings.Add('Microsoft Entra administrative roles are outside the Intune RBAC model and can provide additional Intune access not shown here.') $warnings.Add('The active March 2026 Scoped permissions model is not exposed by a documented Microsoft Graph contract used here.') if ($IncludeWorkloadAssignments -or $IncludeOperationalEvidence -or $IncludePolicyAnalysis) { $workloadInventory = Get-IntuneAccessWorkloadAssignments foreach ($warning in @($workloadInventory.Warnings)) { $warnings.Add([string] $warning) } } if ($IncludeOperationalEvidence) { $operationalEvidence = Get-IntuneAccessOperationalEvidence -Workload $workloadInventory.Workloads foreach ($warning in @($operationalEvidence.Warnings)) { $warnings.Add([string] $warning) } } if ($IncludePolicyAnalysis) { $policyAnalysis = Get-IntunePolicyConflict -Workload @($workloadInventory.Workloads) -Assignment @($workloadInventory.Assignments) foreach ($warning in @($policyAnalysis.Warnings)) { $warnings.Add([string] $warning) } } if ($IncludeAuditEvidence) { $auditEvidence = Get-IntuneAccessAuditEvents foreach ($warning in @($auditEvidence.Warnings)) { $warnings.Add([string] $warning) } } $workloadObjects = if ($null -eq $workloadInventory) { @() } else { @($workloadInventory.Workloads) } $workloadAssignments = if ($null -eq $workloadInventory) { @() } else { @($workloadInventory.Assignments) } $workloadGroups = if ($null -eq $workloadInventory) { @() } else { @($workloadInventory.Groups) } $assignmentFilters = if ($null -eq $workloadInventory) { @() } else { @($workloadInventory.AssignmentFilters) } $workloadCollectionStatus = if ($null -eq $workloadInventory) { @() } else { @($workloadInventory.CollectionStatus) } $managedDevices = if ($null -eq $operationalEvidence) { @() } else { @($operationalEvidence.ManagedDevices) } $managedUsers = if ($null -eq $operationalEvidence) { @() } else { @($operationalEvidence.ManagedUsers) } $deploymentOutcomes = if ($null -eq $operationalEvidence) { @() } else { @($operationalEvidence.DeploymentOutcomes) } $outcomeCollectionStatus = if ($null -eq $operationalEvidence) { @() } else { @($operationalEvidence.CollectionStatus) } $policySettings = if ($null -eq $policyAnalysis) { @() } else { @($policyAnalysis.PolicySettings) } $policyConflictFindings = if ($null -eq $policyAnalysis) { @() } else { @($policyAnalysis.Findings) } $policyConflictCollectionStatus = if ($null -eq $policyAnalysis) { @() } else { @($policyAnalysis.CollectionStatus) } $auditEvents = if ($null -eq $auditEvidence) { @() } else { @($auditEvidence.Events) } $auditCollectionStatus = if ($null -eq $auditEvidence) { $null } else { $auditEvidence.CollectionStatus } $permissionsUsed = @($analysisScopes) if ($null -ne $workloadInventory) { $permissionsUsed = @($permissionsUsed + @($workloadInventory.GraphPermissionsUsed) | Select-Object -Unique) } if ($null -ne $operationalEvidence) { $permissionsUsed = @($permissionsUsed + @($operationalEvidence.GraphPermissionsUsed) | Select-Object -Unique) } if ($null -ne $auditEvidence) { $permissionsUsed = @($permissionsUsed + @($auditEvidence.GraphPermissionsUsed) | Select-Object -Unique) } [PSCustomObject] @{ PSTypeName = 'IntuneAccess.TenantRbac' Tenant = $tenant Administrators = @($administrators | Sort-Object { $_.User.UserPrincipalName }) AdminGroups = @($adminGroups | Sort-Object DisplayName) RoleAssignments = @($assignments | Sort-Object Name) RoleDefinitions = @($roleDefinitions | Sort-Object DisplayName) ScopeGroups = @($scopeGroups | Sort-Object DisplayName) ScopeTags = @($scopeTags | Sort-Object DisplayName) Permissions = $permissions Memberships = $memberships WorkloadObjects = $workloadObjects WorkloadAssignments = $workloadAssignments WorkloadGroups = $workloadGroups AssignmentFilters = $assignmentFilters WorkloadCollectionStatus = $workloadCollectionStatus ManagedDevices = $managedDevices ManagedUsers = $managedUsers DeploymentOutcomes = $deploymentOutcomes OutcomeCollectionStatus = $outcomeCollectionStatus PolicySettings = $policySettings PolicyConflictFindings = $policyConflictFindings PolicyConflictCollectionStatus = $policyConflictCollectionStatus AuditEvents = $auditEvents AuditCollectionStatus = $auditCollectionStatus Warnings = $warnings.ToArray() InitialUserPrincipalName = $InitialUserPrincipalName GraphPermissionsUsed = $permissionsUsed GraphPermissionsGranted = @($context.Scopes) GeneratedAt = [DateTimeOffset]::Now ToolVersion = $script:IntuneAccessVersion } } |