Skip to content

Commit 7472adf

Browse files
authored
[Role] az role assignment list/delete: Add --assignee-object-id (#30469)
1 parent 4c2ab21 commit 7472adf

File tree

5 files changed

+221
-46
lines changed

5 files changed

+221
-46
lines changed

src/azure-cli/azure/cli/command_modules/role/_help.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,8 @@
781781
text: az role assignment delete --role Reader --scope /subscriptions/00000000-0000-0000-0000-000000000000
782782
- name: Delete all role assignments of an assignee at the subscription scope.
783783
text: az role assignment delete --assignee 00000000-0000-0000-0000-000000000000 --scope /subscriptions/00000000-0000-0000-0000-000000000000
784+
- name: Delete all role assignments of an assignee (with its object ID) at the subscription scope.
785+
text: az role assignment delete --assignee-object-id 00000000-0000-0000-0000-000000000000 --scope /subscriptions/00000000-0000-0000-0000-000000000000
784786
"""
785787

786788
helps['role assignment list'] = """
@@ -796,12 +798,16 @@
796798
Delete classic administrators who no longer need access or assign an Azure RBAC role for fine-grained access
797799
control. Learn more: https://go.microsoft.com/fwlink/?linkid=2238474
798800
examples:
799-
- name: List all role assignments with "Reader" role at the subscription scope.
801+
- name: List role assignments at the subscription scope.
802+
text: az role assignment list --scope /subscriptions/00000000-0000-0000-0000-000000000000
803+
- name: List role assignments at the subscription scope, without filling roleDefinitionName property.
804+
text: az role assignment list --scope /subscriptions/00000000-0000-0000-0000-000000000000 --fill-role-definition-name false
805+
- name: List role assignments with "Reader" role at the subscription scope.
800806
text: az role assignment list --role Reader --scope /subscriptions/00000000-0000-0000-0000-000000000000
801-
- name: List all role assignments of an assignee at the subscription scope.
807+
- name: List role assignments of an assignee at the subscription scope.
802808
text: az role assignment list --assignee 00000000-0000-0000-0000-000000000000 --scope /subscriptions/00000000-0000-0000-0000-000000000000
803-
- name: List all role assignments with "Reader" role at the subscription scope, without filling principalName property
804-
text: az role assignment list --role Reader --scope /subscriptions/00000000-0000-0000-0000-000000000000 --fill-principal-name false
809+
- name: List role assignments of an assignee (with its object ID) at the subscription scope, without filling principalName property. This command does not query Microsoft Graph.
810+
text: az role assignment list --assignee-object-id 00000000-0000-0000-0000-000000000000 --scope /subscriptions/00000000-0000-0000-0000-000000000000 --fill-principal-name false
805811
"""
806812

807813
helps['role assignment list-changelogs'] = """

src/azure-cli/azure/cli/command_modules/role/_params.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,11 @@ def load_arguments(self, _):
327327
c.argument('include_inherited', action='store_true', help='include assignments applied on parent scopes')
328328
c.argument('can_delegate', action='store_true', help='when set, the assignee will be able to create further role assignments to the same role')
329329
c.argument('assignee', help='represent a user, group, or service principal. supported format: object id, user sign-in name, or service principal name')
330-
c.argument('assignee_object_id', help="Use this parameter instead of '--assignee' to bypass Graph API invocation in case of insufficient privileges. "
331-
"This parameter only works with object ids for users, groups, service principals, and "
332-
"managed identities. For managed identities use the principal id. For service principals, "
333-
"use the object id and not the app id.")
330+
c.argument('assignee_object_id',
331+
help="The assignee's object ID (also known as principal ID). "
332+
"Use this argument instead of '--assignee' to bypass Microsoft Graph query in case "
333+
"the logged-in account has no permission or the machine has no network access to query "
334+
"Microsoft Graph.")
334335
c.argument('ids', nargs='+', help='space-separated role assignment ids')
335336
c.argument('include_classic_administrators', arg_type=get_three_state_flag(),
336337
help='list default role assignments for subscription classic administrators, aka co-admins')
@@ -350,6 +351,8 @@ def load_arguments(self, _):
350351
c.argument('fill_role_definition_name', arg_type=get_three_state_flag(),
351352
help="Fill roleDefinitionName property in addition to roleDefinitionId. This operation is "
352353
"expensive. If you encounter performance issue, set this flag to false.")
354+
c.argument('include_groups', action='store_true',
355+
help='Include extra assignments to the groups of which the user is a member (transitively).')
353356

354357
time_help = 'The {} of the query in the format of %Y-%m-%dT%H:%M:%SZ, e.g. 2000-12-31T12:59:59Z. Defaults to {}'
355358
with self.argument_context('role assignment list-changelogs') as c:

src/azure-cli/azure/cli/command_modules/role/custom.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def create_role_assignment(cmd, role, scope,
209209
assignment_name=assignment_name)
210210
except Exception as ex: # pylint: disable=broad-except
211211
if _error_caused_by_role_assignment_exists(ex): # for idempotent
212-
return list_role_assignments(cmd, assignee=assignee, role=role, scope=scope)[0]
212+
return list_role_assignments(cmd, assignee_object_id=object_id, role=role, scope=scope)[0]
213213
raise
214214

215215

@@ -232,14 +232,19 @@ def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, s
232232
condition=condition, condition_version=condition_version)
233233

234234

235-
def list_role_assignments(cmd, assignee=None, role=None, resource_group_name=None, # pylint: disable=too-many-locals
236-
scope=None, include_inherited=False,
235+
def list_role_assignments(cmd, # pylint: disable=too-many-locals, too-many-branches
236+
assignee=None, assignee_object_id=None,
237+
role=None,
238+
resource_group_name=None, scope=None,
239+
include_inherited=False,
237240
show_all=False, include_groups=False, include_classic_administrators=False,
238241
fill_role_definition_name=True, fill_principal_name=True):
239-
'''
240-
:param include_groups: include extra assignments to the groups of which the user is a
241-
member(transitively).
242-
'''
242+
if assignee and assignee_object_id:
243+
raise CLIError('Usage error: Provide only one of --assignee or --assignee-object-id.')
244+
if assignee_object_id and include_classic_administrators:
245+
raise CLIError('Usage error: --assignee-object-id cannot be used with --include-classic-administrators. '
246+
'Use --assignee instead.')
247+
243248
if include_classic_administrators:
244249
logger.warning(CLASSIC_ADMINISTRATOR_WARNING)
245250

@@ -256,8 +261,10 @@ def list_role_assignments(cmd, assignee=None, role=None, resource_group_name=Non
256261
scope = _build_role_scope(resource_group_name, scope,
257262
definitions_client._config.subscription_id)
258263

264+
if assignee and not assignee_object_id:
265+
assignee_object_id = _resolve_object_id(cmd.cli_ctx, assignee, fallback_to_object_id=True)
259266
assignments = _search_role_assignments(cmd.cli_ctx, assignments_client, definitions_client,
260-
scope, assignee, role,
267+
scope, assignee_object_id, role,
261268
include_inherited, include_groups)
262269

263270
results = todict(assignments) if assignments else []
@@ -522,13 +529,19 @@ def _get_displayable_name(graph_object):
522529
return graph_object['displayName'] or ''
523530

524531

525-
def delete_role_assignments(cmd, ids=None, assignee=None, role=None, resource_group_name=None,
526-
scope=None, include_inherited=False,
532+
def delete_role_assignments(cmd, ids=None,
533+
assignee=None, assignee_object_id=None,
534+
role=None,
535+
resource_group_name=None, scope=None,
536+
include_inherited=False,
527537
yes=None): # pylint: disable=unused-argument
528538
# yes is currently a no-op
529-
if not any((ids, assignee, role, resource_group_name, scope)):
539+
if not any((ids, assignee, assignee_object_id, role, resource_group_name, scope)):
530540
raise ArgumentUsageError('Please provide at least one of these arguments: '
531-
'--ids, --assignee, --role, --resource-group, --scope')
541+
'--ids, --assignee, --assignee-object-id, --role, --resource-group, --scope')
542+
543+
if assignee and assignee_object_id:
544+
raise CLIError('Usage error: Provide only one of --assignee or --assignee-object-id.')
532545

533546
factory = _auth_client_factory(cmd.cli_ctx, scope)
534547
assignments_client = factory.role_assignments
@@ -559,8 +572,11 @@ def delete_role_assignments(cmd, ids=None, assignee=None, role=None, resource_gr
559572

560573
scope = _build_role_scope(resource_group_name, scope,
561574
assignments_client._config.subscription_id)
575+
# Delay resolving object ID, because if ids are provided, no need to resolve
576+
if assignee and not assignee_object_id:
577+
assignee_object_id = _resolve_object_id(cmd.cli_ctx, assignee, fallback_to_object_id=True)
562578
assignments = _search_role_assignments(cmd.cli_ctx, assignments_client, definitions_client,
563-
scope, assignee, role, include_inherited,
579+
scope, assignee_object_id, role, include_inherited,
564580
include_groups=False)
565581

566582
if assignments:
@@ -571,11 +587,7 @@ def delete_role_assignments(cmd, ids=None, assignee=None, role=None, resource_gr
571587

572588

573589
def _search_role_assignments(cli_ctx, assignments_client, definitions_client,
574-
scope, assignee, role, include_inherited, include_groups):
575-
assignee_object_id = None
576-
if assignee:
577-
assignee_object_id = _resolve_object_id(cli_ctx, assignee, fallback_to_object_id=True)
578-
590+
scope, assignee_object_id, role, include_inherited, include_groups):
579591
# https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-list-rest
580592
# "atScope()" and "principalId eq '{value}'" query cannot be used together (API limitation).
581593
# always use "scope" if provided, so we can get assignments beyond subscription e.g. management groups

0 commit comments

Comments
 (0)