How to create scope using Azure CLI (az ad app)
Asked Answered
O

6

11

Using the Azure CLI 2.x, I cannot find a way to "add a Scope" under the expose an API section in Azure AD Portal.

enter image description here

What I do see is if I pass the --identifier-uris when the app is created, the APP ID URI and a Scope get automatically set:

    `az ad app create --display-name "$appName" --identifier-uris "https://$tenantDomain/$appName" --reply-urls "$replyUrl" --oauth2-allow-implicit-flow true`

enter image description here

Not what I expected nor what I want

So, I removed --identifier-urls from the create command and added the scope I wanted in manually. then I see via manifest what I'm looking for under OAuth2Permissions as shown below. Can I put this in manifest.json with a new guid and insert it somehow?

enter image description here

What CLI command supports the explicit support to define a Scope? Then Adding a Client application I would need to select the defined Scope, how is this referenced?

Documentation is very sparse, IMO. This reference is very helpful but nothing in here talks about adding scopes and clients. https://learn.microsoft.com/en-us/cli/azure/ad?view=azure-cli-latest. Any help towards samples or documentation much appreciated.

Outvote answered 30/6, 2020 at 20:15 Comment(0)
O
2

With help from the thread above, and a ton of trial-n-error plus a pretty useful link, I was able to work out the CLI script to add scope using a windows environment. PowerShell is not happy with 'jq' on windows and use of the back-tick had to be removed to get things working. Now I need to solve adding preAuthorizedApplication with the CLI.

$userAccessScopeApi = '{
    "lang": null,
    "origin": "Application",        
    "adminConsentDescription": "Access CP Debug desc",
    "adminConsentDisplayName": "Access CP Debug",
    "id": "--- replaced in scripts ---",
    "isEnabled": true,
    "type": "Admin",
    "userConsentDescription": null,
    "userConsentDisplayName": null,
    "value": "Access"
}' | ConvertTo-Json | ConvertFrom-Json
`

Write-Host " -  1 read oauth2permissions"
#(az ad app show  --id $appid)
$appjson = (az ad app list --display-name $appName)         
$app = $appjson | ConvertFrom-Json
$oauth2Permissions = $app.oauth2Permissions
$oauth2Permissions[0].isEnabled = 'false'

$oauth2Permissionsjson = ConvertTo-Json -InputObject @($oauth2Permissions) 

Write-Host " -  2 disable oauth2Permission in Azure App Registration"
$oauth2Permissionsjson | Out-File -FilePath .\oauth2Permissionsold.json
az ad app update --id $appId --set [email protected]

Write-Host " -  3 delete the default oauth2Permission"
az ad app update --id $appId --set oauth2Permissions='[]'

Write-Host " -  4 add the new scope required add the new oauth2Permissions values"
$oauth2PermissionsApiNew = $userAccessScopeApi | ConvertFrom-Json
$oauth2PermissionsApiNew[0].id = New-Guid
$oauth2PermissionsApiNew = ConvertTo-Json -InputObject @($oauth2PermissionsApiNew) 

# Write-Host "new oauth2permissions : " + $oauth2PermissionsApiNew" 
$oauth2PermissionsApiNew | Out-File -FilePath .\oauth2Permissionsnew.json
az ad app update --id $appId --set [email protected]

Write-Host " - Updated scopes (oauth2Permissions) for App Registration: $appId"`
Outvote answered 1/7, 2020 at 21:0 Comment(0)
Q
11

As of 7/29/22 the latest Azure CLI command "az ad app update" does not include the oauth2permissions anymore. If you try the above, you will bang you head and hopefully find this post. The new location of these permissions on the app regs are located at api.oauth2PermissionScopes as an array.

In order to get around this I used a combination of a few items from this post and had to get creative as the Azure docs are also still incorrect.

It still stands true that if you have an existing API exposed, you have to disable it to modify the scopes. If you have a fresh app registration you can apply this direct without issue. Hopefully this helps someone like me that has automations that are now broken due to the changes to how apps are registered and the manifest changes. If you don't know about changes to app registrations, I recommend you review. If you got this far though, I assume you already did.

# Add API Read Scope: 
$scopeGUID = [guid]::NewGuid()
$scopeJSONHash = @{
    adminConsentDescription="$apiName on $svrAppRegName"
    adminConsentDisplayName="$apiName on $svrAppRegName" 
    id="$scopeGUID"
    isEnabled=$true
    type="User"
    userConsentDescription="$apiName on $svrAppRegName"
    userConsentDisplayName="$apiName on $svrAppRegName"
    value="$apiName"
}
$azAppOID = (az ad app show --id $serverApplicationId | ConvertFrom-JSON).id
$accesstoken = (Get-AzAccessToken -Resource "https://graph.microsoft.com/").Token
$header = @{
    'Content-Type' = 'application/json'
    'Authorization' = 'Bearer ' + $accesstoken
}
$bodyAPIAccess = @{
    'api' = @{
        'oauth2PermissionScopes' = @($scopeJSONHash)
    }
}|ConvertTo-Json -d 3

#You can try az rest, I used Invoke-RestMethod though.
#$graphURL="https://graph.microsoft.com/v1.0/applications/$azAppOID" 
#az rest --method PATCH --uri $graphurl --headers $header --body $bodyAPIAccess

Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/v1.0/applications/$azAppOID" -Headers $header -Body $bodyAPIAccess
Queue answered 29/7, 2022 at 22:29 Comment(2)
This is now the correct answer! Thanks - helped a lot. For others finding this - note the singular 'Permission' in 'oauth2PermissionScopes' (instead of the old 'oauth2permissions' plural). Got caught on this oneRechabite
I find is completely mad that Microsoft don't document their own API properly.En
U
6

From this article Azure CLI: Create an Azure AD application for an API that exposes OAuth2 Permissions

You can use the az ad app update command (see documentation)

You can then set an application’s property by using the optional parameter –set

  1. Create a oauth2-permissions.json containing the permission:

    [
      {
        "adminConsentDescription": "Access CP Debug Desc",
        "adminConsentDisplayName": "Access CP Debug",
        "id": "85b8f1a0-0733-47dd-9af4-cb7221dbcb73",
        "isEnabled": true,
        "type": "Admin",
        "userConsentDescription": null,
        "userConsentDisplayName": null,
        "value": "Access"
      }
    ]
    
  2. Run this script, it will create the app, disable the existing scope and add the new scope:

    # Create the app registration
    APP_REG=$(az ad app create --display-name myapi --identifier-uris https://myapi)
    
    # Get the app id
    APP_ID=$(echo $APP_REG | jq -r '.appId')
    
    # disable default exposed scope
    DEFAULT_SCOPE=$(az ad app show --id $APP_ID | jq '.oauth2Permissions[0].isEnabled = false' | jq -r '.oauth2Permissions')
    az ad app update --id $APP_ID --set oauth2Permissions="$DEFAULT_SCOPE"
    
    # Create new scopes from file 'oath2-permissions'
    az ad app update --id $APP_ID --set [email protected]
    
Urbanism answered 30/6, 2020 at 21:49 Comment(7)
Hi, this looks promising. However, this line $DEFAULT_SCOPE=$(az ad app show --id $appId | jq '.oauth2Permissions[0].isEnabled = false' | jq -r '.oauth2Permissions') Fails with jq : The term 'jq' is not recognized as the name of a cmdlet, function, script file, or .... I'll see if there's another way.Outvote
Are you running on windows or linux ? jq is a json query package for bash/shell stedolan.github.io/jqUrbanism
Running on windows. I'm looking for an equivalentOutvote
if you use powershell ConvertTo-Json and ConvertFrom-Json, it should do the trick ?Urbanism
If I know what to change, am i simply setting IsEnabled to false in the OAuthPermissions and then passing that JSON (the entire thing) back in the set? I can isolate the JSON block with this command : az ad app list --display-name $appName --query [].oauth2Permissions[0]Outvote
yeah it is to disable the default scope before creating a new oneUrbanism
Hi, can you shoot me what the DEFAULT_SCOPE looks like from the original code? I assumed it was either the entire app or the oauth2permissions block but nothing works. I get deserialize errors like this "Unable to build a model: Cannot deserialize as [OAuth2Permission] an object of type <class 'str'>, DeserializationError: Cannot deserialize as [OAuth2Permission] an object of type <class 'str'>. Looked in the docs but it doesn't come close to telling anyone. Have you run this code successfully?Outvote
F
6

As A2AdminGuy mentioned, it is not possible to update the oauth2Permissions direct anymore, but you can still use az ad app update.

  1. Execute the command az ad app show --id $appClientId and you will see that the oauth2PermissionScopes is inside the api now:
"api": {
    "acceptMappedClaims": null,
    "knownClientApplications": [],
    "oauth2PermissionScopes": [],
    "preAuthorizedApplications": [],
    "requestedAccessTokenVersion": null
}
  1. You need to update the api property to be able to set the oauth2PermissionScopes:
$apiScopeId = [guid]::NewGuid().Guid
$apiScopeJson = @{
    requestedAccessTokenVersion = 2
    oauth2PermissionScopes      = @(
        @{
            adminConsentDescription = "$AppName on $EnvironmentAbbrev"
            adminConsentDisplayName = "$AppName on $EnvironmentAbbrev"
            id                      = "$apiScopeId"
            isEnabled               = $true
            type                    = "User"
            userConsentDescription  = "$AppName on $EnvironmentAbbrev"
            userConsentDisplayName  = "$AppName on $EnvironmentAbbrev"
            value                   = "authenticate"
        }
    )
} | ConvertTo-Json -d 4 -Compress

$apiUpdateBody = $apiScopeJson | ConvertTo-Json -d 4

az ad app update --id $apiClientId --set api=$apiUpdateBody --verbose

The --verbose parameter can be removed, but interestingly, the update command is a PATCH request to the graph API.

Since it is a PATCH request, you don't always need to send the whole api property, you can do two requests to update different properties and the previous one will not be affected.

  1. This is how you can update a SPA redirectUris:
$appSpaJson = @{
    redirectUris = @("http://localhost:3000")
} | ConvertTo-Json -d 3 -Compress
    
$appUpdateBody = $appSpaJson | ConvertTo-Json -d 4

az ad app update --id $appClientId --set spa=$appUpdateBody
Fencible answered 6/12, 2022 at 17:24 Comment(3)
You mean Patch request to the api?Colorant
Yes. Just fixed my text. ThanksFencible
You cannot use $apiUpdateBody = $apiScopeJson | ConvertTo-Json -d 4, you just need to call the update command with the apiScopeJson : az ad app update --id $apiClientId --set api=$apiScopeJson --verboseSeraglio
U
6

Here's the approach I ended up with in bash on WSL Ubuntu, in the event it's useful for anyone else:

# replace all <value> blocks with your own value

# create app registration and extract appId
clientid=$(az ad app create \
    --display-name <name> \
    --query appId \
    --output tsv)

# generate a UUID for the scope
uuid=$(uuidgen)

# set the API object as a JSON object
api=$(echo '{
    "acceptMappedClaims": null,
    "knownClientApplications": [],
    "oauth2PermissionScopes": [{
        "adminConsentDescription": "<description>",
        "adminConsentDisplayName": "<name>",
        "id": "'$uuid'",
        "isEnabled": true,
        "type": "User",
        "userConsentDescription": "<description>",
        "userConsentDisplayName": "<name>",
        "value": "<value>"
    }],
    "preAuthorizedApplications": [],
    "requestedAccessTokenVersion": 2
}' | jq .)

# Update app registration with App ID URL and api object
az ad app update \
    --id $clientid \
    --identifier-uris api://$clientid \
    --set api="$api"
Unipolar answered 11/2, 2023 at 1:50 Comment(3)
This was helpful but worth noting that content of the api property must include all that's required. So, if run against an existing app reg that already has permission scopes errors will be raised if those scopes are not first disabledColorant
@RobBowman good to know, thanks for the heads up. I've been purely using this in the context of generating new app registrations.Unipolar
This helped me a lot, I was trying to do az ad app update --set oauth2Permissions="stuff" and not az ad app update --set api="a bit more stuff"Keary
O
2

With help from the thread above, and a ton of trial-n-error plus a pretty useful link, I was able to work out the CLI script to add scope using a windows environment. PowerShell is not happy with 'jq' on windows and use of the back-tick had to be removed to get things working. Now I need to solve adding preAuthorizedApplication with the CLI.

$userAccessScopeApi = '{
    "lang": null,
    "origin": "Application",        
    "adminConsentDescription": "Access CP Debug desc",
    "adminConsentDisplayName": "Access CP Debug",
    "id": "--- replaced in scripts ---",
    "isEnabled": true,
    "type": "Admin",
    "userConsentDescription": null,
    "userConsentDisplayName": null,
    "value": "Access"
}' | ConvertTo-Json | ConvertFrom-Json
`

Write-Host " -  1 read oauth2permissions"
#(az ad app show  --id $appid)
$appjson = (az ad app list --display-name $appName)         
$app = $appjson | ConvertFrom-Json
$oauth2Permissions = $app.oauth2Permissions
$oauth2Permissions[0].isEnabled = 'false'

$oauth2Permissionsjson = ConvertTo-Json -InputObject @($oauth2Permissions) 

Write-Host " -  2 disable oauth2Permission in Azure App Registration"
$oauth2Permissionsjson | Out-File -FilePath .\oauth2Permissionsold.json
az ad app update --id $appId --set [email protected]

Write-Host " -  3 delete the default oauth2Permission"
az ad app update --id $appId --set oauth2Permissions='[]'

Write-Host " -  4 add the new scope required add the new oauth2Permissions values"
$oauth2PermissionsApiNew = $userAccessScopeApi | ConvertFrom-Json
$oauth2PermissionsApiNew[0].id = New-Guid
$oauth2PermissionsApiNew = ConvertTo-Json -InputObject @($oauth2PermissionsApiNew) 

# Write-Host "new oauth2permissions : " + $oauth2PermissionsApiNew" 
$oauth2PermissionsApiNew | Out-File -FilePath .\oauth2Permissionsnew.json
az ad app update --id $appId --set [email protected]

Write-Host " - Updated scopes (oauth2Permissions) for App Registration: $appId"`
Outvote answered 1/7, 2020 at 21:0 Comment(0)
F
0

Here is the command that worked for me:

az ad app update --id ... --set [email protected]

And the contents of payload.json:

{
  "oauth2PermissionScopes": [
    {
      "adminConsentDescription": "Authenticate",
      "adminConsentDisplayName": "Authenticate",
      "id": "3c8d0da4-8000-49ab-b406-e2d601a12900",
      "isEnabled": true,
      "type": "User",
      "value": "Authenticate"
    }
  ]
}

As an aside, before I wrote this answer, I tried many different strategies based on the varied answers to this question, but here's a list of things that didn't work for me:

  • az update --id ... --set api.oauth2PermissionScopes=...: despite the documentation claiming this notation might work, I could not find a way to set nested properties.
  • az update --id ... --set api='{"oauth2PermissionScopes":[...]}': this is likely some type of shell quoting issues, but the @-file reference was the only way I could get the payload to take.
  • az update -id ... --set oauth2Permissions=...: I believe this strategy conflates the name of this property in the app manifest with the name of the property in the underlying MS Graph API call request body.

That being said, understanding that the --set flag is for setting the top-level properties (again, I never got nesting to work) of a request body to a PATCH call was the key for me. And since it's a PATCH, sending partial bodies is fine and won't blow away existing settings. For example, even though I believe there's flag for this, you can do:

az update -id ... --set displayName=blah

In the end, I find the az ad app update --set command to provide a tiny but of sugar over a direct API call with az rest.

Flume answered 27/3 at 3:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.