azureazure-functionsmicrosoft-graph-apiexchange-online

"Get-MgUser" raised "Insufficient privileges to complete the operation" & "Connect-ExchangeOnline -ManagedIdentity -Organization" raised "UnAuthorize"


I have an azure function running powershell, and here is the run.sp1, which mainly remove a user from all the groups; office 365 groups, distribution lists & security group (mail enabled and none-mail enabled):-

# Input bindings are passed in via param block.
param($Timer)
# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
    Write-Host "PowerShell timer is running late!"
}
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
# Connect to Microsoft Graph with required delegated scopes
 Write-Host "Start to connect to graph"
Connect-MgGraph -Identity
 Write-Host "Connected to Graph"
# Target user email
$userEmail = "test@****.onmicrosoft.com" 
 Write-Host "Start to Get-MgUser"
# --- PART 1: Azure AD + Office 365 Groups ---
$userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipalName eq '$userEmail'"  -ConsistencyLevel eventual -All -Property Id, DisplayName, UserPrincipalName, Mail
 Write-Host "Get-MgUser completed"
$userObj = $userList | Select-Object -First 1
if (-not $userObj) {
    Write-Host "User $userEmail not found in Microsoft Graph. Skipping AAD removal."
} elseif (-not $userObj.Id) {
    Write-Host "User objectId missing even after lookup. Skipping AAD removal."
} else {
    Write-Host "Found user: $($userObj.DisplayName) ($($userObj.UserPrincipalName))"
    $groups = Get-MgUserMemberOf -UserId $userObj.Id -All
    $groupsAsOwner = Get-MgUserOwnedObject -UserId $userObj.Id -All
    foreach ($group in $groups) {
        if ($group.AdditionalProperties.'@odata.type' -ne '#microsoft.graph.group') {
            continue
        }
        $groupId = $group.Id
        $groupName = $group.AdditionalProperties.DisplayName
        if (-not $groupId) {
            Write-Host "Skipping group with empty ID"
            continue
        }
        $fullGroup = Get-MgGroup -GroupId $groupId
        if ($fullGroup.GroupTypes -contains "DynamicMembership") {
            Write-Host "Skipping dynamic group: ${groupName}"
            continue
        }
        # Remove as MEMBER
        try {
            Remove-MgGroupMemberByRef -GroupId $groupId -DirectoryObjectId $userObj.Id -ErrorAction Stop
            Write-Host "Removed as MEMBER from ${groupName}"
        }
        catch {
            Write-Host "Failed MEMBER removal from ${groupName}: $($_.Exception.Message)"
        }}
foreach ($group2 in $groupsAsOwner) {
$groupId2 = $group2.Id
$groupName2 = $group2.AdditionalProperties.DisplayName
        # Remove as OWNER
        try {
            Remove-MgGroupOwnerByRef -GroupId $groupId2 -DirectoryObjectId $userObj.Id -ErrorAction Stop
            Write-Host "Removed as OWNER from ${groupName2}"
        }
        catch {
            Write-Host "Failed OWNER removal from ${groupName2}: $($_.Exception.Message)"
        }
    }
    }
# --- PART 2: Exchange Distribution Groups + Mail-enabled Security Groups ---
# Connect to Exchange Online
 Write-Host "Start to Connect to exchange"
Connect-ExchangeOnline -ManagedIdentity -Organization ****.onmicrosoft.com
 Write-Host "Connected to exchange"
Write-Host "Start Get-DistributionGroup"
$dlGroups = Get-DistributionGroup -ResultSize Unlimited
Write-Host "completed Get-DistributionGroup"
foreach ($dl in $dlGroups) {
    try {
        # Remove as MEMBER
        $members = Get-DistributionGroupMember -Identity $dl.Identity -ResultSize Unlimited
        $memberMatch = $members | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }
        if ($memberMatch) {
            Remove-DistributionGroupMember -Identity $dl.Identity -Member $userEmail -BypassSecurityGroupManagerCheck -Confirm:$false
            Write-Host "Removed as MEMBER from Exchange group: $($dl.DisplayName)"
        }
        # Remove as OWNER
        # $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy
        $owners = (Get-DistributionGroup -Identity $dl.Identity).ManagedBy | Foreach-Object { Get-Mailbox $_ }
        $ownerMatch = $owners | Where-Object { $_.PrimarySmtpAddress -ieq $userEmail }
        if ($ownerMatch) {
            Set-DistributionGroup -Identity $dl.Identity -ManagedBy @{ Remove = "$userEmail" }
            Write-Host "Removed as OWNER from Exchange group: $($dl.DisplayName)"
        }
    }
    catch {
        Write-Host "Failed Exchange removal for group: $($dl.DisplayName) - $($_.Exception.Message)"
    }
}
Write-Host "Cleanup script completed."

now inside the azure function which run each 5 minutes, i am getting this error:-

2025-05-20T19:05:01Z [Error] ERROR: [Authorization_RequestDenied] : Insufficient privileges to complete the operation.

on

userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipalName eq '$userEmail'"  -ConsistencyLevel eventual -All -Property Id, DisplayName, UserPrincipalName, Mail

and i am getting this error:-

UnAuthorized

on

Connect-ExchangeOnline -ManagedIdentity -Organization ***.onmicrosoft.com

now i have enabled managed identity for the azure function + run this command to grant the managed identity the need permissions:-

$tenantID = '0***e'

$managedIdentityObjectId = '7**b'

$msGraphApiResourceId = '00000003-0000-0000-c000-000000000000'

Connect-MgGraph -TenantId $tenantID -Scopes 'Application.Read.All','AppRoleAssignment.ReadWrite.All','Directory.Read.All'

# Find role ID for Group.ReadWrite.All

$msGraphSp = Get-MgServicePrincipal -Filter "appId eq '$msGraphApiResourceId'"

$role = $msGraphSp.AppRoles | Where-Object { $_.Value -eq 'Group.ReadWrite.All' }

# Assign the role

New-MgServicePrincipalAppRoleAssignment 

-ServicePrincipalId $managedIdentityObjectId 

-PrincipalId $managedIdentityObjectId 

-ResourceId
-AppRoleId $role.Id

any advice?

thanks

EDIT

ok based on the comment, I removed the old permission, and run this new script:-

$tenantId = '<your-tenant-id>'
$managedIdentityObjectId = '<objectId-of-managed-identity>'
$graphAppId = '00000003-0000-0000-c000-000000000000'  # Microsoft Graph

# Connect with high privilege (e.g., Global Admin or Privileged Role Admin)
Connect-MgGraph -TenantId $tenantId -Scopes 'AppRoleAssignment.ReadWrite.All', 'Directory.Read.All'

# Get Microsoft Graph service principal
$graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"

# Define the required app roles
$requiredRoles = @('User.Read.All', 'Group.ReadWrite.All')

foreach ($roleName in $requiredRoles) {
    $role = $graphSp.AppRoles | Where-Object {
        $_.Value -eq $roleName -and $_.AllowedMemberTypes -contains 'Application'
    }

    if ($role) {
        Write-Host "Assigning $roleName..."
        New-MgServicePrincipalAppRoleAssignment `
            -ServicePrincipalId $managedIdentityObjectId `
            -PrincipalId $managedIdentityObjectId `
            -ResourceId $graphSp.Id `
            -AppRoleId $role.Id
    } else {
        Write-Warning "Role $roleName not found in Graph AppRoles"
    }
}

But on this command:-

$userList = Get-MgUser -Filter "mail eq '$userEmail' or userPrincipal 

i got this error:-

ERROR: [Authorization_RequestDenied] : Insufficient privileges to complete the operation.

Also on this command :-

Connect-ExchangeOnline -ManagedIdentity -Organization ***i.onmicrosoft.com 

I got :-

 EXCEPTION: UnAuthorized

Solution

  • The Insufficient privileges error is caused by missing required permissions for the managed identity and the Unauthorized error indicates that the Exchange.ManageAsApp application permission has not been assigned to the managed identity.

    The Exchange Administrator role is necessary for a managed identity to connect using Connect-ExchangeOnline

    Initially I got the same error when I run the function with the same permissions that you added. To fix the issue refer below steps

    $tenantID = 'xxxxxxxxxxxxxxxxxxxxxx'
    
    $managedIdentityObjectId = 'c9651dacxxxxxxxxxxxxxxxxxxx'
    
    $msGraphAppId = '00000003-0000-0000-c000-000000000000'
    
    Connect-MgGraph -TenantId $tenantId -Scopes 'Application.Read.All','AppRoleAssignment.ReadWrite.All','Directory.Read.All'
    
    
    # Get Microsoft Graph service principal
    $msGraphSp = Get-MgServicePrincipal -Filter "appId eq '$msGraphAppId'"
    
    # Define required roles
    $requiredRoles = @('User.Read.All', 'Group.ReadWrite.All', 'Directory.Read.All')
    
    foreach ($roleValue in $requiredRoles) {
        $role = $msGraphSp.AppRoles | Where-Object { $_.Value -eq $roleValue -and $_.AllowedMemberTypes -contains "Application" }
        $role
        if ($role) {
            # Assign role to Managed Identity
            New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityObjectId `
                -PrincipalId $managedIdentityObjectId `
                -ResourceId $msGraphSp.Id `
                -AppRoleId $role.Id
            Write-Host "Assigned $($roleValue)"
        } else {
            Write-Host "Role $roleValue not found"
        }
    }
    
    
    # Find the Exchange Online Service Principal
    $exoSp = Get-MgServicePrincipal -All | Where-Object {$_.DisplayName -eq "Office 365 Exchange Online"}
    
    # Find the Exchange.ManageAsApp Permission ID
    $role = $exoSp.AppRoles | Where-Object { $_.Value -eq "Exchange.ManageAsApp" -and $_.AllowedMemberTypes -contains "Application" }
    
    # Assign the Permission
    New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityObjectId -PrincipalId $managedIdentityObjectId -ResourceId $exoSp.Id -AppRoleId $role.Id
    
    Write-Host "Assigned $($role.value)"
    
    

    enter image description here

    enter image description here