Skip to content

Commit 1ee4df8

Browse files
authored
[AKS] az aks update: Add option --assignee-principal-type to specify the principal type when using --attach-acr (#31464)
1 parent 953bbfa commit 1ee4df8

File tree

7 files changed

+80
-14
lines changed

7 files changed

+80
-14
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -580,8 +580,10 @@ def load_arguments(self, _):
580580
c.argument('gmsa_dns_server')
581581
c.argument('gmsa_root_domain_name')
582582
c.argument('disable_windows_gmsa', action='store_true')
583-
c.argument('attach_acr', acr_arg_type, validator=validate_acr)
584-
c.argument('detach_acr', acr_arg_type, validator=validate_acr)
583+
c.argument('attach_acr', acr_arg_type, validator=validate_acr, help='Attach an ACR to the AKS cluster.')
584+
c.argument('detach_acr', acr_arg_type, validator=validate_acr, help='Detach an ACR from the AKS cluster.')
585+
c.argument('assignee_principal_type', validator=validate_acr, options_list=['--assignee-principal-type', '-t'],
586+
help='Use this parameter in conjunction with --attach-acr to explicitly set the principal type in the ACR role assignment. This helps avoid RBAC-related errors by ensuring the correct principal type is applied. Valid values are \'User\', \'Group\' or \'ServicePrincipal\'')
585587
c.argument('disable_defender', action='store_true', validator=validate_defender_disable_and_enable_parameters)
586588
c.argument('enable_defender', action='store_true')
587589
c.argument('defender_config', validator=validate_defender_config_parameter)
@@ -693,7 +695,6 @@ def load_arguments(self, _):
693695
c.argument('disable_cost_analysis', action='store_true')
694696
c.argument("if_match")
695697
c.argument("if_none_match")
696-
697698
with self.argument_context('aks disable-addons', resource_type=ResourceType.MGMT_CONTAINERSERVICE, operation_group='managed_clusters') as c:
698699
c.argument('addons', options_list=['--addons', '-a'])
699700

src/azure-cli/azure/cli/command_modules/acs/_roleassignments.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def build_role_scope(resource_group_name: str, scope: str, subscription_id: str)
6363
return scope
6464

6565

66-
def add_role_assignment_executor(cmd, role, assignee, resource_group_name=None, scope=None, resolve_assignee=True):
66+
def add_role_assignment_executor(cmd, role, assignee, resource_group_name=None,
67+
scope=None, resolve_assignee=True, assignee_principal_type=None):
6768
factory = get_auth_management_client(cmd.cli_ctx, scope)
6869
assignments_client = factory.role_assignments
6970
definitions_client = factory.role_definitions
@@ -94,7 +95,7 @@ def add_role_assignment_executor(cmd, role, assignee, resource_group_name=None,
9495
)
9596
if cmd.supported_api_version(min_api="2018-01-01-preview", resource_type=ResourceType.MGMT_AUTHORIZATION):
9697
parameters = RoleAssignmentCreateParameters(role_definition_id=role_id, principal_id=object_id,
97-
principal_type=None)
98+
principal_type=assignee_principal_type)
9899
return assignments_client.create(scope, assignment_name, parameters, headers=custom_headers)
99100

100101
# for backward compatibility
@@ -109,7 +110,8 @@ def add_role_assignment_executor(cmd, role, assignee, resource_group_name=None,
109110
return assignments_client.create(scope, assignment_name, properties, headers=custom_headers)
110111

111112

112-
def add_role_assignment(cmd, role, service_principal_msi_id, is_service_principal=True, delay=2, scope=None):
113+
def add_role_assignment(cmd, role, service_principal_msi_id, is_service_principal=True,
114+
delay=2, scope=None, assignee_principal_type=None):
113115
# AAD can have delays in propagating data, so sleep and retry
114116
hook = cmd.cli_ctx.get_progress_controller(True)
115117
hook.add(message="Waiting for AAD role to propagate", value=0, total_val=1.0)
@@ -124,6 +126,7 @@ def add_role_assignment(cmd, role, service_principal_msi_id, is_service_principa
124126
service_principal_msi_id,
125127
scope=scope,
126128
resolve_assignee=is_service_principal,
129+
assignee_principal_type=assignee_principal_type,
127130
)
128131
break
129132
except HttpResponseError as ex:
@@ -305,21 +308,26 @@ def ensure_cluster_identity_permission_on_kubelet_identity(cmd, cluster_identity
305308
)
306309

307310

308-
def ensure_aks_acr_role_assignment(cmd, assignee, registry_id, detach=False, is_service_principal=True):
311+
def ensure_aks_acr_role_assignment(cmd, assignee, registry_id, detach=False, is_service_principal=True,
312+
assignee_principal_type=None):
313+
309314
if detach:
310315
if not delete_role_assignments(
311316
cmd.cli_ctx, "acrpull", assignee, scope=registry_id, is_service_principal=is_service_principal
312317
):
313318
raise AzCLIError("Could not delete role assignments for ACR. Are you an Owner on this subscription?")
314319
return
315320

316-
if not add_role_assignment(cmd, "acrpull", assignee, scope=registry_id, is_service_principal=is_service_principal):
321+
if not add_role_assignment(cmd, "acrpull", assignee, scope=registry_id, is_service_principal=is_service_principal,
322+
assignee_principal_type=assignee_principal_type):
317323
raise AzCLIError("Could not create a role assignment for ACR. Are you an Owner on this subscription?")
318324
return
319325

320326

321327
# pylint: disable=unused-argument
322-
def ensure_aks_acr(cmd, assignee, acr_name_or_id, subscription_id, detach=False, is_service_principal=True):
328+
def ensure_aks_acr(cmd, assignee, acr_name_or_id, subscription_id, detach=False,
329+
is_service_principal=True, assignee_principal_type=None):
330+
323331
from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id
324332

325333
# Check if the ACR exists by resource ID.
@@ -330,7 +338,8 @@ def ensure_aks_acr(cmd, assignee, acr_name_or_id, subscription_id, detach=False,
330338
registry = acr_client.registries.get(parsed_registry["resource_group"], parsed_registry["name"])
331339
except HttpResponseError as ex:
332340
raise AzCLIError(ex.message)
333-
ensure_aks_acr_role_assignment(cmd, assignee, registry.id, detach, is_service_principal)
341+
ensure_aks_acr_role_assignment(cmd, assignee, registry.id, detach,
342+
is_service_principal, assignee_principal_type)
334343
return
335344

336345
# Check if the ACR exists by name accross all resource groups.
@@ -342,5 +351,5 @@ def ensure_aks_acr(cmd, assignee, acr_name_or_id, subscription_id, detach=False,
342351
if "was not found" in ex.message:
343352
raise AzCLIError("ACR {} not found. Have you provided the right ACR name?".format(registry_name))
344353
raise AzCLIError(ex.message)
345-
ensure_aks_acr_role_assignment(cmd, assignee, registry.id, detach, is_service_principal)
354+
ensure_aks_acr_role_assignment(cmd, assignee, registry.id, detach, is_service_principal, assignee_principal_type)
346355
return

src/azure-cli/azure/cli/command_modules/acs/_validators.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ def validate_spot_max_price(namespace):
341341
def validate_acr(namespace):
342342
if namespace.attach_acr and namespace.detach_acr:
343343
raise CLIError('Cannot specify "--attach-acr" and "--detach-acr" at the same time.')
344+
if namespace.assignee_principal_type and not namespace.attach_acr:
345+
raise CLIError('The "--assignee-principal-type" argument can only be used with "--attach-acr".')
346+
if namespace.attach_acr:
347+
# Validate assignee_principal_type if specified
348+
if namespace.assignee_principal_type:
349+
valid_types = ['User', 'ServicePrincipal', 'Group']
350+
if namespace.assignee_principal_type not in valid_types:
351+
raise CLIError(
352+
f"Invalid value for --assignee_principal_type. "
353+
f"Allowed values are: {', '.join(valid_types)}"
354+
)
344355

345356

346357
def validate_nodepool_tags(ns):

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ def aks_update(
756756
disable_windows_gmsa=False,
757757
attach_acr=None,
758758
detach_acr=None,
759+
assignee_principal_type=None,
759760
nrg_lockdown_restriction_level=None,
760761
enable_defender=False,
761762
disable_defender=False,

src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,21 @@ def get_detach_acr(self) -> Union[str, None]:
18721872
# this parameter does not need validation
18731873
return detach_acr
18741874

1875+
def get_assignee_principal_type(self) -> Union[str, None]:
1876+
"""Obtain the value of assignee_principal_type.
1877+
1878+
This function will return the override value of the assignee principal type (e.g., User, ServicePrincipal, Group)
1879+
to determine the type of identity being assigned for the ACR role. This is used
1880+
during the attach ACR operation to ensure proper role assignment based on the identity type.
1881+
1882+
:return: string or None
1883+
"""
1884+
# read the original value passed by the command
1885+
assignee_principal_type = self.raw_param.get("assignee_principal_type")
1886+
# this parameter does not need dynamic completion
1887+
# this parameter does not need validation
1888+
return assignee_principal_type
1889+
18751890
def get_http_proxy_config(self) -> Union[Dict, ManagedClusterHTTPProxyConfig, None]:
18761891
"""Obtain the value of http_proxy_config.
18771892
@@ -7006,6 +7021,7 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
70067021
acr_name_or_id=attach_acr,
70077022
subscription_id=self.context.get_subscription_id(),
70087023
is_service_principal=False,
7024+
assignee_principal_type=self.context.get_assignee_principal_type()
70097025
)
70107026

70117027
# azure monitor metrics addon (v2)
@@ -7374,14 +7390,15 @@ def process_attach_detach_acr(self, mc: ManagedCluster) -> None:
73747390
assignee, is_service_principal = self.context.get_assignee_from_identity_or_sp_profile()
73757391
attach_acr = self.context.get_attach_acr()
73767392
detach_acr = self.context.get_detach_acr()
7377-
7393+
assignee_principal_type = self.context.get_assignee_principal_type()
73787394
if attach_acr:
73797395
self.context.external_functions.ensure_aks_acr(
73807396
self.cmd,
73817397
assignee=assignee,
73827398
acr_name_or_id=attach_acr,
73837399
subscription_id=subscription_id,
73847400
is_service_principal=is_service_principal,
7401+
assignee_principal_type=assignee_principal_type,
73857402
)
73867403

73877404
if detach_acr:
@@ -7392,6 +7409,7 @@ def process_attach_detach_acr(self, mc: ManagedCluster) -> None:
73927409
subscription_id=subscription_id,
73937410
detach=True,
73947411
is_service_principal=is_service_principal,
7412+
assignee_principal_type=assignee_principal_type,
73957413
)
73967414

73977415
def update_azure_service_mesh_profile(self, mc: ManagedCluster) -> ManagedCluster:
@@ -8925,6 +8943,7 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None:
89258943
acr_name_or_id=attach_acr,
89268944
subscription_id=self.context.get_subscription_id(),
89278945
is_service_principal=False,
8946+
assignee_principal_type=self.context.get_assignee_principal_type()
89288947
)
89298948

89308949
enable_azure_container_storage = self.context.get_intermediate("enable_azure_container_storage")

src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7291,7 +7291,7 @@ def test_aks_update_attatch_acr(self, resource_group, resource_group_location, s
72917291
assignee_object_id = _get_test_sp_object_id(sp_name)
72927292
role_assignment_check_cmd = (
72937293
"role assignment list --scope {acr_scope} --assignee " +
7294-
assignee_object_id if assignee_object_id else sp_name
7294+
assignee_object_id if assignee_object_id else "role assignment list --scope {acr_scope} --assignee " + sp_name
72957295
)
72967296

72977297
# attach acr
@@ -7308,6 +7308,27 @@ def test_aks_update_attatch_acr(self, resource_group, resource_group_location, s
73087308
# check role assignment
73097309
self.cmd(role_assignment_check_cmd, checks=[self.is_empty()])
73107310

7311+
# Test with --assignee-principal-type
7312+
attach_with_principal_cmd = "aks update --resource-group={resource_group} --name={name} --attach-acr={acr_name} --assignee-principal-type=ServicePrincipal"
7313+
self.cmd(attach_with_principal_cmd)
7314+
7315+
# check role assignment with principal type
7316+
role_assignment_with_principal_check_cmd = (
7317+
"role assignment list --scope {acr_scope} --assignee " +
7318+
assignee_object_id if assignee_object_id else "role assignment list --scope {acr_scope} --assignee " + sp_name
7319+
)
7320+
self.cmd(role_assignment_with_principal_check_cmd, checks=[
7321+
self.check('length(@) == `1`', True),
7322+
self.check('[0].principalType', 'ServicePrincipal')
7323+
])
7324+
7325+
# detach acr
7326+
attach_cmd = 'aks update --resource-group={resource_group} --name={name} --detach-acr={acr_name}'
7327+
self.cmd(attach_cmd)
7328+
7329+
# check role assignment
7330+
self.cmd(role_assignment_check_cmd, checks=[self.is_empty()])
7331+
73117332
# delete
73127333
self.cmd(
73137334
'aks delete -g {resource_group} -n {name} --yes --no-wait', checks=[self.is_empty()])

src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6578,7 +6578,7 @@ def test_process_attach_acr(self):
65786578
return_value=registry,
65796579
), patch("azure.cli.command_modules.acs._roleassignments.ensure_aks_acr_role_assignment") as ensure_assignment:
65806580
dec_3.process_attach_acr(mc_3)
6581-
ensure_assignment.assert_called_once_with(self.cmd, "test_service_principal", "test_registry_id", False, True)
6581+
ensure_assignment.assert_called_once_with(self.cmd, "test_service_principal", "test_registry_id", False, True, None)
65826582

65836583
def test_set_up_network_profile(self):
65846584
# default value in `aks_create`
@@ -8222,6 +8222,7 @@ def test_postprocessing_after_mc_created(self):
82228222
acr_name_or_id="test_attach_acr",
82238223
subscription_id="1234-5678-9012",
82248224
is_service_principal=False,
8225+
assignee_principal_type=None,
82258226
)
82268227

82278228
def test_put_mc(self):
@@ -8946,6 +8947,7 @@ def test_process_attach_detach_acr(self):
89468947
acr_name_or_id="test_attach_acr",
89478948
subscription_id="test_subscription_id",
89488949
is_service_principal=False,
8950+
assignee_principal_type=None,
89498951
),
89508952
call(
89518953
self.cmd,
@@ -8954,6 +8956,7 @@ def test_process_attach_detach_acr(self):
89548956
subscription_id="test_subscription_id",
89558957
detach=True,
89568958
is_service_principal=False,
8959+
assignee_principal_type=None,
89578960
),
89588961
]
89598962
)
@@ -11929,6 +11932,7 @@ def test_postprocessing_after_mc_created(self):
1192911932
acr_name_or_id="test_attach_acr",
1193011933
subscription_id="1234-5678-9012",
1193111934
is_service_principal=False,
11935+
assignee_principal_type=None,
1193211936
)
1193311937

1193411938
def test_put_mc(self):

0 commit comments

Comments
 (0)