Last active
May 12, 2024 12:58
-
-
Save jpawlowski/7d4f2e76851349800e1cf86ff00ca43c to your computer and use it in GitHub Desktop.
This script removes delegated permissions for a user 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'. You may specify a list of …
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.0.0 | |
.GUID fa3a4d56-2d51-465c-b6f7-6c8518b51e2f | |
.AUTHOR Julian Pawlowski | |
.COMPANYNAME Julian Pawlowski | |
.COPYRIGHT © 2024 Julian Pawlowski | |
.TAGS | |
.LICENSEURI https://opensource.org/license/MIT | |
.PROJECTURI https://gist.github.com/jpawlowski/7d4f2e76851349800e1cf86ff00ca43c | |
.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-10) | |
- Initial release. | |
#> | |
<# | |
.SYNOPSIS | |
Remove delegated permissions for a user to Microsoft Graph Explorer and Microsoft Graph PowerShell. | |
.DESCRIPTION | |
This script removes delegated permissions for a user 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'. | |
You may specify a list of user IDs, or use the 'All' parameter to remove delegated permissions for all users. | |
.PARAMETER UserId | |
The ID of the user to remove the delegated permissions for. | |
Use the 'All' parameter to remove delegated permissions for all users. | |
.PARAMETER All | |
Remove delegated permissions for all users. | |
Scopes parameter is required in this case as well. | |
.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. | |
#> | |
#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, Position = 0, ParameterSetName = 'ByUserIdWithGraphPowerShell')] | |
[Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByUserIdWithGraphExplorer')] | |
[Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByUserIdWithBoth')] | |
[array]$UserId, | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphPowerShell')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphExplorer')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithBoth')] | |
[switch]$All, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByUserIdWithGraphPowerShell')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByUserIdWithBoth')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphPowerShell')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithBoth')] | |
[switch]$GraphPowerShell, | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByUserIdWithGraphExplorer')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'ByUserIdWithBoth')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphExplorer')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithBoth')] | |
[switch]$GraphExplorer, | |
[Parameter(ParameterSetName = 'ByUserIdWithGraphPowerShell')] | |
[Parameter(ParameterSetName = 'ByUserIdWithGraphExplorer')] | |
[Parameter(ParameterSetName = 'ByUserIdWithBoth')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphPowerShell')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithGraphExplorer')] | |
[Parameter(Mandatory = $true, ParameterSetName = 'AllUsersWithBoth')] | |
[array]$Scopes = @() | |
) | |
#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 | |
} | |
$MgServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" -ErrorAction Stop | |
#endregion | |
#region Reset user consent for individual users | |
$UserId | ForEach-Object { | |
if ([string]::IsNullOrEmpty($_) -or [string]::IsNullOrWhiteSpace($_)) { return } | |
$User = Get-MgUser -UserId $_ -ErrorAction SilentlyContinue | |
if ($null -eq $User) { | |
Write-Host "User with ID '$_' not found." | |
return | |
} | |
Write-Host "`n$($User.UserPrincipalName)`n" -ForegroundColor Cyan | |
$( | |
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 principalId eq '$($User.Id)'" | |
if ($null -eq $Oauth2PermissionGrant -or $Oauth2PermissionGrant.Count -eq 0) { | |
Write-Host "$($User.UserPrincipalName) did not delegate any permissions to $($ServicePrincipal.DisplayName)." | |
return | |
} | |
$CurrentScopes = ($Oauth2PermissionGrant.Scope).Trim().Split(' ') | |
$NewScopes = @() | |
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 delegated permission '$_' for $($ServicePrincipal.DisplayName)", | |
"$($_) - Do you want to remove consent? Say no, if you want to keep the consent.", | |
"Confirm to remove consent for $_" | |
) | |
) { | |
Write-Host "(Remove) $($_)" -ForegroundColor Red | |
} | |
elseif ($WhatIfPreference) { | |
# Do nothing | |
} | |
else { | |
Write-Host "(Keep) $($_)" -ForegroundColor Green | |
$NewScopes += $_ | |
} | |
} | |
} | |
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 the delegated permissions for $($ServicePrincipal.DisplayName)?", | |
"Confirm to update the delegated permissions for $($ServicePrincipal.DisplayName)" | |
) | |
) { | |
Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId $Oauth2PermissionGrant.Id -Scope ($NewScopes -join ' ') | |
} | |
} | |
else { | |
if ( | |
$PSCmdlet.ShouldContinue( | |
"Do you want to remove all the delegated permissions for $($ServicePrincipal.DisplayName)?", | |
"Confirm to remove all delegated permissions for $($ServicePrincipal.DisplayName)" | |
) | |
) { | |
Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $Oauth2PermissionGrant.Id | |
} | |
} | |
} | |
} | |
} | |
#endregion | |
#region Reset user consent for all users | |
if ($All) { | |
$( | |
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))." | |
$Oauth2PermissionGrants = @( Get-MgOauth2PermissionGrant -Filter "resourceId eq '$($MgServicePrincipal.Id)' and clientId eq '$($ServicePrincipal.Id)' and ConsentType eq 'Principal'" ) | |
if ($null -eq $Oauth2PermissionGrants -or $Oauth2PermissionGrants.Count -eq 0) { | |
Write-Host "No user has delegated permissions to $($ServicePrincipal.DisplayName)" -ForegroundColor White | |
return | |
} | |
$Oauth2PermissionGrants | ForEach-Object { | |
$Oauth2PermissionGrant = $_ | |
$User = Get-MgUser -UserId $Oauth2PermissionGrant.PrincipalId -ErrorAction SilentlyContinue | |
if ($null -eq $User) { | |
Write-Host "User with ID '$UserId' not found." | |
return | |
} | |
Write-Host "`n$($User.UserPrincipalName)`n" -ForegroundColor Cyan | |
$CurrentScopes = ($Oauth2PermissionGrant.Scope).Trim().Split(' ') | |
$NewScopes = @() | |
if ($Scopes.Count -eq 0) { | |
Write-Error "The 'Scopes' parameter is required when using the 'All' parameter." | |
exit 1 | |
} | |
$NewScopes = $CurrentScopes | ForEach-Object { | |
if ($_ -in $Scopes -or 'all' -in $Scopes) { | |
Write-Host "(Remove) $_" -ForegroundColor Red | |
} | |
else { | |
Write-Host "(Keep) $_" -ForegroundColor Green | |
$_ | |
} | |
} | |
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.ShouldProcess( | |
"Update $($User.UserPrincipalName) delegated permissions for $($ServicePrincipal.DisplayName)", | |
"Do you want to update $($User.UserPrincipalName) delegated permissions for $($ServicePrincipal.DisplayName)?", | |
"Confirm to update $($User.UserPrincipalName) delegated permissions for $($ServicePrincipal.DisplayName)" | |
) | |
) { | |
Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId $Oauth2PermissionGrant.Id -Scope ($NewScopes -join ' ') | |
} | |
} | |
else { | |
if ( | |
$PSCmdlet.ShouldProcess( | |
"Remove all $($User.UserPrincipalName) delegated permissions for $($ServicePrincipal.DisplayName)", | |
"Do you want to remove all $($User.UserPrincipalName) delegated permissions for $($ServicePrincipal.DisplayName)?", | |
"Confirm to remove all $($User.UserPrincipalName) 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