Skip to content

{Role} az role assignment create: Use RoleAssignmentExists error code to implement idempotence#32027

Merged
jiasli merged 1 commit intoAzure:devfrom
jiasli:RoleAssignmentExists
Sep 5, 2025
Merged

{Role} az role assignment create: Use RoleAssignmentExists error code to implement idempotence#32027
jiasli merged 1 commit intoAzure:devfrom
jiasli:RoleAssignmentExists

Conversation

@jiasli
Copy link
Copy Markdown
Member

@jiasli jiasli commented Aug 29, 2025

Related command
az role assignment create

Description
Resolve #31995

az role assignment create command relies on the response message ('role assignment already exists' in ex.message) to implement idempotence. The code was introduced by #9108 in 2019 and hasn't been changed since then.

However, recently ARM is making a breaking change to the response message on the existing API version 2022-04-01 without an API version update. This breaks the idempotence of az role assignment create command.

This PR uses the error code (ex.error.code == 'RoleAssignmentExists') to detect if there is an existing role assignment.

Testing Guide

az role assignment create --assignee a7003d8c-e50f-4371-91a6-ef37bba4ab23 --role reader --scope /subscriptions/0b1f6471-1bf0-4dda-aec3-cb9272f09590
# Run the command again and make sure no error is returned
az role assignment create --assignee a7003d8c-e50f-4371-91a6-ef37bba4ab23 --role reader --scope /subscriptions/0b1f6471-1bf0-4dda-aec3-cb9272f09590

History Notes

[Component Name 1] BREAKING CHANGE: az command a: Make some customer-facing breaking change
[Component Name 2] az command b: Add some customer-facing feature


This checklist is used to make sure that common guidelines for a pull request are followed.

@azure-client-tools-bot-prd
Copy link
Copy Markdown

azure-client-tools-bot-prd bot commented Aug 29, 2025

️✔️AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
️✔️core
️✔️latest
️✔️3.12
️✔️3.13
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@azure-client-tools-bot-prd
Copy link
Copy Markdown

azure-client-tools-bot-prd bot commented Aug 29, 2025

️✔️AzureCLI-BreakingChangeTest
️✔️Non Breaking Changes

@yonzhan
Copy link
Copy Markdown
Collaborator

yonzhan commented Aug 29, 2025

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link
Copy Markdown

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@microsoft-github-policy-service microsoft-github-policy-service bot added the Auto-Assign Auto assign by bot label Aug 29, 2025
@jiasli jiasli marked this pull request as ready for review September 1, 2025 07:38
Copilot AI review requested due to automatic review settings September 1, 2025 07:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses a breaking change in ARM's response message for existing role assignments by updating the error detection logic in az role assignment create command to use error codes instead of message content for implementing idempotence.

  • Updates error detection from message-based to error code-based approach
  • Maintains backward compatibility while fixing broken idempotence behavior
  • Ensures reliable detection of existing role assignments regardless of message format changes

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


def _error_caused_by_role_assignment_exists(ex):
return getattr(ex, 'status_code', None) == 409 and 'role assignment already exists' in ex.message
return getattr(ex, 'status_code', None) == 409 and ex.error.code == 'RoleAssignmentExists'
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code assumes ex.error exists but doesn't handle the case where ex.error might be None. This could cause an AttributeError if the exception object doesn't have an error attribute. Consider using getattr(ex, 'error', None) and checking if it's not None before accessing the code attribute.

Suggested change
return getattr(ex, 'status_code', None) == 409 and ex.error.code == 'RoleAssignmentExists'
error = getattr(ex, 'error', None)
return getattr(ex, 'status_code', None) == 409 and error is not None and getattr(error, 'code', None) == 'RoleAssignmentExists'

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhoxing-ms, is it possible that ex.error is None?

Copy link
Copy Markdown
Contributor

@zhoxing-ms zhoxing-ms Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know, if the type of ex must be HttpResponseError, ex.error is guaranteed to exist, since many old codes are directly used ex.error.code for HttpResponseError. And because if ex has status_code attribute, it should indicate that ex is HttpResponseError, so, I personally speculate that this usage should be safe

@jiasli
Copy link
Copy Markdown
Member Author

jiasli commented Sep 1, 2025

az role assignment create's idempotence is tested by

# verify role assignment create is idempotent
self.cmd('role assignment create --assignee {upn} --role {role} --scope {rg_id}',
self.check("principalName", self.kwargs["upn"]))

if ex.error.code == 'RoleAssignmentExists':
logger.info('Role assignment already exists')
break
if retry_time < retry_times and ' does not exist in the directory ' in ex.message:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar bad pattern of relying on error message instead of error code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new code is indeed relying on error code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant, this line still relies on message:
if retry_time < retry_times and ' does not exist in the directory ' in ex.message

Copy link
Copy Markdown
Member Author

@jiasli jiasli Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I see your point. This PR only aims to use RoleAssignmentExists error code. I will inform the ARM owner fix this line. @zhoxing-ms

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll take a look when I have time and fix it in other PR

Copy link
Copy Markdown
Member

@FumingZhang FumingZhang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved for acs(aks)

@jiasli jiasli merged commit cf11272 into Azure:dev Sep 5, 2025
48 checks passed
@jiasli jiasli deleted the RoleAssignmentExists branch September 5, 2025 06:58
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 RBAC az role

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Idempotency of az role assignment create is not working

7 participants