How to grant admin consent to an API programmatically

Sahil Malik
Winsmarts.com
Published in
4 min readFeb 23, 2020

--

Here is the issue I am trying to solve. When logged in as a service principal, say in CI/CD scenarios I wish to grant admin consent to an API. So in the below image, you see that exclamation mark? If from the Azure portal I was to click “grant admin consent”, that will allow this app to access MSGraph User.Read.All. I want to do exactly that, while logged in as service principal.

Note that the way you do it for delegated permissions vs. application permissions is different. Here is how you do it for delegated permissions. The process for application permissions is at the bottom.

Important Note: While this blogpost demonstrates the steps using a service principal, effectively this means you have a service principal credential sitting around with fairly high permissions.This is obviously not ideal, so in production scenarios, I highly recommend that you perform these steps using managed identities instead. The process of granting permission to a managed identity is as illustrated here.However, for illustrative and simplicity purposes, this blogpost shows the concepts using a service principal.

Delegated Permission

Here is how to do it (with full repro steps).

Create a service principal

az ad sp create-for-rbac -n sahil

Note down it’s credentials,

{"appId": "truncated","displayName": "sahil","name": "http://sahil","password": "truncated","tenant": "truncated"}

Now, to this newly created service principal, manually (but this is a one time thing), grant the following MSGraph permission, ensure to grant admin consent here.

Go ahead and login as that service principal,

az login --service-principal -u "appIdFromAbove" -p "passwordFromAbove" -t "tenantIDFromAbove"

Grab the access token for this service principal,

az account get-access-token --resource https://graph.microsoft.com | jq .\accessToken | pbcopy

It’s in your clipboard, I’ll call it bearertoken_slartibartfast so you can search in this article and see where it is used.

Now create a second application, call it “test” (or whatever you want). And grant it a graph scope as below, but do not click grant-admin-consent,

Now issue a POST request as follows to grant User.Read.All to this “test” application.

curl --location --request POST 'https://graph.microsoft.com/v1.0/oauth2PermissionGrants' \
--header 'Authorization: Bearer <bearertoken_slartibartfast>' \
--header 'Content-Type: application/json' \
--data-raw '{
"clientId": "theObjectId_for_the_service_principal_for_test",
"consentType": "AllPrincipals",
"expiryTime": "2018-05-12T19:34:28.9831598Z",
"principalId": null,
"resourceId": "id_of_resource_principal_for_msgraph",
"scope": "User.Read.All"
}'

Now there seems to be a small replication delay (few seconds), but after a few seconds, you should see the grant reflected here.

Application Permission

The way you do this for application permission is slightly (but not too) different.

First, the granting service principal (the one you are logged in as) is going to need an additional permission. Specifically, you’ll need to grant the AppRoleAssignment.ReadWrite.All permission.

Keep in mind that AppRoleAssignment.ReadWrite.All is extremely privileged, as it allows granting any app-only permission, including RoleManagement.ReadWrite.Directory, which can then be used to give anyone (or any app) even higher privileges up to and including Company Administrator (i.e. Global Admin).

A much better way is to use consent policies, which allow you to restrict what permissions can an automation account grant, without granting AppRoleAssignment.ReadWrite.All.

That’s it, now go ahead and login as the service principal, and grab a new access token.

az account get-access-token --resource https://graph.microsoft.com | jq .\accessToken | pbcopy

Ensure you have a permission ready to go but not given admin consent for

And execute the below POST request,

curl --location --request POST 'https://graph.microsoft.com/v1.0/servicePrincipals/9654473a-512a-4c6a-8525-02cc112c5b08/appRoleAssignments' \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"principalId": "..",
"resourceId": "9654473a-512a-4c6a-8525-02cc112c5b08",
"appRoleId": "df021288-bdef-4463-88db-98f22de89214"
}'

Here note the GUID 9654473a-512a-4c6a-8525–02cc112c5b08 is the GUID of the resource servicePrincipal which has defined the app role .. in this case MSGraph’s servicePrincipal GUID is 965….

Note that there is an equivalent command that lets you attack this problem from the principalID as, same payload, but you target

https://graph.microsoft.com/v1.0/servicePrincipals/<principalId>/appRoleAssignedTo

The command for that would look like this,

curl --location --request POST 'https://graph.microsoft.com/v1.0/servicePrincipals/5dd72f2f-e305-413c-8c25-78a18e152cd2/appRoleAssignedTo' \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"principalId": "..",
"resourceId": "9654473a-512a-4c6a-8525-02cc112c5b08",
"appRoleId": "df021288-bdef-4463-88db-98f22de89214"
}'

The principalId is the GUID of the user, group or client servicePrincipal to which you are assigning the app role. In this case the ObjectID of the service Principal of the app you are granting permissions to.

And the appRoleId is the GUID that represents the appRole, so User.Read.All is df021288-bdef-4463–88db-98f22de89214.

That’s it! Run the CURL and you should have the grant,

EZYPEZY!

--

--