Functions/Public/Remove-MsGServicePrincipalOwner.ps1

function Remove-MsGServicePrincipalOwner {
    <#
    .SYNOPSIS
    Removes a specified owner from a service principal in Entra ID.
    .DESCRIPTION
    This function removes a specified owner from a service principal in Entra ID.
    .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 UserPrincipalName
    The UserPrincipalName of the user to remove as an owner from the service principal.
    .PARAMETER DirectoryObjectId
    The Directory Object Id of the principal to remove as an owner from the service principal.
    .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-MsGServicePrincipalOwner -DisplayName TestApp-01 -UserPrincipalName benjamin.cohn@testdomain.com -Force

    Removes the user "benjamin.cohn@testdomain.com" as an owner 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 = $false, Position = 3)][string[]]$UserPrincipalName,
        [parameter(Mandatory = $false, Position = 4)][string[]]$DirectoryObjectId,
        [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 ServicePrincipal 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 Owners
        try {
            $servicePrincipalOwners = Get-MsGServicePrincipalOwner -ObjectId $servicePrincipal.id @irmParams
            $ownerIDsToRemove = $servicePrincipalOwners | Where-Object { $_.userPrincipalName -in $UserPrincipalName -or $_.id -in $DirectoryObjectId } | Select-Object -ExpandProperty id
        }
        catch {
            $errorMessage = Get-MsGErrorMessage $_
            $errorException = '{0} : Unable to validate service principal owners from Entra ID. Error: {1}' -f $MyInvocation.MyCommand.Name, $errorMessage
            throw $errorException
        }
        # end region

        # Remove Owners
        foreach ($ownerId in $ownerIDsToRemove) {
            if (($PSCmdlet.ShouldProcess($ownerId, 'Remove Service Principal Owner')) -and ($Force.IsPresent -or ($PSCmdlet.ShouldContinue("Remove '$ownerId' as an owner from '$($servicePrincipal.displayName)'.", 'Are you sure you want to proceed?')))) {
                $appOwnerEndpoint = 'servicePrincipals/{0}/owners/{1}/$ref' -f $servicePrincipal.id, $ownerId
                try {
                    Invoke-MsGRequest -Method Delete -Endpoint $appOwnerEndpoint -Body $body @irmParams > $null
                }
                catch {
                    $errorMessage = Get-MsGErrorMessage $_
                    $errorException = 'Unable to remove {0} as an owner from the service principal. Error: {1}' -f $ownerId, $errorMessage
                    Write-Error $errorException
                    continue
                }
            }
        }
        # end region
    }
}