Skip to content

Instantly share code, notes, and snippets.

@jpawlowski
Last active May 12, 2024 12:50
Show Gist options
  • Save jpawlowski/07d0ad578d50027c962c63228fd5c1a6 to your computer and use it in GitHub Desktop.
Save jpawlowski/07d0ad578d50027c962c63228fd5c1a6 to your computer and use it in GitHub Desktop.
Remove delegated permissions for all users (also known as admin consent) to Microsoft Graph Explorer and Microsoft Graph PowerShell.
<#PSScriptInfo
.VERSION 1.0.0
.GUID d24ad04e-2bbc-4087-a8b3-9da63d79edc4
.AUTHOR Julian Pawlowski
.COMPANYNAME Julian Pawlowski
.COPYRIGHT © 2024 Julian Pawlowski
.TAGS
.LICENSEURI https://opensource.org/license/MIT
.PROJECTURI https://gist.github.com/jpawlowski/07d0ad578d50027c962c63228fd5c1a6
.ICONURI
.EXTERNALMODULEDEPENDENCIES 'Microsoft.Graph.Identity.SignIns','Microsoft.Graph.Identity.DirectoryManagement','Microsoft.Graph.Applications','Microsoft.Graph.Users'
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
Version 1.0.0 (2024-05-12)
- Initial release.
#>
<#
.SYNOPSIS
Remove delegated permissions for all users (also known as admin consent) to Microsoft Graph Explorer and Microsoft Graph PowerShell.
.DESCRIPTION
This script removes delegated permissions for all users (also known as admin consent) to Microsoft Graph Explorer and Microsoft Graph PowerShell.
The script requires the connecting user to have an active assignment for at least one of the following directory roles:
'Cloud Application Administrator', 'Application Administrator', 'Global Administrator'.
.PARAMETER GraphPowerShell
Remove delegated permissions for Microsoft Graph PowerShell.
.PARAMETER GraphExplorer
Remove delegated permissions for Microsoft Graph Explorer.
.PARAMETER Scopes
Specify the scopes to remove. If not specified, the script will prompt for each scope to remove.
If the 'all' scope is specified, all scopes will be removed.
.PARAMETER ResetToDefault
Reset the delegated permissions to the default scopes.
.PARAMETER DefaultScopes
Specify the default scopes to reset the delegated permissions.
If not specified, the script will use the following default scopes for Microsoft Graph PowerShell and Microsoft Graph Explorer:
- offline_access
- openid
- profile
- User.Read
- Directory.Read.All (required to unconcent for delegated permissions)
- DelegatedPermissionGrant.ReadWrite.All (required to unconcent for delegated permissions)
#>
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Identity.SignIns'; ModuleVersion = '2.0.0'}
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Identity.DirectoryManagement'; ModuleVersion = '2.0.0'}
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Applications'; ModuleVersion = '2.0.0' }
#Requires -Modules @{ ModuleName = 'Microsoft.Graph.Users'; ModuleVersion = '2.0.0' }
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param(
[Parameter(Mandatory = $true, ParameterSetName = 'GraphPowerShell')]
[Parameter(Mandatory = $true, ParameterSetName = 'GraphPowerShellAndGraphExplorer')]
[switch]$GraphPowerShell,
[Parameter(Mandatory = $true, ParameterSetName = 'GraphExplorer')]
[Parameter(Mandatory = $true, ParameterSetName = 'GraphPowerShellAndGraphExplorer')]
[switch]$GraphExplorer,
[Parameter(ParameterSetName = 'GraphPowerShell')]
[Parameter(ParameterSetName = 'GraphExplorer')]
[Parameter(ParameterSetName = 'GraphPowerShellAndGraphExplorer')]
[array]$Scopes = @(),
[Parameter(ParameterSetName = 'GraphPowerShell')]
[Parameter(ParameterSetName = 'GraphExplorer')]
[Parameter(ParameterSetName = 'GraphPowerShellAndGraphExplorer')]
[switch]$ResetToDefault,
[Parameter(ParameterSetName = 'GraphPowerShell')]
[Parameter(ParameterSetName = 'GraphExplorer')]
[Parameter(ParameterSetName = 'GraphPowerShellAndGraphExplorer')]
[array]$DefaultScopes
)
#region Connect to Microsoft Graph
Connect-MgGraph -ContextScope Process -NoWelcome -Scopes @(
'AppRoleAssignment.ReadWrite.All'
'Directory.Read.All'
'DelegatedPermissionGrant.ReadWrite.All'
)
$context = Get-MgContext
if ($null -eq $context.AuthType -or $context.AuthType -ne 'Delegated') {
Write-Error "This script requires to be connected with delegated permissions. That means you need to connect with a user account."
exit 1
}
$CurrentUser = Get-MgUser -UserId (Get-MgContext).Account
#endregion
#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(@(
'Cloud Application Administrator',
'Application 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 Reset admin consent
$DefaultScopesSet = @(
'offline_access'
'openid'
'profile'
'User.Read'
# Allow users to withdraw their own delegated permissions that were previously consented to.
'Directory.Read.All'
'DelegatedPermissionGrant.ReadWrite.All'
)
$MgServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" -ErrorAction Stop
$(
if ($GraphPowerShell) { '14d82eec-204b-4c2f-b7e8-296a70dab67e' }
if ($GraphExplorer) { 'de8bc8b5-d9f9-48b1-a8ad-b748da725064' }
) | ForEach-Object {
$ServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$_'" -ErrorAction Stop
if ($null -eq $ServicePrincipal) {
Write-Host "Service principal with AppId '$_' not found."
return
}
Write-Host "`n$($ServicePrincipal.DisplayName)`n" -ForegroundColor Yellow
Write-Verbose "Working on: $($ServicePrincipal.DisplayName) (Id: $($ServicePrincipal.Id), AppId: $($ServicePrincipal.AppId))."
$Oauth2PermissionGrant = Get-MgOauth2PermissionGrant -Filter "resourceId eq '$($MgServicePrincipal.Id)' and clientId eq '$($ServicePrincipal.Id)' and ConsentType eq 'AllPrincipals'"
if ($null -eq $Oauth2PermissionGrant) {
if ($ResetToDefault) {
$params = @{
ResourceId = $MgServicePrincipal.Id
ClientId = $ServicePrincipal.Id
ConsentType = 'AllPrincipals'
Scope = if ($DefaultScopes) {
$DefaultScopes
}
else {
$DefaultScopesSet
}
}
if (
$PSCmdlet.ShouldContinue(
"Do you want to create new admin consent delegated permissions for $($ServicePrincipal.DisplayName) with the following scopes:`n $($params.Scope -join "`n ")",
"Confirm to create new admin consent delegated permissions for $($ServicePrincipal.DisplayName)"
)
) {
$params.Scope = $params.Scope -join ' '
$null = New-MgOauth2PermissionGrant @params
}
return
}
Write-Host "No admin consent delegated permissions have been given to $($ServicePrincipal.DisplayName)" -ForegroundColor White
return
}
$CurrentScopes = ($Oauth2PermissionGrant.Scope).Trim().Split(' ')
$NewScopes = if ($ResetToDefault) {
if ($DefaultScopes) {
$DefaultScopes
}
else {
$DefaultScopesSet
}
}
else { @() }
if ($Scopes.Count -gt 0) {
$NewScopes = $CurrentScopes | ForEach-Object {
if ($_ -in $Scopes -or 'all' -in $Scopes) {
Write-Host "(Remove) $_" -ForegroundColor Red
}
else {
Write-Host "(Keep) $_" -ForegroundColor Green
$_
}
}
}
else {
$CurrentScopes | ForEach-Object {
if (
$PSCmdlet.ShouldProcess(
"Remove admin consent delegated permission '$_' for $($ServicePrincipal.DisplayName)",
"$($_) - Do you want to remove admin consent? Say no, if you want to keep the admin consent.",
"Confirm to remove admin consent for $_"
)
) {
Write-Host "(Remove) $($_)" -ForegroundColor Red
}
elseif ($WhatIfPreference) {
# Do nothing
}
else {
Write-Host "(Keep) $($_)" -ForegroundColor Green
$NewScopes += $_
}
}
}
if ($NewScopes.Count -gt 0) { $NewScopes = $NewScopes | Sort-Object -Unique }
if ($CurrentScopes.Count -eq $NewScopes.Count -and ($CurrentScopes | Where-Object { $NewScopes -notcontains $_ }).Count -eq 0) {
Write-Host "No changes for $($ServicePrincipal.DisplayName)." -ForegroundColor White
return
}
if (-Not $WhatIfPreference) {
if ($NewScopes.Count -gt 0) {
if (
$PSCmdlet.ShouldContinue(
"Do you want to update admin consent delegated permissions for $($ServicePrincipal.DisplayName) with the following scopes:`n $($NewScopes -join "`n ")",
"Confirm to update admin consent delegated permissions for $($ServicePrincipal.DisplayName)"
)
) {
Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId $Oauth2PermissionGrant.Id -Scope ($NewScopes -join ' ')
}
}
else {
if (
$PSCmdlet.ShouldContinue(
"Do you want to remove all admin consent delegated permissions for $($ServicePrincipal.DisplayName)?",
"Confirm to remove all admin consent delegated permissions for $($ServicePrincipal.DisplayName)"
)
) {
Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $Oauth2PermissionGrant.Id
}
}
}
}
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment