Use Graph API to get contacts from Outlook.com - powershell

I am trying to get a list of contacts via Graph API. In the portal.azure.com I went to App registrations and did a new registrations. I created secrets and added permission (picture below)
I am connecting to Graph API with this code
$Body = #{
'tenant' = $TenantId
'client_id' = $ClientId
'scope' = 'https://graph.microsoft.com/.default'
'client_secret' = $ClientSecret
'grant_type' = 'client_credentials'
}
$Params = #{
'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $Body
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod #Params
$Headers = #{
'Authorization' = "Bearer $($AuthResponse.access_token)"
}
$Result = Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users' -Headers $Headers
However I get the error message
*Invoke-RestMethod : {
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"date": "2020-09-04T17:54:13",
"request-id": "2113f712-f022-4ebc-8263-d26c469840d0"
}
}
}
At line:31 char:11
$Result = Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/us ...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand*
I assume when I get the user ID then I should be able to call https://graph.microsoft.com/v1.0/users/ID/contacts API and I should be able to create/delete contacts. What am I missing or how can I achieve it please?

Edit: I don't believe my answer is accurate as the "Me" endpoint won't work with the client_credential flow.
Original answer --
The Users endpoint is to query all users in your organization, which is different from contacts.
To get your outlook.com contacts, you need to have the following delegated permission in your application.
You will need to grant your application one of the following permissions
permissions (delegated)
OrgContact.Read.All
Directory.Read.All
Directory.ReadWrite.All
Directory.AccessAsUser.All
The endpoint you will be using is : https://graph.microsoft.com/v1.0/me/contacts
You can experiment the different endpoints with Microsoft Graph explorer. The website will also inform you of the required permissions for each calls (through the Modify permissions tab) and give you insights on all aspects of the call itself.

Related

Microsoft Graph "Access is denied. Check credentials and try again" in PowerShell

I am unable to list events in a calendar via MS Graph API: calls result in "Access is denied". Switching to raw HTTP results in error 403 as well. I am able to create an event via a POST to /events endpoint, though.
This is the code I am executing:
$tenant = '<SNIP>'
$client_id = '<SNIP>'
$client_secret = '<SNIP>'
$scope = [System.Web.HttpUtility]::UrlEncode('https://graph.microsoft.com/.default')
$url = "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token"
$Body = "client_id=$client_id&scope=$scope&client_secret=$client_secret&grant_type=client_credentials"
$response = Invoke-RestMethod $url -Method Post -Body $Body -Headers #{'Content-Type'='application/x-www-form-urlencoded'}
$token = $response.access_token
Connect-MgGraph -AccessToken $token
Get-MgUserEvent -UserId '17160c5f-dd86-46cc-92b8-54d6e94861e6'
Output:
.\Calendar.ps1
Welcome To Microsoft Graph!
Get-MgUserEvent : Access is denied. Check credentials and try again.
In C:\Users\SYSTOLA-rk\Calendar.ps1:15 Zeichen:1
+ Get-MgUserEvent -UserId '17160c5f-dd86-46cc-92b8-54d6e94861e6'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({ UserId = 1716..., Property = }:<>f__AnonymousType39`7) [Get-MgUserEvent_List1], RestException`1
+ FullyQualifiedErrorId : ErrorAccessDenied,Microsoft.Graph.PowerShell.Cmdlets.GetMgUserEvent_List1
Here are the permissions in Azure configured for the app (I lack reputation for posting images): App Permissions

Unauthorized (401) listing synchronization jobs for service principal in MS Graph API

I am trying to use MS Graph API to configure Azure AD Connect Cloud Sync from these instructions but I am having trouble calling this endpoint in Powershell using client credentials:
https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID}/synchronization/jobs
I can successfully call this using the Graph Explorer, but no luck using Application permission and authentication with a client secret in Powershell. I get 401 Unauthorized error. I can call other endpoints like:
https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID} # no /synchronization/jobs at the end
The application has the API permissions: Directory.ReadWrite.All and Application.ReadWrite.OwnedBy (Application) The permissions has been granted by the admin:
Below is the detail of the code I use to authenticate:
$Body = #{
'tenant' = $TenantId
'client_id' = $ClientId
'scope' = 'https://graph.microsoft.com/.default'
'client_secret' = $ClientSecret
'grant_type' = 'client_credentials'
}
$Params = #{
'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $Body
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod #Params
And this is how I call the endpoint:
$Headers = #{
'Authorization' = "Bearer $($AuthResponse.access_token)"
}
$Params = #{
Uri = "https://graph.microsoft.com/beta/servicePrincipals/{SERVICE_PRINCIPAL_ID}/synchronization/jobs"
Method = 'Get'
ContentType = 'application/json'
Headers = $Headers
}
$res = Invoke-RestMethod #Params
And the error:
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized
If I use the token from the Graph Explorer it works...
My token from Powershell decoded contains this "roles" section but no "scp" like in the Graph Explorer token:
"roles": [
"Application.ReadWrite.OwnedBy",
"Directory.ReadWrite.All"
],
Full token obfuscated:
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/{TENANT_ID}/",
"iat": 1629836586,
"nbf": 1629836586,
"exp": 1629840486,
"aio": "{AIO}",
"app_displayname": "AppForAdConnect2",
"appid": "{APPID}",
"appidacr": "1",
"idp": "https://sts.windows.net/{TENANT_ID}/",
"idtyp": "app",
"oid": "{OID}",
"rh": "{RH}",
"roles": [
"Application.ReadWrite.OwnedBy",
"Directory.ReadWrite.All"
],
"sub": "{SUB}",
"tenant_region_scope": "NA",
"tid": "{TENANT_ID}",
"uti": "{UTI}",
"ver": "1.0",
"wids": [
"{WID}"
],
"xms_tcdt": 1584535155
}
Thank you for your help!
I tried with same permissions that you have provided for your Service Principal with the below script and it successfully gave me the output.
$TenantName = "<your azure AD tenant primary domain here (ex-abc.onmicrosoft.com)>"
$clientID = "Application (client) ID of the registered App here"
$clientSecret = "client secret for the app"
$Scope = "https://graph.microsoft.com/.default"
$Body = #{
Grant_Type = "client_credentials"
Scope = $Scope
client_Id = $clientID
Client_Secret = $clientSecret
}
$authUri = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"
$TokenResponse = Invoke-RestMethod -Uri $authUri -Method POST -Body $Body
$spobject = "ServicePrincipalObjectID that was returned after you did post operation in the document"
$Headers = #{
"Authorization" = "Bearer $($TokenResponse.access_token)"
"Content-type" = "application/json"
}
$apiUri = "https://graph.microsoft.com/beta/servicePrincipals/$spobject/synchronization/jobs"
$response = Invoke-RestMethod -Headers $Headers -Uri $apiUri -Method GET
$response
Which is same as the output I get from Graph Explorer.
Note: If you are using the highlighted object ID (Image 1 below) in serviceprincipalobjectID it will throw 401 error instead of that you have to go Enterprise Application and use the objectID (image 2 below) shown there .
Update:
As Discussed we are not able to do the above operation using client credentials for the AD2AADSync Service Principal but we can use another way to do those operations.
We can use Microsoft Graph Powershell SDK :
Step-1 : Install the Module in powershell using below command
Install-Module Microsoft.Graph
Step-2 : Set the profile for the above module to beta as we will be using it to get the synchronization jobs.
Select-MgProfile -Name "beta"
Step-3 : Use the below script to get the values of the synschronization job.
Connect-MgGraph -Scopes "Application.ReadWrite.All","Directory.ReadWrite.All"
$value = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId "AD2AADSync_service_principal_objectId"
$value
$value.Status
Output:
Reference:
Microsoft.Graph.Applications Module | Microsoft Docs
Ok so the solution was to add the role "Hybrid Identity Administrator" to the Service Principal or the User used to call the endpoint "synchronization/jobs". For some reason this application template needed this additional role and the API Permission enough is not enough.

Sharepoint Online list items query returns empty array with Microsoft Graph API

TLDR: I am trying to get the list items of a Sharepoint Online list with the Microsoft Graph API in Powershell. But all I get back is an empty array.
I have created an application in Azure and given it these permissions:
I know it's way too much permissions for what I need, but for the sake of troubleshooting and making sure it's not a permission issue, I added them all.
This is the code I use to get the Graph API token:
$TenantId = 'my-tenant-id'
$ClientId = 'my-client-id'
$ClientSecret = 'my-client-secret'
$Body = #{
'tenant' = $TenantId
'client_id' = $ClientId
'scope' = 'https://graph.microsoft.com/.default'
'client_secret' = $ClientSecret
'grant_type' = 'client_credentials'
}
$Params = #{
'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $Body
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod #Params
$Headers = #{
'Authorization' = "Bearer $($AuthResponse.access_token)"
}
When I run the following command, just to get the list info, it works.
$SiteID = "my-site-id"
$ListID = "my-list-id"
$url = "https://graph.microsoft.com/v1.0/sites/$($SiteID)/lists/$($ListID)"
$Result = Invoke-RestMethod -Uri $url -Headers $Headers -Method Get -ContentType "application/json"
$Result | fl
This gives me the following result back:
#odata.context : https://graph.microsoft.com/v1.0/$metadata#sites('abc.sharepoint.com%2Csite-id')/lists/$entity
#odata.etag : "list-id,1139"
createdDateTime : 2020-09-22T08:18:49Z
description :
eTag : "list-id,1139"
id : list-id
lastModifiedDateTime : 2020-10-21T14:53:18Z
name : list-name
webUrl : https://abc.sharepoint.com/sites/my-site/Lists/my-list
displayName : my-list
createdBy : #{user=}
lastModifiedBy : #{user=}
parentReference : #{siteId=abc.sharepoint.com,list-id
cca2b90}
list : #{contentTypesEnabled=True; hidden=False; template=genericList}
So I know my authentication with the Graph API works.
But then when I want to get the actual list items, I get an empty array back.
Command:
$SiteID = "my-site-id"
$ListID = "my-list-id"
$url = "https://graph.microsoft.com/v1.0/sites/$($SiteID)/lists/$($ListID)/items?expand=fields"
$Result = Invoke-RestMethod -Uri $url -Headers $Headers -Method Get -ContentType "application/json"
$Result | fl
Result:
#odata.context : https://graph.microsoft.com/v1.0/$metadata#sites('abc.sharepoint.com%2Csite-id')/lists('list-id')/items
value : {}
I have no idea what the issue is. Similar posts on here made me think it's a permission problem. But I've thrown every permission in the book at it and it's still not returning the list items.
Help would be greatly appreciated.
Since you are using Microsoft Graph API, you need to select the microsoft graph api permissions in the Azure AD portal but not under the sharepoint. The permissions under the sharepoint are related to sharepoint API.

How to correctly access v2.0 of REST API in PowerShell for mailbox management

We have a PowerShell script which checks a service mailbox and updates our wallboards with information, then moves the relevant emails to the deleted folder. This was set up under API v1.0 which is now deprecated and we're needing to get it changed for version 2.0.
Unfortunately, this has led to a lot of confusion and a lot of scratched heads which isn't too fun. This has occurred mainly around getting an oAuth2 token then feeding it into the request for the mailbox.
So far, we've managed to get the Azure AD app registered and it's providing an authentication token, but that's coming back as unauthorised from Microsoft Graph.
# tenantID, clientSecret and clientID not here.
# The resource URI
$resource = "https://graph.microsoft.com"
# Your Client ID and Client Secret obainted when registering your WebApp
$redirectUri = "http://returnuri"
# UrlEncode the ClientID and ClientSecret and URL's for special characters
$clientIDEncoded = [System.Web.HttpUtility]::UrlEncode($ClientID)
$clientSecretEncoded = [System.Web.HttpUtility]::UrlEncode($clientSecret)
$redirectUriEncoded = [System.Web.HttpUtility]::UrlEncode($redirectUri)
$resourceEncoded = [System.Web.HttpUtility]::UrlEncode($resource)
$scopeEncoded = [System.Web.HttpUtility]::UrlEncode("https://outlook.office.com/user.readwrite.all")
$body = #"
client_id=$ClientID
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=$clientSecretEncoded
&grant_type=client_credentials
"#
$userid = 'userID'
$accessToken = Invoke-RestMethod "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method Post -Body $body
#
#$cred = $(Get-Credential)
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$userid/messages" -Headers #{Authorization = "Bearer $($accessToken.access_token)"} -Credential $cred
Expected results: access to the mailbox.
Actual results:
Invoke-RestMethod : The remote server returned an error: (401) Unauthorized.
At line:32 char:1
+ Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$useri ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

'CompactToken parsing failed with error code: -2147184105' when trying to access Users with Office 365 Unified API using PowerShell

I am trying to list users using Office 365 Unified API with the following code:
$TenantID = "xxx"
$F_ClientID = "yyy"
$F_ClientSecret = "zzz"
Add-Type #'
using System;
public class OAuthContext{
public string AccessToken{get;set;}
public string TokenType{get;set;}
public string ExpiresIn{get;set;}
public string RefreshToken{get;set;}
}
'#
$Uri = "https://login.microsoftonline.com/$($TenantID)/oauth2/token"
$ContentType = 'application/x-www-form-urlencoded'
$Headers = #{}
$Body = [System.Text.Encoding]::UTF8.GetBytes('grant_type=client_credentials&client_id='+$F_ClientID+'&client_secret='+$F_Clie ntSecret+'&resource"=https://graph.microsoft.com')
$Response = Invoke-RestMethod -Method POST -Uri $Uri -Headers $Headers -ContentType $ContentType -Body $Body
$Response
$Context = New-Object OAuthContext
$Context.AccessToken = $Response.access_token
$Context.ExpiresIn = $Response.expires_in
$Context.RefreshToken = $Response.refresh_token
$Context.TokenType = $Response.token_type
$Context
$Headers = #{}
$Headers.Add('Authorization',$Context.TokenType + ' ' + $Context.AccessToken)
$Headers
$Uri = "https://graph.microsoft.com/v1.0/users"
Invoke-RestMethod -Method GET -Uri $Uri -Headers $Headers
As seen from the result, the access token seems to be successfully generated.
But when trying to list the users, I get the following error:
Invoke-RestMethod : {
"error": {
"code": "InvalidAuthenticationToken",
"message": "CompactToken parsing failed with error code: -2147184105",
"innerError": {
"request-id": "067c7044-0c59-4a39-86ac-b89e6b13229c",
"date": "2016-02-12T17:09:56"
}
}
}
At line:41 char:1
+ Invoke-RestMethod -Method GET -Uri $Uri -Headers $Headers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I don't really know what I am doing wrong here!
Thanks for your help!
The response actually indicates that the access token was not successfully generated or passed to the graph endpoint. Microsoft Graph couldn't parse it as a JWT token and thus attempted to process it as a Microsoft Account/Live Id compact token, which also failed. Please check the response that you got from the call to login.microsoftonline.com and that the token passed to graph.microsoft.com is a valid JWT token.
Can you check through this page if the client secret that you are sending matches the result of the page when coding it?
The recipient when viewing 'application / x-www-form-urlencoded' will decode the url, and if your client secret is not encode well, someone characters will disappear. (This was my problem)
I used this code and it worked
What i recommend is to test your query you are sending to the graph api by using the graph explorer tool first. and then mimic the same request in your PS script.
https://graphexplorer2.azurewebsites.net