Describe the bug
"az ad app permission grant" only seems to grant a single scope. If I call it successive times, the existing scope is overwritten. This behaviour is not clearly documented, nor is the way to grant. multiple scopes.
"az ad app permission admin-consent" appears to be non-functional. How can admin consent be granted without three key pieces of information, AppID, APIID, and Scope? What is this doing? How can a single scope be granted at the application level?
I would also consider it a bug that "az ad app permission grant" cannot be used to grant application type scopes.
To Reproduce
Try to execute the above commands. "az ad app permission admin-consent" is not documented in any way that makes sense. "az ad app permission grant" seems to work, but successive calls over-write your work.
Expected behavior
There should be a single command that allows you to grant individual (or comma separated lists) of scopes for an API to an app. That same command should have a flag for Delegated vs Application Type. It should not overwrite itself. Worst case, if there have to be two commands, the one used to grant admin-consent to the application type should allow appropriate parameters.
Documentation of these calls should be clear and concise.
Environment summary
Windows 10. Installed via downloaded MSI for x64. cmd.exe shell.
C:\Program Files\Microsoft SDKs\Azure.NET SDK\v2.9\bin>az --version
azure-cli 2.0.81
command-modules-nspkg 2.0.3
core 2.0.81
nspkg 3.0.4
telemetry 1.0.4
Python location 'C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\python.exe'
Extensions directory 'C:Users\dpomerantz.azure\cliextensions'
Python (Windows) 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 02:47:15) [MSC v.1900 32 bit (Intel)]
Hi @jiasli , could you please help take a look at this ?
add to S166.
This was pushed to a future release. How many times is it going to get pushed before being addressed and resolved. Is any one able to comment on a commitment that this will be resolved with a definitive timeline?
az ad app permission admin-consent
is the old way of granting all Application Permissions and Delegated Permissions to an app at the same time. It calls an internal API: https://github.com/Azure/azure-cli/blob/1c0e9c3e18c34116d5a2e25cb7460d1af8a85969/src/azure-cli/azure/cli/command_modules/role/custom.py#L893
โ It can only be called by a user, not a service principal.
โ It fails in Cloud Shell, because main.iam.ad.ext.azure.com
is an endpoint not supported by Cloud Shell.
โ We will deprecate it in the future.
For example, we can call it with app's Application ID:
az ad app permission admin-consent --id 46eb4122-bd2b-4f54-af7b-6d79b46ee31a
Before:
After:
To grant Delegated Permissions, we recommend using az ad app permission grant
. If multiple scopes are needed, separate them with spaces and surround the scopes with quotes: "a b"
. For example, to grant MS Graph (whose Application ID is 00000003-0000-0000-c000-000000000000
) Delegated Permission to my app (Application ID 46eb4122-bd2b-4f54-af7b-6d79b46ee31a
):
# Line breaks for legibility only, same for following commands
az ad app permission grant --id 46eb4122-bd2b-4f54-af7b-6d79b46ee31a
--api 00000003-0000-0000-c000-000000000000
--scope "Directory.Read.All Directory.ReadWrite.All"
To grant Application Permissions, we can use any of these 2 APIs:
{principalId}/appRoleAssignments
# URL contains principalId
az rest --method POST
--uri https://graph.microsoft.com/v1.0/servicePrincipals/8aaa6158-7450-4407-ba08-61377f23d05f/appRoleAssignments
--body '{
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7"
}'
{resourceId}/appRoleAssignedTo
# URL contains resourceId
az rest --method POST
--uri https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignedTo
--body '{
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7"
}'
principalId
(8aaa6158-7450-4407-ba08-61377f23d05f
) is the objectId
of the assigned client service principal. Service principal is also called Managed application in local directory or Enterprise Application.
You may get it from Azure Portal:
Or query principalId
with Application ID:
> az ad sp show --id 46eb4122-bd2b-4f54-af7b-6d79b46ee31a --query "objectId" --output tsv
8aaa6158-7450-4407-ba08-61377f23d05f
resourceId
(a3efc889-f1b7-4532-9e01-91e32d1039f4
) is the objectId
of the resource service principal. In this case it is for MS Graph's service principal. It varies in different tenants (directories). Retrieve it with MS Graph's Application ID:
$ az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "objectId" --output tsv
a3efc889-f1b7-4532-9e01-91e32d1039f4
appRoleId
(19dbc75e-c2e2-444c-a770-ec69d8559fc7
) is the ID of the permission. In this case, it is for Directory.ReadWrite.All
. Retrieve it with MS Graph's Application ID:
> az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?value=='Directory.ReadWrite.All']"
[
{
"allowedMemberTypes": [
"Application"
],
"description": "Allows the app to read and write data in your organization's directory, such as users, and groups, without a signed-in user. Does not allow user or group deletion.",
"displayName": "Read and write directory data",
"id": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"isEnabled": true,
"value": "Directory.ReadWrite.All"
}
]
You may also use F12 Developer Tools of the browser to capture network trace to check related ID conversions. (Quite complicated I admit.)
Thanks for all of the detailed information! I'll take some time and digest this, and come back if I have any further questions on this. I understand a bit more on the delays though.
Hi @dmprantz , thanks a lot for your understanding. We have been working tightly with AAD team on this feature. Don't hesitate to let us know if there are any concerns.
For the granting app (or service principal, which you use to log in and grant permissions to another service principal),
โ 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).
If you use az ad
command, make sure the permission is given under Azure Active Directory Graph, since az ad
internally uses AD Graph API. If you use az rest --uri https://graph.microsoft.com/
, the permission should be given under Microsoft Graph.
Reference: https://winsmarts.com/how-to-grant-admin-consent-to-an-api-programmatically-e32f4a100e9d
Hi @jiasli ,using the az ad app permission grant
command to grand Delegated Permission is giving us a 404 NotFound error (like #12797, but we are NOT using a Service Principal). In our case the Delegated Permission is from Microsoft Graph (Directory.ReadWrite.All). Can you please clarify HOW to grant Delegated Permissions programmatically?
I found a solution.
BEFORE invoking the az ad app permission grant
, the Service Principal for your App Registration MUST exists (that's why you get the 404 NotFound!). So, invoking the az ad sp create --id <yourAppId>
before the az ad app permission grant
will fix everything.
This is really confusing and undocumented.
@fume, glad to know it is solved. You may use --debug
to check which request failed and fix it accordingly. BTW, We will track MS Graph related issues at #12946.
Service Principal API of Microsoft Graph is now GA. I have updated my answer https://github.com/Azure/azure-cli/issues/12137#issuecomment-596567479 to reflect the most recent changes.
The GA APIs have some changes from this blog I provided earlier for granting Application Permissions.
appRoleAssignments
and appRoleAssignedTo
are interchanged.We can consider the assignment as a relationship/binding between 2 service principals. There are 4 ways to create an assignment.
In the blog, the APIs are
{resourceId}/appRoleAssignments
{principalId}/appRoleAssignedTo
Now the official APIs are
We can draw a diagram like this:
| Id | | API | Correctness
|-|-|-|-
| {principalId}
| โก | appRoleAssignments
| (1) ๐ข Correct
| | โ | | (2) ๐ก Undocumented, unsupported
| | โ | | (3) ๐ก Undocumented, unsupported
| {resourceId}
| โก | appRoleAssignedTo
| (4) ๐ข Correct
Ideally the same URL can be used for 4 operations:
POST
to createGET
to listGET
to show, by appending the id
of the assignmentDELETE
to delete, by appending the id
of the assignmentTo use the above operations with URLs:
{principalId}/appRoleAssignments
and {resourceId}/appRoleAssignedTo
are the only official ways. All 4 operations are supported.{resourceId}/appRoleAssignments
and {principalId}/appRoleAssignedTo
are undocumented and unsupported. They only work for an individual assignment (operation 1, 3, 4), but not GET
to list (operation 2).If we enumerate all combinations:
POST
:
POST {principalId}/appRoleAssignments
POST {resourceId}/appRoleAssignedTo
POST {principalId}/appRoleAssignedTo
(Unsupported)POST {resourceId}/appRoleAssignments
(Unsupported)GET
to list:
GET {principalId}/appRoleAssignments
GET {resourceId}/appRoleAssignedTo
GET {principalId}/appRoleAssignedTo
(Not return the desired result)GET {resourceId}/appRoleAssignments
(Not return the desired result)GET
to show:
GET {principalId}/appRoleAssignments/id
GET {resourceId}/appRoleAssignedTo/id
GET {principalId}/appRoleAssignedTo/id
(Unsupported)GET {resourceId}/appRoleAssignments/id
(Unsupported)DELETE
:
DELETE {principalId}/appRoleAssignments/id
DELETE {resourceId}/appRoleAssignedTo/id
DELETE {principalId}/appRoleAssignedTo/id
(Unsupported)DELETE {resourceId}/appRoleAssignments/id
(Unsupported){principalId}/appRoleAssignments
(1)# POST {principalId}/appRoleAssignments (1)
$ az rest --method POST --url https://graph.microsoft.com/v1.0/servicePrincipals/8aaa6158-7450-4407-ba08-61377f23d05f/appRoleAssignments --body '{"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f","resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4","appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7"}'
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('8aaa6158-7450-4407-ba08-61377f23d05f')/appRoleAssignments/$entity",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:12:09.7096052Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX_E14wqwY2lBhDlpweb26uo",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
# GET {principalId}/appRoleAssignments (1) to list
$ az rest --method GET --url https://graph.microsoft.com/v1.0/servicePrincipals/8aaa6158-7450-4407-ba08-61377f23d05f/appRoleAssignments
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('8aaa6158-7450-4407-ba08-61377f23d05f')/appRoleAssignments",
"value": [
{
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:12:09.7096052Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX_E14wqwY2lBhDlpweb26uo",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
]
}
# GET {resourceId}/appRoleAssignments (3) to list doesn't return the desired assignment
$ az rest --method GET --url https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignments
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('a3efc889-f1b7-4532-9e01-91e32d1039f4')/appRoleAssignments",
"value": []
}
# GET {resourceId}/appRoleAssignments/{id} (3) to show is unsupported, but works
$ az rest --method GET --url https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignments/WGGqilB0B0S6CGE3fyPQX_E14wqwY2lBhDlpweb26uo
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('a3efc889-f1b7-4532-9e01-91e32d1039f4')/appRoleAssignments/$entity",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:12:09.7096052Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX_E14wqwY2lBhDlpweb26uo",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
# DELETE {principalId}/appRoleAssignments/{id} (1)
$ az rest --method DELETE --url https://graph.microsoft.com/v1.0/servicePrincipals/8aaa6158-7450-4407-ba08-61377f23d05f/appRoleAssignments/WGGqilB0B0S6CGE3fyPQX0ka7r14XhdLpH3TFDkPj6I
{resourceId}/appRoleAssignedTo
(4)# POST {resourceId}/appRoleAssignments (4)
$ az rest --method POST --url https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignedTo --body '{"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f","resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4","appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7"}'
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('a3efc889-f1b7-4532-9e01-91e32d1039f4')/appRoleAssignedTo/$entity",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:13:58.8505897Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
# We don't list all appRoleAssignedTo items because the list is too large
# GET {resourceId}/appRoleAssignments/{id} (4) to show
$ az rest --method GET --url https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignedTo/WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('a3efc889-f1b7-4532-9e01-91e32d1039f4')/appRoleAssignedTo/$entity",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:13:58.8505897Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
# GET {principalId}/appRoleAssignedTo/{id} (2) to show is unsupported, but works
az rest --method GET --url https://graph.microsoft.com/v1.0/servicePrincipals/8aaa6158-7450-4407-ba08-61377f23d05f/appRoleAssignedTo/WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#servicePrincipals('8aaa6158-7450-4407-ba08-61377f23d05f')/appRoleAssignedTo/$entity",
"appRoleId": "19dbc75e-c2e2-444c-a770-ec69d8559fc7",
"createdDateTime": "2020-05-26T08:13:58.8505897Z",
"deletedDateTime": null,
"id": "WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y",
"principalDisplayName": "myapp",
"principalId": "8aaa6158-7450-4407-ba08-61377f23d05f",
"principalType": "ServicePrincipal",
"resourceDisplayName": "Microsoft Graph",
"resourceId": "a3efc889-f1b7-4532-9e01-91e32d1039f4"
}
# DELETE {resourceId}/appRoleAssignments/{id} (4)
$ az rest --method DELETE --url https://graph.microsoft.com/v1.0/servicePrincipals/a3efc889-f1b7-4532-9e01-91e32d1039f4/appRoleAssignedTo/WGGqilB0B0S6CGE3fyPQX1Gp1oD6w_JCqCeCp4QUT0Y
However I still find the doc titles of the API appRoleAssignments and appRoleAssignedTo confusing. Will track at issue https://github.com/microsoftgraph/microsoft-graph-docs/issues/8412.
RE: https://github.com/Azure/azure-cli/issues/12137#issuecomment-596567479
โ We will deprecate it in the future.
@jiasli any indication when az ad app permission admin-consent
will be deprecated, and will there (please) be another CLI function to wrap the API calls you are documenting above?
Most helpful comment
az ad app permission admin-consent
is the old way of granting all Application Permissions and Delegated Permissions to an app at the same time. It calls an internal API: https://github.com/Azure/azure-cli/blob/1c0e9c3e18c34116d5a2e25cb7460d1af8a85969/src/azure-cli/azure/cli/command_modules/role/custom.py#L893โ It can only be called by a user, not a service principal.
โ It fails in Cloud Shell, because
main.iam.ad.ext.azure.com
is an endpoint not supported by Cloud Shell.โ We will deprecate it in the future.
For example, we can call it with app's Application ID:
Before:
After:
Grant Delegated Permissions
To grant Delegated Permissions, we recommend using
az ad app permission grant
. If multiple scopes are needed, separate them with spaces and surround the scopes with quotes:"a b"
. For example, to grant MS Graph (whose Application ID is00000003-0000-0000-c000-000000000000
) Delegated Permission to my app (Application ID46eb4122-bd2b-4f54-af7b-6d79b46ee31a
):Grant Application Permissions
To grant Application Permissions, we can use any of these 2 APIs:
{principalId}/appRoleAssignments
{resourceId}/appRoleAssignedTo
principalId
(8aaa6158-7450-4407-ba08-61377f23d05f
) is theobjectId
of the assigned client service principal. Service principal is also called Managed application in local directory or Enterprise Application.You may get it from Azure Portal:
Or query
principalId
with Application ID:resourceId
(a3efc889-f1b7-4532-9e01-91e32d1039f4
) is theobjectId
of the resource service principal. In this case it is for MS Graph's service principal. It varies in different tenants (directories). Retrieve it with MS Graph's Application ID:appRoleId
(19dbc75e-c2e2-444c-a770-ec69d8559fc7
) is the ID of the permission. In this case, it is forDirectory.ReadWrite.All
. Retrieve it with MS Graph's Application ID:You may also use F12 Developer Tools of the browser to capture network trace to check related ID conversions. (Quite complicated I admit.)