Connect-MgGraph app-only authentication - Why getting access is denied errors ?

2022-11-21T18:27:13.303+00:00

Hi Gents,

I'm trying to use the Graph SDK to manage organization contacts for internal employees via a runbook.
I see from the MS documentation that app-only authentication is supported:
https://learn.microsoft.com/en-us/powershell/microsoftgraph/app-only?view=graph-powershell-1.0&tabs=powershell

Therefore, I’m connecting as follows:

$Connection = Get-AutomationConnection -Name “AzureRunAsConnectionGraph”  
Connect-MgGraph -ClientID $Connection.ApplicationId -TenantId $Connection.TenantId -CertificateThumbprint $Connection.CertificateThumbprint  
$context = Get-MgContext  
$context.Scopes  

Here is the output:

Welcome To Microsoft Graph!  
TeamMember.Read.All  
User.ReadWrite.All  
Group.Read.All  
Directory.Read.All  
GroupMember.Read.All  
Team.ReadBasic.All  
MailboxSettings.Read  
Contacts.ReadWrite  
Mail.Send  
MailboxSettings.ReadWrite  
Contacts.Read  

Despite using the app-only authentication and having "Contacts.ReadWrite" permission, I’m getting "access is denied" errors on all below commands:

  • Get-MgUserContactFolder $folderStaffDirectory = Get-MgUserContactFolder -UserId $Mbx.UserPrincipalName | Where-Object {$_.DisplayName -eq $folderName}
  • New-MgUserContactFolder New-MgUserContactFolderContact -UserId $Mbx.UserPrincipalName -BodyParameter $params -ContactFolderId $folderStaffDirectory.Id -DisplayName $Contact.DisplayName -CompanyName $Contact.CompanyName -Department $Contact.Department -JobTitle $Contact.JobTitle -PersonalNotes $OrgNotes -MobilePhone $Contact.Mobile
  • Get-MgUserContactFolderContact [array]$ContactsInMbx = Get-MgUserContactFolderContact -UserId $Mbx.UserPrincipalName -ContactFolderId $folderStaffDirectory.Id -All
  • New-MgUserContactFolderContact $folderStaffDirectory = New-MgUserContactFolder -UserId $Mbx.UserPrincipalName -BodyParameter $paramsFolder

Error example:

Get-MgUserContactFolder : Access is denied. Check credentials and try again. At line:91 char:2 + $folderStaffDirectory = Get-MgUserContactFolder -UserId $Mbx.User ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: ({ UserId = conf..., Property = }:<>f__AnonymousType317) [Get-MgUserContactFolder_List1], RestException1 + FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.GetMgUserContactFolder_List1

Is there anything wrong that I'm doing ?
Please feel free whether you need more details.

Thanks in advance.
Cheers,
Patrick

Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
12,767 questions
0 comments No comments
{count} votes

5 answers

Sort by: Most helpful
  1. Vicky Kumar (Mindtree Consulting PVT LTD) 1,161 Reputation points Microsoft Employee
    2022-11-22T04:52:50.997+00:00

    Have you imported module Microsoft.Graph.PersonalContacts before accessing contact folder , please see the doc for more details - https://learn.microsoft.com/en-us/graph/api/user-list-contactfolders?view=graph-rest-1.0&tabs=powershell

    Import-Module Microsoft.Graph.PersonalContacts

    Get-MgUserContactFolder -UserId $userId

    Hope this helps

    Thanks

    0 comments No comments

  2. 2022-11-22T08:15:06.8+00:00

    Hi @Vicky Kumar (Mindtree Consulting PVT LTD)

    I confirm that I'm importing this module.

    Here is the full script:

    # Import modules  
    Import-Module -Name MSAL.PS  
    Import-Module -Name ExchangeOnlineManagement  
    Import-Module -Name AzureADPreview  
    Import-Module -Name Microsoft.Graph.PersonalContacts  
    Import-Module -Name Microsoft.Graph.Authentication  
    Import-Module -Name Microsoft.Graph.Applications  
      
    # Variables  
    $subscriptionId = 'XXXXXXXXXXX'  
    $tenant = 'xxxxxx.onmicrosoft.com'  
    $serviceId = 'xxxxxxxxxxxxxxxxxxx'  
    $appSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'  
    $tenantId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'  
    $dlOrgContactId = 'xxxxxxxxxxxxxxxxxxxxxx'  
    $folderName = "Folder name"  
    $folderId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  
    $Now = Get-Date -format D  
    $OrgNotes = "Organization contact created " + $Now  
      
    # Azure Run as connection  
    $Connection = Get-AutomationConnection -Name "AzureRunAsConnectionGraph"  
      
    $Connection  
      
    # Oauth connection to EXO  
    try {   
    	Connect-ExchangeOnline -CertificateThumbprint $Connection.CertificateThumbprint -AppId $Connection.ApplicationId -Organization $tenant  
    	write-output -ForegroundColor Green "Connection Successfull to EXO"  
    }  
    catch {  
      	write-output -ForegroundColor Red "Connection Error to EXO"  
    }  
    # Oauth connection to AAD  
    try {   
    	Connect-AzureAD -TenantId $Connection.TenantId -CertificateThumbprint $Connection.CertificateThumbprint -ApplicationId $Connection.ApplicationId  
    	write-output -ForegroundColor Green "Connection Successfull to AAD"  
    }  
    catch {  
      	write-output -ForegroundColor Red "Connection Error to AAD"  
    }  
      
    # Find the set of organization contacts from DL All MMV Contact  
    [array]$OrgContacts = [array]$OrgContacts = Get-AzureADGroupMember -ObjectId $dlOrgContactId -All:$true  
    If (!($OrgContacts)) {Write-Host "No organization contacts found - exiting" ; break }  
    write-output ("Found {0} organization contacts - continuing..." -f $OrgContacts.count)  
      
    # Look for target mailboxes. In this example, we get the mailboxed created in the last month  
    $LastMonth = (Get-Date).AddDays(-60)  
    [array]$Mailboxes = Get-ExoMailbox -Filter "WhenMailboxCreated -gt '$LastMonth'" -RecipientTypeDetails UserMailbox | Select ExternalDirectoryObjectId, DisplayName, UserPrincipalName  
    If (!($Mailboxes)) { write-output "No mailboxes found to process - exiting" ; break }  
    write-output ("Found {0} mailboxes to process - continuing..." -f $Mailboxes.count)  
      
    # Connect to the Graph SDK endpoint using the automation account  
    $RequiredScopes = @("Contacts.ReadWrite")  
    Connect-MgGraph -ClientID $Connection.ApplicationId -TenantId $Connection.TenantId -CertificateThumbprint $Connection.CertificateThumbprint  
    $context = Get-MgContext  
    $context.Scopes  
      
    ForEach ($Mbx in $Mailboxes) {  
    	# Populate email addresses for existing contacts  
    	$folderStaffDirectory = Get-MgUserContactFolder -UserId $Mbx.UserPrincipalName -Debug | Where-Object {$_.DisplayName -eq $folderName}  
      
    	# Create contacts folder if does not exists  
    	if(!($folderStaffDirectory))  
    	{  
    		$paramsFolder = @{  
    			ParentFolderId = $folderId  
    			DisplayName = $folderName  
    		}  
    		# Create folder  
    		$folderStaffDirectory = New-MgUserContactFolder -UserId $Mbx.UserPrincipalName -BodyParameter $paramsFolder -Debug  
    	}  
    	# Get contacts from folder  
    	[array]$ContactsInMbx = Get-MgUserContactFolderContact -UserId $Mbx.UserPrincipalName -ContactFolderId $folderStaffDirectory.Id -All -Debug  
      
    	If ($ContactsInMbx.Count -gt 0) { $ContactsInMbx = $ContactsInMbx | Select emailaddresses }  
    	$CheckTable = [System.Collections.Generic.List[Object]]::new()  
    	ForEach ($C in $ContactsInMbx) { $CheckTable.Add($C[0].EmailAddresses[0].Address.toString()) }  
    	write-output "Processing mailbox" $Mbx.DisplayName  
      
    	ForEach ($Contact in $OrgContacts) {  
      
    		write-output "Processing contact" $Contact.DisplayName  
    		# Check if the contact is already there. If not, we go ahead and add the contact  
    		If ($Email -in $CheckTable) {  
    			write-output ("Contact record for {0} is already present in the mailbox" -f $Contact.Maill) }  
    		Else {  
    			Write-Host "Proceeding $($Contact.Mail)..."  
    			# Build the contact object  
    			$params = @{  
    				GivenName = "$($Contact.GivenName)"  
    				Surname = "$($Contact.Surname)"  
    				EmailAddresses = @(  
    					@{  
    						Address = "$($Contact.Mail)"  
    						Name = "$($Contact.DisplayName)"  
    					}  
    				)  
    				BusinessPhones = @(  
    					"$($Contact.TelephoneNumber)"  
    				)  
    			}  
    			# And add the new contact  
    			Try {  
    				New-MgUserContactFolderContact -UserId $Mbx.UserPrincipalName -BodyParameter $params -ContactFolderId $folderStaffDirectory.Id -Debug -DisplayName $Contact.DisplayName -CompanyName $Contact.CompanyName -Department $Contact.Department -JobTitle $Contact.JobTitle -PersonalNotes $OrgNotes -MobilePhone $Contact.Mobile  
    			}  
    			catch {  
    				write-output "Error creating contact $($Contact.Mail) for $($Mbx.UserPrincipalName) $($_.Exception.Message)"  
    			}  
    		} #End Else  
     	} #End ForEach OrgContacts  
    } #End ForEach Mailboxes  
    

    Feel free to let me know what I'm doing wrong.

    Thanks.

    Cheers,
    Patrick


  3. 2022-12-12T21:35:26.387+00:00

    269831-22-full.txtHi @Vicky Kumar (Mindtree Consulting PVT LTD)

    Sorry for the delay in replying, here is what I'm getting with -verbose

    Is this helping ?

    Get-MgUserContactFolder : Access to OData is disabled.
    At line:3 char:1

    • $folderStaffDirectory = Get-MgUserContactFolder -UserId $Mbx.UserPrin ...
    • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    • CategoryInfo : InvalidOperation: ({ UserId = godo..., Property = }:<>f__AnonymousType317) [Get-MgUser ContactFolder_List1], RestException1
    • FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.GetMgUserContactFolder_List1
      VERBOSE: Performing the operation "New-MgUserContactFolder_Create1" on target "Call remote 'UsersCreateContactFolders1'
      operation".
      New-MgUserContactFolder : Access to OData is disabled.
      At line:13 char:1
    • $folderStaffDirectory = New-MgUserContactFolder -UserId $Mbx.UserPrin ...
    • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    • CategoryInfo : InvalidOperation: ({ UserId = godo...ContactFolder }:<>f__AnonymousType442) [New-MgUser ContactFolder_Create1], RestException1
    • FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.NewMgUserContactFolder_Create1
      Get-MgUserContactFolderContact : Cannot bind argument to parameter 'ContactFolderId' because it is an empty string.
      At line:16 char:104
    • ... x.UserPrincipalName -ContactFolderId $folderStaffDirectory.Id -All -v ...
    • ~~~~~~~~~~~~~~~~~~~~~~~~
    • CategoryInfo : InvalidData: (:) [Get-MgUserContactFolderContact], ParameterBindingValidationException
    • FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Get-MgUserContactFolderContact

    When I check granted scopes, here is the output:

    $context = Get-MgContext  
    $context.Scopes  
    

    TeamMember.Read.All
    User.ReadWrite.All
    Group.Read.All
    Directory.Read.All
    GroupMember.Read.All
    Team.ReadBasic.All
    MailboxSettings.Read
    Contacts.ReadWrite
    Mail.Send
    MailboxSettings.ReadWrite
    Contacts.Read

    If I'm not wrong, it should be enough to manage contacts and contact folder.

    In case, I also attached a fiddler trace.

    Thanks in advance.

    Best,
    Patrick

    0 comments No comments

  4. 2023-01-09T08:46:09.697+00:00

    Hi @Vicky Kumar (Mindtree Consulting PVT LTD)

    First of all, all my best wishes for 2023 !

    Are the details that I provided in my last comment enough ?
    Are you still able help me ?

    Thanks in advance for your help.

    Best regards

    0 comments No comments

  5. 2023-01-23T15:34:46.26+00:00

    Hi everyone,

    Anyone that could help ?

    Thanks in advance.

    Best regards.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.