Last active
August 30, 2024 08:05
-
-
Save jpawlowski/ca1bde7e979f367e8007b056bc032b6e to your computer and use it in GitHub Desktop.
Create a custom role in Microsoft Entra that grants the ability to consent for delegated permissions and application permissions, including most application permissions for Microsoft Graph, except for a few sensitive permissions. Azure AD Graph permissions are NOT included.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#PSScriptInfo | |
.VERSION 1.1.0 | |
.GUID a17ec91c-0f75-42ab-b4ef-8766c1a25fca | |
.AUTHOR Julian Pawlowski | |
.COMPANYNAME Julian Pawlowski | |
.COPYRIGHT © 2024 Julian Pawlowski | |
.TAGS | |
.LICENSEURI https://opensource.org/license/MIT | |
.PROJECTURI https://gist.github.com/jpawlowski/ca1bde7e979f367e8007b056bc032b6e | |
.ICONURI | |
.EXTERNALMODULEDEPENDENCIES 'Microsoft.Graph.Identity.SignIns','Microsoft.Graph.Identity.Governance','Microsoft.Graph.Applications' | |
.REQUIREDSCRIPTS | |
.EXTERNALSCRIPTDEPENDENCIES | |
.RELEASENOTES | |
Version 1.1.0 (2024-05-12) | |
- Add directory role assignment validation. | |
Version 1.0.1 (2024-05-10) | |
- Add module dependencies. | |
Version 1.0.0 (2024-05-03) | |
- Initial release. | |
#> | |
<# | |
.SYNOPSIS | |
Create a custom role in Microsoft Entra that grants the ability to consent for delegated permissions and application permissions, including most application permissions for Microsoft Graph, except for a few sensitive permissions. Azure AD Graph permissions are NOT included. | |
.DESCRIPTION | |
This script creates a custom role in Microsoft Entra that grants the ability to consent for delegated permissions and application permissions, including most application permissions for Microsoft Graph, except for a few sensitive permissions. Azure AD Graph permissions are NOT included. Note that to approve Microsoft Graph application permissions, Microsoft Entra roles Cloud Application Administrator and Application Administrator MUST NOT be active as otherwise, their exclusion policy will take precedence. | |
#> | |
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Identity.SignIns'; ModuleVersion = '2.0.0' } | |
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Identity.Governance'; ModuleVersion = '2.0.0' } | |
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Applications'; ModuleVersion = '2.0.0' } | |
Connect-MgGraph -ContextScope Process -Scopes @( | |
'Application.Read.All', | |
'DelegatedPermissionGrant.Read.All', | |
'RoleManagement.ReadWrite.Directory', | |
'Policy.ReadWrite.PermissionGrant' | |
) | |
#region Validate active directory role assignment | |
$requiredDirectoryRoles = New-Object System.Collections.ArrayList # Use an ArrayList to avoid array flattening | |
# Either of the following roles is required | |
$null = $requiredDirectoryRoles.Add(@( | |
'Privileged Role Administrator', | |
'Global Administrator' | |
)) | |
$directoryRoles = Get-MgDirectoryRole -ErrorAction Stop | Group-Object -Property DisplayName -AsHashTable -AsString | |
$hasActiveRoleAssignment = $false | |
for ($i = 0; $i -lt $requiredDirectoryRoles.Count; $i++) { | |
$roleGroup = $requiredDirectoryRoles[$i] | |
$rolesToCheck = if ($roleGroup -is [array]) { $roleGroup } else { @($roleGroup) } | |
$hasActiveRole = $false | |
foreach ($role in $rolesToCheck) { | |
Write-Debug "Checking if $($CurrentUser.UserPrincipalName) has an active assignment for the directory role '$role'." | |
$directoryRole = $directoryRoles[$role] | |
if ($null -ne $directoryRole -and (Get-MgDirectoryRoleMember -DirectoryRoleId $directoryRole.Id).Id -contains $CurrentUser.Id) { | |
$hasActiveRole = $true | |
Write-Verbose "$($CurrentUser.UserPrincipalName) HAS an active assignment for the directory role '$role'." | |
break | |
} | |
} | |
if (-not $hasActiveRole) { | |
Write-Debug "$($CurrentUser.UserPrincipalName) DOES NOT HAVE an active assignment for ANY of the following directory roles: $($rolesToCheck -join ', ')." | |
break | |
} | |
elseif ($i -eq $requiredDirectoryRoles.Count - 1) { | |
$hasActiveRoleAssignment = $true | |
} | |
} | |
if (-not $hasActiveRoleAssignment) { | |
$roleRequirements = $requiredDirectoryRoles | ForEach-Object { | |
if ($_ -is [array]) { | |
"at least one of the following roles: " + ($_ -join ', ') | |
} | |
else { | |
"'$_'" | |
} | |
} | |
$roleRequirements = $roleRequirements -join ' AND ' | |
Write-Error "$($CurrentUser.UserPrincipalName) does not have an active assignment for all required directory roles. The user must have an active assignment for $roleRequirements." | |
exit 1 | |
} | |
#endregion | |
#region Create a new permission grant policy | |
$PermissionGrantPolicy = New-MgPolicyPermissionGrantPolicy -Id 'custom-appconsent-admin' -Description 'Permissions consentable by Privileged Application Consent Administrators.' -DisplayName 'Custom App Consent Admin Policy' -ErrorAction Stop | |
# Include all delegated and application permissions | |
New-MgPolicyPermissionGrantPolicyInclude -PermissionGrantPolicyId $PermissionGrantPolicy.Id -PermissionType 'delegated' -PermissionClassification 'all' -ClientApplicationsFromVerifiedPublisherOnly:$false | |
New-MgPolicyPermissionGrantPolicyInclude -PermissionGrantPolicyId $PermissionGrantPolicy.Id -PermissionType 'application' -PermissionClassification 'all' -ClientApplicationsFromVerifiedPublisherOnly:$false | |
# Exclude all Azure Graph application permissions | |
New-MgPolicyPermissionGrantPolicyExclude -PermissionGrantPolicyId $PermissionGrantPolicy.Id -ResourceApplication '00000002-0000-0000-c000-000000000000' -PermissionType 'application' | |
#endregion | |
#region Exclude specific application permissions from Microsoft Graph | |
$ServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" -ErrorAction Stop | |
$ExcludedAppRoleValues = @( | |
'AdministrativeUnit.ReadWrite.All' | |
'Application.ReadWrite.All' | |
'AppRoleAssignment.ReadWrite.All' | |
'AuthenticationContext.ReadWrite.All' | |
'BillingConfiguration.ReadWrite.All' | |
'ConsentRequest.ReadWrite.All' | |
'CrossTenantUserProfileSharing.ReadWrite.All' | |
'CustomAuthenticationExtension.ReadWrite.All' | |
'DelegatedAdminRelationship.ReadWrite.All' | |
'DelegatedPermissionGrant.ReadWrite.All' | |
'Device.ReadWrite.All' | |
'DeviceManagementApps.ReadWrite.All' | |
'DeviceManagementConfiguration.ReadWrite.All' | |
'DeviceManagementManagedDevices.PrivilegedOperations.All' | |
'DeviceManagementManagedDevices.ReadWrite.All' | |
'DeviceManagementRBAC.ReadWrite.All' | |
'DeviceManagementServiceConfig.ReadWrite.All' | |
'Directory.ReadWrite.All' | |
'DirectoryRecommendations.ReadWrite.All' | |
'Domain.ReadWrite.All' | |
'eDiscovery.ReadWrite.All' | |
'ExternalConnection.ReadWrite.All' | |
'ExternalConnection.ReadWrite.OwnedBy' | |
'Files.ReadWrite.All' | |
'Group.ReadWrite.All' | |
'GroupMember.ReadWrite.All' | |
'IdentityProvider.ReadWrite.All' | |
'IdentityRiskEvent.ReadWrite.All' | |
'IdentityRiskyServicePrincipal.ReadWrite.All' | |
'IdentityRiskyUser.ReadWrite.All' | |
'MultiTenantOrganization.ReadWrite.All' | |
'OnPremDirectorySynchronization.ReadWrite.All' | |
'OnPremisesPublishingProfiles.ReadWrite.All' | |
'Organization.ReadWrite.All' | |
'OrganizationalBranding.ReadWrite.All' | |
'Policy.ReadWrite.AccessReview' | |
'Policy.ReadWrite.ApplicationConfiguration' | |
'Policy.ReadWrite.AuthenticationFlows' | |
'Policy.ReadWrite.AuthenticationMethod' | |
'Policy.ReadWrite.Authorization' | |
'Policy.ReadWrite.ConditionalAccess' | |
'Policy.ReadWrite.ConsentRequest' | |
'Policy.ReadWrite.CrossTenantAccess' | |
'Policy.ReadWrite.ExternalIdentities' | |
'Policy.ReadWrite.FeatureRollout' | |
'Policy.ReadWrite.FedTokenValidation' | |
'Policy.ReadWrite.IdentityProtection' | |
'Policy.ReadWrite.PermissionGrant' | |
'Policy.ReadWrite.SecurityDefaults' | |
'Policy.ReadWrite.TrustFramework' | |
'PrivilegedAccess.ReadWrite.AzureAD' | |
'PrivilegedAccess.ReadWrite.AzureADGroup' | |
'PrivilegedAccess.ReadWrite.AzureResources' | |
'PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup' | |
'PrivilegedEligibilitySchedule.ReadWrite.AzureADGroup' | |
'PublicKeyInfrastructure.ReadWrite.All' | |
'RoleAssignmentSchedule.ReadWrite.Directory' | |
'RoleEligibilitySchedule.ReadWrite.Directory' | |
'RoleManagement.ReadWrite.CloudPC' | |
'RoleManagement.ReadWrite.Directory' | |
'RoleManagement.ReadWrite.Exchange' | |
'RoleManagementAlert.ReadWrite.Directory' | |
'RoleManagementPolicy.ReadWrite.AzureADGroup' | |
'RoleManagementPolicy.ReadWrite.Directory' | |
'SharePointTenantSettings.ReadWrite.All' | |
'Synchronization.ReadWrite.All' | |
'TrustFrameworkKeySet.ReadWrite.All' | |
'User.EnableDisableAccount.All' | |
'User.Export.All' | |
'User.Invite.All' | |
'User.ManageIdentities.All' | |
'User.ReadWrite.All' | |
'UserAuthenticationMethod.ReadWrite.All' | |
) | |
# Convert app role values to app role IDs | |
$ExcludedPermissions = $ExcludedAppRoleValues | ForEach-Object { | |
$item = $_ | |
$roles = $ServicePrincipal.AppRoles | Where-Object { $_.Value -eq $item } | |
if ($roles) { | |
$roles | ForEach-Object { $_.Id } | |
} | |
else { | |
Write-Error "No matching app role found for value: $item" | |
} | |
} | |
if ($ExcludedPermissions.Count -gt 100) { | |
Throw "Excluded permissions count exceeds 100" | |
} | |
else { | |
$params = @{ | |
PermissionType = 'application' | |
ResourceApplication = $ServicePrincipal.AppId | |
Permissions = [array]$ExcludedPermissions | |
ErrorAction = 'Stop' | |
} | |
New-MgPolicyPermissionGrantPolicyExclude -PermissionGrantPolicyId $PermissionGrantPolicy.Id -BodyParameter $params | |
} | |
#endregion | |
#region Create new custom admin role | |
$params = @{ | |
DisplayName = 'Privileged Application Consent Administrator' | |
Description = 'This role grants the ability to consent for delegated permissions and application permissions, including most application permissions for Microsoft Graph, except for a few sensitive permissions. Azure AD Graph permissions are NOT included. Note that to approve Microsoft Graph application permissions, Microsoft Entra roles Cloud Application Administrator and Application Administrator MUST NOT be active as otherwise, their exclusion policy will take precedence.' | |
TemplateId = (New-Guid).Guid | |
IsEnabled = $true | |
RolePermissions = @( | |
@{ | |
AllowedResourceActions = @( | |
'microsoft.directory/servicePrincipals/credentials/update' | |
'microsoft.directory/servicePrincipals/managePermissionGrantsForAll.' + $PermissionGrantPolicy.Id | |
) | |
} | |
) | |
ErrorAction = 'Stop' | |
} | |
$customAdmin = New-MgRoleManagementDirectoryRoleDefinition @params | |
#endregion |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment