Functions/Public/Remove-MsGServicePrincipalAPIPermission.ps1

function Remove-MsGServicePrincipalAPIPermission {
    <#
    .SYNOPSIS
    Removes specified API permission from service principal.
    .DESCRIPTION
    This function removes specified API permissions from a service principal. Both application and delegated permissions can be removed for any resource application.
    .PARAMETER DisplayName
    Display Name of the service principal.
    .PARAMETER AppId
    App/Client Id of the service principal.
    .PARAMETER ObjectId
    Object Id of the service principal.
    .PARAMETER Permission
    List of permissions to remove from the service principal.
    .PARAMETER Type
    Type of permissions to remove from the service principal.
    .PARAMETER ResourceAppId
    App/Client Id of the resource application that the permissions are assigned from.
    .PARAMETER Force
    Bypasses the confirmation check.
    .PARAMETER Headers
    Authentication Headers to connect to Microsoft Graph.
    .PARAMETER Jwt
    Jwt to connect to Microsoft Graph.
    .EXAMPLE
    PS> Remove-MsGServicePrincipalAPIPermission -DisplayName TestApp-01 -Permission User.Read.All -Type Application -Force

    This removes the User.Read.All Application API Permission for Microsoft Graph from the TestApp-01 service principal.
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Name')]
    param(
        [parameter(Mandatory = $true, ParameterSetName = 'Name', Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][string]$DisplayName,
        [parameter(Mandatory = $true, ParameterSetName = 'AppId', Position = 1)][string]$AppId,
        [parameter(Mandatory = $true, ParameterSetName = 'ObjId', Position = 2)][string]$ObjectId,
        [parameter(Mandatory = $true, Position = 3)][string[]]$Permission,
        [parameter(Mandatory = $false, Position = 4)][ValidateSet('Application', 'Delegated')][string]$Type = 'Application',
        [parameter(Mandatory = $false)][string]$ResourceAppId = '00000003-0000-0000-c000-000000000000',
        [parameter(Mandatory = $false)][switch]$Force,
        [parameter(Mandatory = $false)][hashtable]$Headers,
        [parameter(Mandatory = $false)][string]$Jwt
    )
    BEGIN {
        # Session Checks
        if ([string]::IsNullOrEmpty($Headers) -and [string]::IsNullOrEmpty($Jwt)) {
            if ($null -ne $myGraphToken) {
                Write-Verbose "Setting Jwt to Global: $myGraphToken"
                $Jwt = $myGraphToken
            }
        }
        if ([string]::IsNullOrEmpty($Headers) -and -not([string]::IsNullOrEmpty($Jwt))) {
            $Headers = @{
                'Authorization' = 'Bearer {0}' -f $Jwt
                'Content-Type'  = 'application/json'
            }
        }
        if ([string]::IsNullOrEmpty($Headers)) {
            $errorMessage = '{0} : Unable to run command. Headers are empty.' -f $MyInvocation.MyCommand.Name
            throw $errorMessage
        }

        $irmParams = @{
            'Headers'     = $Headers
            'ErrorAction' = 'Stop'
        }
        # end Session Checks
    }
    PROCESS {
        # Retrieve Service Principal from Entra ID
        try {
            switch ($PSCmdlet.ParameterSetName) {
                "Name" {
                    $appEndpoint = "servicePrincipals?`$filter=displayName eq '{0}'" -f $DisplayName
                }
                "AppId" {
                    $appEndpoint = "servicePrincipals?`$filter=appId eq '{0}'" -f $AppId
                }
                "ObjId" {
                    $appEndpoint = "servicePrincipals/{0}" -f $ObjectId
                }
            }
            $servicePrincipal = Invoke-MsGRequest -Method Get -Endpoint $appEndpoint @irmParams
            if ([string]::IsNullOrEmpty($servicePrincipal)) {
                $errorMessage = 'Service Principal not found.'
                throw $errorMessage
            }
        }
        catch {
            $errorMessage = Get-MsGErrorMessage $_
            $errorException = '{0} : Unable to retrieve service principal from Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage
            throw $errorException
        }
        # end region

        # Retrieve existing permissions.
        try {
            $servicePrincipalPermissions = Get-MsGServicePrincipalAPIPermission -ObjectId $servicePrincipal.id @irmParams
        }
        catch {
            $errorMessage = Get-MsGErrorMessage $_
            $errorException = '{0} : Unable to retrieve service principal permissions from Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage
            throw $errorException
        }
        # end region

        # Remove Permission if it exists
        switch ($Type) {
            "Application" {
                foreach ($SpecifiedPermission in $Permission) {
                    $permissionInfo = $null
                    $permissionEndpoint = $null

                    $permissionInfo = $servicePrincipalPermissions | Where-Object { $_.Name -eq $SpecifiedPermission -and $_.ResourceAppId -eq $ResourceAppId -and $_.Type -eq $Type }
                    if ([string]::IsNullOrEmpty($permissionInfo)) {
                        $warningMessage = "{0} permission '{1}' of the '{2}' resource app is not present on this service principal. Nothing to remove." -f $Type, $SpecifiedPermission, $ResourceAppId
                        Write-Warning $warningMessage
                        continue
                    }

                    if (($PSCmdlet.ShouldProcess($SpecifiedPermission, 'Remove Service Principal API Permission')) -and ($Force.IsPresent -or ($PSCmdlet.ShouldContinue("Remove [$ResourceAppId] '$SpecifiedPermission' $Type-Level permission from '$($servicePrincipal.displayName)'.", 'Are you sure you want to proceed?')))) {
                        try {
                            $permissionEndpoint = 'servicePrincipals/{0}/appRoleAssignments/{1}' -f $servicePrincipal.id, $permissionInfo.id
                            Invoke-MsGRequest -Method Delete -Endpoint $permissionEndpoint @irmParams
                        }
                        catch {
                            $errorMessage = Get-MsGErrorMessage $_
                            $errorException = 'Unable to remove {0} permission from the service principal. Error: {1}' -f $SpecifiedPermission, $errorMessage
                            Write-Error $errorException
                            continue
                        }
                    }
                }
            }
            "Delegated" {
                # Retrieve permission id.
                $oauth2PermissionId = $servicePrincipalPermissions | Where-Object { $_.Type -eq 'Delegated' -and $_.ResourceAppId -eq $ResourceAppId } | Select-Object -ExpandProperty Id -Unique
                if (-not([string]::IsNullOrEmpty($oauth2PermissionId))) {
                    try {
                        # Retrieve delegated permission grants.
                        $oauth2PermissionEndpoint = 'oauth2PermissionGrants/{0}' -f $oauth2PermissionId
                        $oauth2PermissionGrants = Invoke-MsGRequest -Method Get -Endpoint $oauth2PermissionEndpoint @irmParams
                        $existingScopeList = $oauth2PermissionGrants.scope.Trim().Split(' ')

                        # Retrieve updated scope list.
                        $permissionScopeList = New-Object -TypeName System.Collections.Generic.List[string]
                        foreach ($existingPermission in $existingScopeList) {
                            if ($existingPermission -notin $Permission) {
                                $permissionScopeList.Add($existingPermission)
                            }
                        }

                        # Update permission list if permissions still remain. Otherwise delete the delegation grants.
                        if (($PSCmdlet.ShouldProcess(($Permission -join ' '), 'Remove Service Principal API Permission')) -and ($Force.IsPresent -or ($PSCmdlet.ShouldContinue("Remove [$ResourceAppId] '$($Permission -join ' ')' $Type-Level permission from '$($servicePrincipal.displayName)'.", 'Are you sure you want to proceed?')))) {
                            if ($permissionScopeList.Count -gt 0) {
                                $scopeString = $permissionScopeList -join ' '
                                $permissionUpdateBody = @{'scope' = $scopeString }
                                Invoke-MsGRequest -Method Patch -Endpoint $oauth2PermissionEndpoint -Body $permissionUpdateBody @irmParams
                            }
                            else {
                                Invoke-MsGRequest -Method Delete -Endpoint $oauth2PermissionEndpoint @irmParams

                            }
                        }
                    }
                    catch {
                        $errorMessage = Get-MsGErrorMessage $_
                        $warningMessage = 'Failed to update delegated permissions. Error: {0}' -f $errorMessage
                        Write-Warning $warningMessage
                    }
                }
            }
        }
        # end region
    }
}