APP-03: Internal App Registration Permissions
Overview
Internal app registrations are applications you created and registered in your own Entra ID tenant. You own the code and control the configuration. Even so, misconfigured Graph API permissions can expose excessive access to mailboxes, directory data, or role management, often more than the application actually needs.
This control requires quarterly review of all internal app registrations that hold high-privilege Graph permissions, with documented business justification for each elevated permission retained. Internal apps are distinct from third-party enterprise apps consented from external vendors; those are covered by APP-06: Third-Party Enterprise App Permissions.
"Internal app registrations are applications you created and control. While you own the code, misconfigured permissions can expose excessive access. Regular review ensures your own apps only have necessary permissions."
Prerequisites
Required Roles
- Global Administrator - Full access to review any app registration
- Application Administrator - Can review and modify most app registrations
- Cloud Application Administrator - Can review app registrations (excluding app proxy apps)
- Privileged Role Administrator - Required for apps holding role management permissions
Required Licenses
- Microsoft Entra ID (any tier)
Time Estimate
- Initial Audit: 30-45 minutes
- Per-App Review: 10-15 minutes
- Documentation: 15-30 minutes
Step-by-Step Instructions
Step 1: Identify Internal App Registrations
Internal app registrations are found under App registrations, not Enterprise applications. Enterprise applications include both your own apps and third-party apps you have consented to.
- Navigate to Microsoft Entra admin center (https://entra.microsoft.com)
- Go to Identity → Applications → App registrations
- Click All applications to see everything registered in your tenant
- Filter by Owned applications to narrow to apps your organization owns
Note: Enterprise applications that correspond to your own app registrations appear in both blades. Third-party vendor apps appear only under Enterprise applications. If an app has no corresponding App registrations entry owned by your organization, it is a third-party app. Review it under APP-06: Third-Party Enterprise App Permissions instead.
Step 2: Flag High-Privilege Graph Permissions
Review the API permissions configured on each internal app registration. The following permissions require elevated scrutiny and documented business justification:
Permissions to Flag for Review
| Permission | Risk | Description |
|---|---|---|
Mail.ReadWrite.All | High | Read and write all mailboxes in the tenant |
Directory.ReadWrite.All | High | Full read/write access to Entra ID directory data |
RoleManagement.ReadWrite.Directory | High | Assign or remove any admin role in the tenant |
AppRoleAssignment.ReadWrite.All | High | Grant the app additional permissions at runtime |
Mail.ReadWrite (Application) | High | Read/write all mailboxes without user context |
Files.ReadWrite.All (Application) | High | Access all OneDrive and SharePoint files |
User.ReadWrite.All (Application) | High | Modify all user accounts |
Audit via PowerShell
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.Read.All", "Directory.Read.All"
# Permissions to flag
$flaggedPermissions = @(
"Mail.ReadWrite.All",
"Directory.ReadWrite.All",
"RoleManagement.ReadWrite.Directory",
"AppRoleAssignment.ReadWrite.All",
"Mail.ReadWrite",
"Files.ReadWrite.All",
"User.ReadWrite.All",
"Group.ReadWrite.All"
)
# Get Microsoft Graph service principal for permission resolution
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
# Get all app registrations owned by your organization
$appRegistrations = Get-MgApplication -All
$results = @()
foreach ($app in $appRegistrations) {
# Get the corresponding service principal
$sp = Get-MgServicePrincipal -Filter "appId eq '$($app.AppId)'"
if (-not $sp) { continue }
# Get granted app role assignments (application permissions)
$appRoles = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id
foreach ($role in $appRoles) {
$permission = $graphSp.AppRoles | Where-Object { $_.Id -eq $role.AppRoleId }
if ($permission.Value -in $flaggedPermissions) {
$results += [PSCustomObject]@{
AppName = $app.DisplayName
AppId = $app.AppId
Permission = $permission.Value
GrantedOn = $role.CreatedDateTime
}
}
}
}
$results | Export-Csv -Path "InternalApps-HighPrivPermissions.csv" -NoTypeInformation
$results | Format-Table -AutoSize
Step 3: Document Business Justification
For each internal app with a flagged permission, record the following in your app inventory or ITSM system:
- Application name and App ID
- Permission name and type (delegated vs. application)
- Business purpose: What function requires this permission?
- Data access scope: Which mailboxes, users, or directory objects does the app actually touch?
- Owner: Which team or individual owns and maintains this app?
- Last reviewed date
- Justification decision: Retain, reduce, or revoke
If a less-privileged alternative is available, prefer it:
| Current Permission | Least-Privilege Alternative |
|---|---|
Mail.ReadWrite.All | Mail.ReadWrite.Shared + Application Access Policy |
Files.ReadWrite.All | Sites.Selected + specific site permissions |
User.ReadWrite.All | Delegated flow or scoped admin role |
Directory.ReadWrite.All | Specific read/write permissions scoped to needed object types |
Step 4: Reduce Permissions Where Possible
Remove a Permission via Entra Admin Center
- Navigate to App registrations → Select the application
- Go to API permissions
- Select the permission to remove
- Click Remove permission
- Re-grant admin consent for the updated, reduced permission set
Restrict Mail Access to Specific Mailboxes
If Mail.ReadWrite.All is necessary but can be scoped:
Connect-ExchangeOnline
# Create a mail-enabled security group for allowed mailboxes
New-DistributionGroup -Name "AppName-MailboxAccess" -Type Security
# Add only the mailboxes the app legitimately needs
Add-DistributionGroupMember -Identity "AppName-MailboxAccess" -Member "service@contoso.com"
# Create an Application Access Policy to restrict the app
New-ApplicationAccessPolicy `
-AppId "your-internal-app-id" `
-PolicyScopeGroupId "AppName-MailboxAccess" `
-AccessRight RestrictAccess `
-Description "Restrict internal app to specific mailboxes only"
# Verify the policy works
Test-ApplicationAccessPolicy -Identity "service@contoso.com" -AppId "your-internal-app-id"
Use Sites.Selected Instead of Sites.ReadWrite.All
$siteId = "contoso.sharepoint.com,<site-guid>,<web-guid>"
$appId = "your-internal-app-id"
$params = @{
roles = @("write")
grantedToIdentities = @(
@{
application = @{
id = $appId
displayName = "Your Internal App"
}
}
)
}
New-MgSitePermission -SiteId $siteId -BodyParameter $params
Step 5: Establish Quarterly Review Cadence
APP-03 requires a formal quarterly review cycle. At each review:
- Re-run the PowerShell audit to detect new or changed permissions
- Verify that justifications remain current and accurate
- Remove permissions for apps that are decommissioned or no longer active
- Record the review date and reviewer in your app inventory
Suggested schedule:
| Review Item | Frequency |
|---|---|
| Full permission audit of internal apps | Quarterly |
| Newly registered internal apps | At registration |
| Decommissioned app cleanup | At decommission |
| Business justification refresh | Annually (minimum) |
Verification Checklist
- All internal app registrations are inventoried and have an assigned owner
- Apps holding
Mail.ReadWrite.All,Directory.ReadWrite.All, orRoleManagement.ReadWrite.Directoryare identified and flagged - Business justification is documented for every flagged permission
- Least-privilege alternatives have been evaluated for each flagged permission
- Application Access Policies are configured where mailbox-scoped access is sufficient
- Decommissioned internal apps have been removed or disabled
- Quarterly review schedule is established and the first review is complete
- Review findings are recorded in your ITSM or app inventory system
Troubleshooting
Issue: App stops working after permission reduction
Cause: The application actively uses the permission that was removed.
Solution:
- Check application error logs and Entra sign-in logs for failed API calls
- Identify the specific Graph calls failing and the minimum permission required
- If a broad permission is genuinely necessary, document the business justification and add compensating controls (Application Access Policy, enhanced audit logging)
Issue: Cannot determine the purpose of an internal app
Cause: The app may have been created by a former employee or undocumented project.
Solution:
- Check audit logs for the original registration event:
Search-UnifiedAuditLog -StartDate (Get-Date).AddYears(-1) -EndDate (Get-Date) -Operations "Add application" -RecordType AzureActiveDirectory - Review sign-in logs for recent activity
- Check the Owners list on the app registration
- If no owner or justification can be found after 30 days, disable (do not delete) the app and monitor for any reported breakage before removal
Issue: Permission reappears after removal
Cause: The app or a developer is re-requesting the permission through a consent flow.
Solution:
- Restrict user consent to prevent re-consent (see APP-08)
- Configure the admin consent workflow so that future re-grants require explicit approval (see APP-04)
- Document the permission as "denied" in the app inventory
Issue: App has delegated permissions granted by individual users
Cause: Users consented to delegated permissions outside of the admin consent process.
Solution:
- Query OAuth2 permission grants:
Get-MgOauth2PermissionGrant -Filter "clientId eq 'service-principal-id'" - Revoke individual user consents:
Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId "<grant-id>"
Related Resources
- Microsoft Graph permissions reference
- Application Access Policies for Exchange
- Sites.Selected permission for SharePoint
- APP-06: Third-Party Enterprise App Permissions
- APP-04: Enable Admin Consent Workflow
- APP-08: Restrict User Application Consent
Last updated: January 2025