Public/Get-RBACforAppEntry.ps1
|
<# .SYNOPSIS Gets Exchange Online RBAC role assignments for application roles (the roles that New-RBACforAppEntry creates). .DESCRIPTION Get-RBACforAppEntry retrieves Exchange Online management role assignments whose role is an application role (named "Application <permission>", e.g. "Application Mail.Send"). It returns only assignments scoped the way this module creates them: group-scoped or custom-recipient-scoped entries. These are the assignments produced by New-RBACforAppEntry. By default service-principal application role assignments are returned, limited to RecipientWriteScope values of Group or CustomRecipientScope. You can narrow the results to a specific application (by display name, AppId, or service principal object id) and/or to specific roles. Filtering by application is done client-side because Get-ManagementRoleAssignment has no -App parameter; the resolved identity is matched against each assignment's RoleAssigneeName and Name. .PARAMETER RegisteredAppName Display name of the application / service principal to filter assignments by. This is matched (via the resolved service principal) against the assignment's assignee and name. .PARAMETER AppId Application (client) id of the application to filter by. GUID-validated. .PARAMETER SpObjectId Object id of the service principal to filter by. GUID-validated. .PARAMETER Role One or more application roles to filter by. Short names such as Mail.Send are accepted and normalized to "Application Mail.Send". .PARAMETER Enabled Return only enabled ($true) or only disabled ($false) assignments. Omit to return both. .PARAMETER RoleAssigneeType Filter by the assignment's RoleAssigneeType. Defaults to 'ServicePrincipal' (application assignments). Use 'All' to return every assignee type within the supported Group / CustomRecipientScope recipient scopes, or pass a specific type to narrow. .EXAMPLE Get-RBACforAppEntry Returns service-principal application role assignments that are group-scoped or use a custom recipient scope (the default behavior). .EXAMPLE Get-RBACforAppEntry -RoleAssigneeType All Returns application role assignments regardless of assignee type, still limited to Group / CustomRecipientScope recipient scopes. .EXAMPLE Get-RBACforAppEntry -RegisteredAppName 'Contoso Mail App' -Role 'Mail.Send' Returns the Application Mail.Send assignments scoped to the resolved Contoso Mail App. .EXAMPLE Get-RBACforAppEntry -AppId '11111111-2222-3333-4444-555555555555' | Format-Table Name,Role,Scope Filters by AppId and formats the key columns. .OUTPUTS PSCustomObject One object per matching assignment with Name, Role, assignee, scope, enabled state, and identity fields. .NOTES Requires a connected Exchange Online session (Connect-ExchangeOnline) for Get-ManagementRoleAssignment. When an application filter is supplied, a connected Microsoft Graph session (Connect-MgGraph) is also required to resolve the service principal. Companion to New-RBACforAppEntry. #> function Get-RBACforAppEntry { [CmdletBinding(DefaultParameterSetName = 'All')] [OutputType([pscustomobject])] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ByName')] [Alias('DisplayName','Name')] [ValidateNotNullOrEmpty()] [string] $RegisteredAppName, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ByAppId')] [Alias('ClientId','ApplicationId')] [ValidatePattern('^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$')] [string] $AppId, [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'BySpObjectId')] [Alias('Id','ObjectId','ServicePrincipalId')] [ValidatePattern('^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$')] [string] $SpObjectId, [Parameter()] [ValidateNotNullOrEmpty()] [string[]] $Role, [Parameter()] [bool] $Enabled, [Parameter()] [ValidateSet('All','ServicePrincipal','User','SecurityGroup','RoleGroup','RoleAssignmentPolicy','ForeignSecurityPrincipal','LinkedRoleGroup','Computer')] [string] $RoleAssigneeType = 'ServicePrincipal' ) process { # --- Resolve the service principal when an application filter is requested. $sp = $null try { switch ($PSCmdlet.ParameterSetName) { 'BySpObjectId' { $sp = Get-MgServicePrincipal -ServicePrincipalId $SpObjectId -ErrorAction Stop } 'ByAppId' { $matchesRes = @(Get-MgServicePrincipal -Filter "appId eq `'$AppId`'" -ErrorAction Stop) if ($matchesRes.Count -eq 0) { throw "No service principal found for AppId '$AppId'." } $sp = $matchesRes[0] } 'ByName' { $matchesRes = @(Get-MgServicePrincipal -Filter "displayName eq `'$RegisteredAppName`'" -ErrorAction Stop) if ($matchesRes.Count -eq 0) { throw "No service principal found for displayName '$RegisteredAppName'." } if ($matchesRes.Count -gt 1) { $ids = ($matchesRes | Select-Object -First 10 -ExpandProperty Id) -join ', ' throw "Ambiguous displayName '$RegisteredAppName' matched $($matchesRes.Count) service principals. Re-run with -AppId or -SpObjectId. Example SP objectIds: $ids" } $sp = $matchesRes[0] } } } catch { Write-Error -Message $_.Exception.Message return } # --- Retrieve assignments, filtered by role where possible. $rolesNormalized = if ($PSBoundParameters.ContainsKey('Role')) { @($Role | ForEach-Object { ConvertTo-AppRole $_ }) } else { @() } $assignments = if ($rolesNormalized.Count -gt 0) { # Query per requested role so EXO does the role filtering for us. foreach ($r in $rolesNormalized) { Get-ManagementRoleAssignment -Role $r -ErrorAction SilentlyContinue } } else { # No role filter: get everything and keep only application roles. Get-ManagementRoleAssignment -ErrorAction SilentlyContinue | Where-Object { $_.Role -like 'Application *' } } # --- Optional enabled-state filter. if ($PSBoundParameters.ContainsKey('Enabled')) { $assignments = $assignments | Where-Object { $_.Enabled -eq $Enabled } } # --- Keep only the recipient scopes this module creates/uses. $assignments = $assignments | Where-Object { $_.RecipientWriteScope -in @('Group', 'CustomRecipientScope') } # --- Assignee-type filter (default: ServicePrincipal; 'All' returns every type). if ($RoleAssigneeType -ne 'All') { $assignments = $assignments | Where-Object { $_.RoleAssigneeType -eq $RoleAssigneeType } } # --- Optional application filter (client-side: Get-ManagementRoleAssignment has no -App). if ($sp) { $needles = @($sp.DisplayName, ("{0}_SP" -f $sp.DisplayName), $sp.AppId, $sp.Id) | Where-Object { $_ } $assignments = $assignments | Where-Object { $assignee = [string]$_.RoleAssigneeName $name = [string]$_.Name $hit = $false foreach ($n in $needles) { if (($assignee -and $assignee -like "*$n*") -or ($name -and $name -like "*$n*")) { $hit = $true; break } } $hit } } foreach ($a in $assignments) { if (-not $a) { continue } [pscustomobject][ordered]@{ Name = $a.Name Role = $a.Role RoleAssigneeName = $a.RoleAssigneeName RoleAssigneeType = $a.RoleAssigneeType Scope = $a.CustomRecipientWriteScope RecipientScope = $a.RecipientWriteScope Enabled = $a.Enabled Guid = $a.Guid Identity = $a.Identity } } } } |