Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payload for authV2 running in Azure DevOps Pipelines invalid #30695

Closed
Tom-CT opened this issue Jan 22, 2025 · 5 comments
Closed

Payload for authV2 running in Azure DevOps Pipelines invalid #30695

Tom-CT opened this issue Jan 22, 2025 · 5 comments
Assignees
Labels
Auto-Assign Auto assign by bot Auto-Resolve Auto resolve by bot Azure CLI Team The command of the issue is owned by Azure CLI team customer-reported Issues that are reported by GitHub users external to the Azure organization. Graph az ad question The issue doesn't require a change to the product in order to be resolved. Most issues start as that
Milestone

Comments

@Tom-CT
Copy link

Tom-CT commented Jan 22, 2025

Describe the bug

I am trying to use az ad app update --id [appId] --set api=[json] to add a scope to an app registration. Locally I am using powershell 7.4.6, cli 2.67.0 and authV2 0.1.3 on win 24H2.

The following is the code used to reproduce the issue:

$scopeId = [guid]::NewGuid().Guid
$userImpersonationScope = [ordered]@{
    adminConsentDescription = "user impersonation"
    adminConsentDisplayName = "user_impersonation"
    id                      = "$scopeId"
    isEnabled               = "true"
    type                    = "User"
    userConsentDescription  = "user impersonation"
    userConsentDisplayName  = "user_impersonation"
    value                   = "user_impersonation"
}

Write-Host "user_impersonation permission scope not found - creating with id: $scopeId"

$update = @{
    oauth2PermissionScopes = @($userImpersonationScope)
}
$updateJson = ConvertTo-Json $update -Depth 4 -Compress 
$escapedJson = ConvertTo-Json $updateJson

az ad app update --id $appId --set api=$updateJson

If I run this locally, I get a 200 and adding the --debug flag to the az command, I can see there's a payload that looks like:

{"api": {"oauth2PermissionScopes": [{"adminConsentDescription": "user impersonation", "adminConsentDisplayName": "user_impersonation", "id": "[some guid]", "isEnabled": "true", "type": "User", "userConsentDescription": "user impersonation", "userConsentDisplayName": "user_impersonation", "value": "user_impersonation"}]}}

If I run the same thing in an Azure Pipeline on a windows-latest agent, I get a 400 and can see it has a payload that looks like:

{"api": "{\"oauth2PermissionScopes\":[{\"adminConsentDescription\":\"user", "impersonation\",\"adminConsentDisplayName\":\"user_impersonation\",\"id\":\"[some guid]\",\"isEnabled\":\"true\",\"type\":\"User\",\"userConsentDescription\":\"user": "", "impersonation\",\"userConsentDisplayName\":\"user_impersonation\",\"value\":\"user_impersonation\"}]}": ""}

I have added additional logging in and can confirm that $updateJson and $escapedJson are identical when running locally and in the pipeline. I have also checked that the service connection that the pipeline is using for the AzureCLI@2 task this is running in has sufficient privileges to view and modify registered applications.

The only difference I can really see is that I have the authV2 extension installed locally, but to ensure that it is being used by the pipeline, I have this towards the top of my script:

az extension add --name authV2

Related command

az ad app update --id [appId] --set api="{\"oauth2PermissionScopes\":[{\"adminConsentDescription\":\"user impersonation\",\"adminConsentDisplayName\":\"user_impersonation\",\"id\":\"[some guid]\",\"isEnabled\":\"true\",\"type\":\"User\",\"userConsentDescription\":\"user impersonation\",\"userConsentDisplayName\":\"user_impersonation\",\"value\":\"user_impersonation\"}]}"

Errors

{"error":{"code":"BadRequest","message":"Property api in payload has a value that does not match schema.","innerError":{"date":"2025-01-22T12:07:30","request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931","client-request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931"}}}

Issue script & Debug output

INFO: cli.azure.cli.core.util: Request body:
INFO: cli.azure.cli.core.util: {"api": "{\"oauth2PermissionScopes\":[{\"adminConsentDescription\":\"user", "impersonation\",\"adminConsentDisplayName\":\"user_impersonation\",\"id\":\"ef874f2c-d624-480d-bc54-caa096e9a443\",\"isEnabled\":\"true\",\"type\":\"User\",\"userConsentDescription\":\"user": "", "impersonation\",\"userConsentDisplayName\":\"user_impersonation\",\"value\":\"user_impersonation\"}]}": ""}
DEBUG: urllib3.connectionpool: Starting new HTTPS connection (1): graph.microsoft.com:443
DEBUG: urllib3.connectionpool: [https://graph.microsoft.com:443](https://graph.microsoft.com/) "PATCH /v1.0/applications/[app-id-hidden] HTTP/1.1" 400 None
INFO: cli.azure.cli.core.util: Response status: 400
INFO: cli.azure.cli.core.util: Response headers:
INFO: cli.azure.cli.core.util:     'Transfer-Encoding': 'chunked'
INFO: cli.azure.cli.core.util:     'Content-Type': 'application/json'
INFO: cli.azure.cli.core.util:     'Content-Encoding': 'gzip'
INFO: cli.azure.cli.core.util:     'Vary': 'Accept-Encoding'
INFO: cli.azure.cli.core.util:     'Strict-Transport-Security': 'max-age=31536000'
INFO: cli.azure.cli.core.util:     'request-id': '86c0e7aa-05c1-4ffa-b2da-e6e55edfe931'
INFO: cli.azure.cli.core.util:     'client-request-id': '86c0e7aa-05c1-4ffa-b2da-e6e55edfe931'
INFO: cli.azure.cli.core.util:     'x-ms-ags-diagnostic': '{"ServerInfo":{"DataCenter":"UK South","Slice":"E","Ring":"5","ScaleUnit":"001","RoleInstance":"LN2PEPF00012B76"}}'
INFO: cli.azure.cli.core.util:     'Date': 'Wed, 22 Jan 2025 12:07:30 GMT'
INFO: cli.azure.cli.core.util: Response content:
INFO: cli.azure.cli.core.util: {"error":{"code":"BadRequest","message":"Property api in payload has a value that does not match schema.","innerError":{"date":"2025-01-22T12:07:30","request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931","client-request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931"}}}
DEBUG: cli.azure.cli.core.azclierror: Traceback (most recent call last):
  File "D:\a\_work\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/command_modules/role/_msgrpah/_graph_client.py", line 57, in _send
  File "D:\a\_work\1\s\build_scripts\windows\artifacts\cli\Lib\site-packages\azure/cli/core/util.py", line 1010, in send_raw_request
azure.cli.core.azclierror.HTTPError: Bad Request({"error":{"code":"BadRequest","message":"Property api in payload has a value that does not match schema.","innerError":{"date":"2025-01-22T12:07:30","request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931","client-request-id":"86c0e7aa-05c1-4ffa-b2da-e6e55edfe931"}}})

Expected behavior

authV2 should have a payload of when running under the AzureCLI@2 task:

{"api": {"oauth2PermissionScopes": [{"adminConsentDescription": "user impersonation", "adminConsentDisplayName": "user_impersonation", "id": "[some guid]", "isEnabled": "true", "type": "User", "userConsentDescription": "user impersonation", "userConsentDisplayName": "user_impersonation", "value": "user_impersonation"}]}}

Environment Summary

azure-cli 2.67.0 *

core 2.67.0 *
telemetry 1.1.0

Extensions:
account 0.2.5
authV2 0.1.3
datafactory 1.0.2

Dependencies:
msal 1.31.0
azure-mgmt-resource 23.1.1

Python location 'C:\Program Files\Microsoft SDKs\Azure\CLI2\python.exe'
Extensions directory 'C:\Users\Tom.Allen.azure\cliextensions'

Python (Windows) 3.12.7 (tags/v3.12.7:0b05ead, Oct 1 2024, 03:06:41) [MSC v.1941 64 bit (AMD64)]

Additional context

No response

@Tom-CT Tom-CT added the bug This issue requires a change to an existing behavior in the product in order to be resolved. label Jan 22, 2025
Copy link

Hi @Tom-CT,

2.67.0 is not the latest Azure CLI(2.68.0).

If you haven't already attempted to do so, please upgrade to the latest Azure CLI version by following https://learn.microsoft.com/en-us/cli/azure/update-azure-cli.

@azure-client-tools-bot-prd azure-client-tools-bot-prd bot added the Auto-Resolve Auto resolve by bot label Jan 22, 2025
@microsoft-github-policy-service microsoft-github-policy-service bot added customer-reported Issues that are reported by GitHub users external to the Azure organization. Auto-Assign Auto assign by bot Graph az ad labels Jan 22, 2025
@yonzhan
Copy link
Collaborator

yonzhan commented Jan 22, 2025

Thank you for opening this issue, we will look into it.

@microsoft-github-policy-service microsoft-github-policy-service bot added Azure CLI Team The command of the issue is owned by Azure CLI team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that labels Jan 22, 2025
@yonzhan yonzhan removed the bug This issue requires a change to an existing behavior in the product in order to be resolved. label Jan 22, 2025
@yonzhan yonzhan added this to the Backlog milestone Jan 22, 2025
@jiasli
Copy link
Member

jiasli commented Jan 23, 2025

I don't think this is related to authV2 extension. This is actually PowerShell quoting issue #15529.

The values of $updateJson and $escapedJson are:

> $updateJson
{"oauth2PermissionScopes":[{"adminConsentDescription":"user impersonation","adminConsentDisplayName":"user_impersonation","id":"6504e98c-0c83-424a-a0cc-7c8e4dbb5999","isEnabled":"true","type":"User","userConsentDescription":"user impersonation","userConsentDisplayName":"user_impersonation","value":"user_impersonation"}]}

> $escapedJson
"{\"oauth2PermissionScopes\":[{\"adminConsentDescription\":\"user impersonation\",\"adminConsentDisplayName\":\"user_impersonation\",\"id\":\"6504e98c-0c83-424a-a0cc-7c8e4dbb5999\",\"isEnabled\":\"true\",\"type\":\"User\",\"userConsentDescription\":\"user impersonation\",\"userConsentDisplayName\":\"user_impersonation\",\"value\":\"user_impersonation\"}]}"

When executed locally in PowerShell 7.4.6 on Windows, \ is interpreted by CMD to escape ":

> az --debug $escapedJson
cli.knack.cli: Command arguments: ['--debug', '{"oauth2PermissionScopes":[{"adminConsentDescription":"user impersonation","adminConsentDisplayName":"user_impersonation","id":"6504e98c-0c83-424a-a0cc-7c8e4dbb5999","isEnabled":"true","type":"User","userConsentDescription":"user impersonation","userConsentDisplayName":"user_impersonation","value":"user_impersonation"}]}']

In CMD, the escaping also works as expected:

>az --debug "{\"oauth2PermissionScopes\":[{\"adminConsentDescription\":\"user impersonation\",\"adminConsentDisplayName\":\"user_impersonation\",\"id\":\"[some guid]\",\"isEnabled\":\"true\",\"type\":\"User\",\"userConsentDescription\":\"user impersonation\",\"userConsentDisplayName\":\"user_impersonation\",\"value\":\"user_impersonation\"}]}"

cli.knack.cli: Command arguments: ['--debug', '{"oauth2PermissionScopes":[{"adminConsentDescription":"user impersonation","adminConsentDisplayName":"user_impersonation","id":"[some guid]","isEnabled":"true","type":"User","userConsentDescription":"user impersonation","userConsentDisplayName":"user_impersonation","value":"user_impersonation"}]}']

However, in PowerShell on Linux, CLI is receiving

> az --debug $escapedJson                                                                                                                                                    
cli.knack.cli: Command arguments: ['--debug', '"{\\"oauth2PermissionScopes\\":[{\\"adminConsentDescription\\":\\"user impersonation\\",\\"adminConsentDisplayName\\":\\"user_impersonation\\",\\"id\\":\\"3088a995-16a4-4f95-8197-38dfe2280cea\\",\\"isEnabled\\":\\"true\\",\\"type\\":\\"User\\",\\"userConsentDescription\\":\\"user impersonation\\",\\"userConsentDisplayName\\":\\"user_impersonation\\",\\"value\\":\\"user_impersonation\\"}]}"']

This matches your observation on Azure DevOps Pipelines, so please check if you are using a Linux agent.

Actually, instead of escaping the JSON and passing it as a shell parameter, I highly recommend following https://github.com/Azure/azure-cli/blob/dev/doc/quoting-issues-with-powershell.md#best-practice-use-file-input-for-json to use stdin instead:

> echo $updateJson | az rest --url 'https://127.0.0.1' --method POST --body '@-' --debug
...
cli.azure.cli.core.util: Request body:
cli.azure.cli.core.util: {"oauth2PermissionScopes": [{"adminConsentDescription": "user impersonation", "adminConsentDisplayName": "user_impersonation", "id": "3088a995-16a4-4f95-8197-38dfe2280cea", "isEnabled": "true", "type": "User", "userConsentDescription": "user impersonation", "userConsentDisplayName": "user_impersonation", "value": "user_impersonation"}]}

This bypasses shell quoting and ensures the JSON is passed to Azure CLI untouched.

@Tom-CT
Copy link
Author

Tom-CT commented Jan 23, 2025

Thanks, using stdin definitely makes it easier and helped solve my issue

@Tom-CT Tom-CT closed this as completed Jan 23, 2025
@dbradish-microsoft
Copy link
Contributor

@Tom-CT, here are some links to published docs that talk about quoting differences between environments:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auto-Assign Auto assign by bot Auto-Resolve Auto resolve by bot Azure CLI Team The command of the issue is owned by Azure CLI team customer-reported Issues that are reported by GitHub users external to the Azure organization. Graph az ad question The issue doesn't require a change to the product in order to be resolved. Most issues start as that
Projects
None yet
Development

No branches or pull requests

4 participants