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

Viewed 321

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:

enter image description here

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!

2 Answers

I tried with same permissions that you have provided for your Service Principal with the below script and it successfully gave me the output.

enter image description here

$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 

enter image description here

Which is same as the output I get from Graph Explorer.

enter image description here

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 .

enter image description here

enter image description here

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:

enter image description here

enter image description here

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.

Related