Skip to content

Commit 7627ed5

Browse files
authored
[AKS] Support updating from SPN cluster to MSI cluster (Azure#17902)
1 parent 716b10f commit 7627ed5

File tree

7 files changed

+4663
-57
lines changed

7 files changed

+4663
-57
lines changed

linter_exclusions.yml

+3
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ aks update:
300300
load_balancer_outbound_ports:
301301
rule_exclusions:
302302
- option_length_too_long
303+
enable_managed_identity:
304+
rule_exclusions:
305+
- option_length_too_long
303306
aks update-credentials:
304307
parameters:
305308
aad_server_app_secret:

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

+10
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,12 @@
559559
* Has a special character (Regex match [\\W_])
560560
- Disallowed values: "abc@123", "P@$$w0rd", "P@ssw0rd", "P@ssword123", "Pa$$word", "pass@word1", "Password!", "Password1", "Password22", "iloveyou!"
561561
Reference: https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.management.compute.models.virtualmachinescalesetosprofile.adminpassword?view=azure-dotnet
562+
- name: --enable-managed-identity
563+
type: bool
564+
short-summary: Update current cluster to use managed identity to manage cluster resource group.
565+
- name: --assign-identity
566+
type: string
567+
short-summary: Specify an existing user assigned identity to manage cluster resource group.
562568
examples:
563569
- name: Update a kubernetes cluster with standard SKU load balancer to use two AKS created IPs for the load balancer outbound connection usage.
564570
text: az aks update -g MyResourceGroup -n MyManagedCluster --load-balancer-managed-outbound-ip-count 2
@@ -586,6 +592,10 @@
586592
text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-ahub
587593
- name: Update Windows password of a kubernetes cluster
588594
text: az aks update -g MyResourceGroup -n MyManagedCLuster --windows-admin-password "Repl@cePassw0rd12345678"
595+
- name: Update the cluster to use system assigned managed identity in control plane.
596+
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-managed-identity
597+
- name: Update the cluster to use user assigned managed identity in control plane.
598+
text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-managed-identity --assign-identity <user_assigned_identity_resource_id>
589599
"""
590600

591601
helps['aks delete'] = """

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

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ def load_arguments(self, _):
250250
c.argument('enable_ahub', options_list=['--enable-ahub'])
251251
c.argument('disable_ahub', options_list=['--disable-ahub'])
252252
c.argument('windows_admin_password', options_list=['--windows-admin-password'])
253+
c.argument('enable_managed_identity', action='store_true')
254+
c.argument('assign_identity', type=str, validator=validate_assign_identity)
255+
c.argument('yes', options_list=['--yes', '-y'], help='Do not prompt for confirmation.', action='store_true')
253256

254257
with self.argument_context('aks disable-addons') as c:
255258
c.argument('addons', options_list=['--addons', '-a'])

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

+172-57
Original file line numberDiff line numberDiff line change
@@ -2219,60 +2219,23 @@ def aks_create(cmd, client, resource_group_name, name, ssh_key_value, # pylint:
22192219
retry_exception = Exception(None)
22202220
for _ in range(0, max_retry):
22212221
try:
2222-
need_pull_for_result = (monitoring or
2223-
(enable_managed_identity and attach_acr) or
2224-
ingress_appgw_addon_enabled or
2225-
enable_virtual_node or
2226-
need_post_creation_vnet_permission_granting)
2227-
if need_pull_for_result:
2228-
# adding a wait here since we rely on the result for role assignment
2229-
result = LongRunningOperation(cmd.cli_ctx)(client.create_or_update(
2230-
resource_group_name=resource_group_name,
2231-
resource_name=name,
2232-
parameters=mc))
2233-
else:
2234-
result = sdk_no_wait(no_wait,
2235-
client.create_or_update,
2236-
resource_group_name=resource_group_name,
2237-
resource_name=name,
2238-
parameters=mc,
2239-
custom_headers=custom_headers)
2240-
if monitoring:
2241-
cloud_name = cmd.cli_ctx.cloud.name
2242-
# add cluster spn/msi Monitoring Metrics Publisher role assignment to publish metrics to MDM
2243-
# mdm metrics is supported only in azure public cloud, so add the role assignment only in this cloud
2244-
if cloud_name.lower() == 'azurecloud':
2245-
from msrestazure.tools import resource_id
2246-
cluster_resource_id = resource_id(
2247-
subscription=subscription_id,
2248-
resource_group=resource_group_name,
2249-
namespace='Microsoft.ContainerService', type='managedClusters',
2250-
name=name
2251-
)
2252-
_add_monitoring_role_assignment(result, cluster_resource_id, cmd)
2253-
if enable_managed_identity and attach_acr:
2254-
if result.identity_profile is None or result.identity_profile["kubeletidentity"] is None:
2255-
logger.warning('Your cluster is successfully created, but we failed to attach acr to it, '
2256-
'you can manually grant permission to the identity named <ClUSTER_NAME>-agentpool '
2257-
'in MC_ resource group to give it permission to pull from ACR.')
2258-
else:
2259-
kubelet_identity_client_id = result.identity_profile["kubeletidentity"].client_id
2260-
_ensure_aks_acr(cmd.cli_ctx,
2261-
client_id=kubelet_identity_client_id,
2262-
acr_name_or_id=attach_acr,
2263-
subscription_id=subscription_id)
2264-
if ingress_appgw_addon_enabled:
2265-
_add_ingress_appgw_addon_role_assignment(result, cmd)
2266-
if enable_virtual_node:
2267-
_add_virtual_node_role_assignment(cmd, result, vnet_subnet_id)
2268-
if need_post_creation_vnet_permission_granting:
2269-
if not _create_role_assignment(cmd.cli_ctx, 'Network Contributor',
2270-
result.identity.principal_id, scope=vnet_subnet_id,
2271-
resolve_assignee=False):
2272-
logger.warning('Could not create a role assignment for subnet. '
2273-
'Are you an Owner on this subscription?')
2274-
2275-
return result
2222+
created_cluster = _put_managed_cluster_ensuring_permission(
2223+
cmd,
2224+
client,
2225+
subscription_id,
2226+
resource_group_name,
2227+
name,
2228+
mc,
2229+
monitoring,
2230+
ingress_appgw_addon_enabled,
2231+
enable_virtual_node,
2232+
need_post_creation_vnet_permission_granting,
2233+
vnet_subnet_id,
2234+
enable_managed_identity,
2235+
attach_acr,
2236+
custom_headers,
2237+
no_wait)
2238+
return created_cluster
22762239
except CloudError as ex:
22772240
retry_exception = ex
22782241
if 'not found in Active Directory tenant' in ex.message:
@@ -2486,6 +2449,9 @@ def aks_update(cmd, client, resource_group_name, name,
24862449
enable_ahub=False,
24872450
disable_ahub=False,
24882451
windows_admin_password=None,
2452+
enable_managed_identity=False,
2453+
assign_identity=None,
2454+
yes=False,
24892455
no_wait=False):
24902456
update_autoscaler = enable_cluster_autoscaler + disable_cluster_autoscaler + update_cluster_autoscaler
24912457
update_lb_profile = is_load_balancer_profile_provided(load_balancer_managed_outbound_ip_count,
@@ -2506,7 +2472,9 @@ def aks_update(cmd, client, resource_group_name, name,
25062472
not update_aad_profile and
25072473
not enable_ahub and
25082474
not disable_ahub and
2509-
not windows_admin_password):
2475+
not windows_admin_password and
2476+
not enable_managed_identity and
2477+
not assign_identity):
25102478
raise CLIError('Please specify one or more of "--enable-cluster-autoscaler" or '
25112479
'"--disable-cluster-autoscaler" or '
25122480
'"--update-cluster-autoscaler" or '
@@ -2525,7 +2493,12 @@ def aks_update(cmd, client, resource_group_name, name,
25252493
'"--aad-admin-group-object-ids" or '
25262494
'"--enable-ahub" or '
25272495
'"--disable-ahub" or '
2528-
'"--windows-admin-password"')
2496+
'"--windows-admin-password" or '
2497+
'"--enable-managed-identity" or '
2498+
'"--assign-identity"')
2499+
2500+
if not enable_managed_identity and assign_identity:
2501+
raise CLIError('--assign-identity can only be specified when --enable-managed-identity is specified')
25292502

25302503
instance = client.get(resource_group_name, name)
25312504
# For multi-agent pool, use the az aks nodepool command
@@ -2655,7 +2628,72 @@ def aks_update(cmd, client, resource_group_name, name,
26552628
if windows_admin_password:
26562629
instance.windows_profile.admin_password = windows_admin_password
26572630

2658-
return sdk_no_wait(no_wait, client.create_or_update, resource_group_name, name, instance)
2631+
current_identity_type = "spn"
2632+
if instance.identity is not None:
2633+
current_identity_type = instance.identity.type.casefold()
2634+
2635+
goal_identity_type = current_identity_type
2636+
if enable_managed_identity:
2637+
if not assign_identity:
2638+
goal_identity_type = "systemassigned"
2639+
else:
2640+
goal_identity_type = "userassigned"
2641+
2642+
if current_identity_type != goal_identity_type:
2643+
msg = ""
2644+
if current_identity_type == "spn":
2645+
msg = ('Your cluster is using service principal, and you are going to update '
2646+
'the cluster to use {} managed identity.\n After updating, your '
2647+
'cluster\'s control plane and addon pods will switch to use managed '
2648+
'identity, but kubelet will KEEP USING SERVICE PRINCIPAL '
2649+
'until you upgrade your agentpool.\n '
2650+
'Are you sure you want to perform this operation?').format(goal_identity_type)
2651+
else:
2652+
msg = ('Your cluster is already using {} managed identity, and you are going to '
2653+
'update the cluster to use {} managed identity. \nAre you sure you want to '
2654+
'perform this operation?').format(current_identity_type, goal_identity_type)
2655+
if not yes and not prompt_y_n(msg, default="n"):
2656+
return None
2657+
if goal_identity_type == "systemassigned":
2658+
instance.identity = ManagedClusterIdentity(
2659+
type="SystemAssigned"
2660+
)
2661+
elif goal_identity_type == "userassigned":
2662+
user_assigned_identity = {
2663+
assign_identity: ManagedClusterIdentityUserAssignedIdentitiesValue()
2664+
}
2665+
instance.identity = ManagedClusterIdentity(
2666+
type="UserAssigned",
2667+
user_assigned_identities=user_assigned_identity
2668+
)
2669+
2670+
monitoring_addon_enabled = False
2671+
ingress_appgw_addon_enabled = False
2672+
virtual_node_addon_enabled = False
2673+
if instance.addon_profiles is not None:
2674+
monitoring_addon_enabled = CONST_MONITORING_ADDON_NAME in instance.addon_profiles and \
2675+
instance.addon_profiles[CONST_MONITORING_ADDON_NAME].enabled
2676+
ingress_appgw_addon_enabled = CONST_INGRESS_APPGW_ADDON_NAME in instance.addon_profiles and \
2677+
instance.addon_profiles[CONST_INGRESS_APPGW_ADDON_NAME].enabled
2678+
virtual_node_addon_enabled = CONST_VIRTUAL_NODE_ADDON_NAME + 'Linux' in instance.addon_profiles and \
2679+
instance.addon_profiles[CONST_VIRTUAL_NODE_ADDON_NAME + 'Linux'].enabled
2680+
2681+
return _put_managed_cluster_ensuring_permission(
2682+
cmd,
2683+
client,
2684+
subscription_id,
2685+
resource_group_name,
2686+
name,
2687+
instance,
2688+
monitoring_addon_enabled,
2689+
ingress_appgw_addon_enabled,
2690+
virtual_node_addon_enabled,
2691+
False,
2692+
instance.agent_pool_profiles[0].vnet_subnet_id,
2693+
_is_msi_cluster(instance),
2694+
attach_acr,
2695+
None,
2696+
no_wait)
26592697

26602698

26612699
# pylint: disable=unused-argument,inconsistent-return-statements,too-many-return-statements
@@ -4113,3 +4151,80 @@ def _is_msi_cluster(managed_cluster):
41134151
return (managed_cluster and managed_cluster.identity and
41144152
(managed_cluster.identity.type.casefold() == "systemassigned" or
41154153
managed_cluster.identity.type.casefold() == "userassigned"))
4154+
4155+
4156+
def _put_managed_cluster_ensuring_permission(
4157+
cmd, # pylint: disable=too-many-locals,too-many-statements,too-many-branches
4158+
client,
4159+
subscription_id,
4160+
resource_group_name,
4161+
name,
4162+
managed_cluster,
4163+
monitoring_addon_enabled,
4164+
ingress_appgw_addon_enabled,
4165+
virtual_node_addon_enabled,
4166+
need_grant_vnet_permission_to_cluster_identity,
4167+
vnet_subnet_id,
4168+
enable_managed_identity,
4169+
attach_acr,
4170+
headers,
4171+
no_wait
4172+
):
4173+
# some addons require post cluster creation role assigment
4174+
need_post_creation_role_assignment = (monitoring_addon_enabled or
4175+
ingress_appgw_addon_enabled or
4176+
(enable_managed_identity and attach_acr) or
4177+
virtual_node_addon_enabled or
4178+
need_grant_vnet_permission_to_cluster_identity)
4179+
if need_post_creation_role_assignment:
4180+
# adding a wait here since we rely on the result for role assignment
4181+
cluster = LongRunningOperation(cmd.cli_ctx)(client.create_or_update(
4182+
resource_group_name=resource_group_name,
4183+
resource_name=name,
4184+
parameters=managed_cluster,
4185+
custom_headers=headers))
4186+
cloud_name = cmd.cli_ctx.cloud.name
4187+
# add cluster spn/msi Monitoring Metrics Publisher role assignment to publish metrics to MDM
4188+
# mdm metrics is supported only in azure public cloud, so add the role assignment only in this cloud
4189+
if monitoring_addon_enabled and cloud_name.lower() == 'azurecloud':
4190+
from msrestazure.tools import resource_id
4191+
cluster_resource_id = resource_id(
4192+
subscription=subscription_id,
4193+
resource_group=resource_group_name,
4194+
namespace='Microsoft.ContainerService', type='managedClusters',
4195+
name=name
4196+
)
4197+
_add_monitoring_role_assignment(cluster, cluster_resource_id, cmd)
4198+
if ingress_appgw_addon_enabled:
4199+
_add_ingress_appgw_addon_role_assignment(cluster, cmd)
4200+
if virtual_node_addon_enabled:
4201+
_add_virtual_node_role_assignment(cmd, cluster, vnet_subnet_id)
4202+
if need_grant_vnet_permission_to_cluster_identity:
4203+
if not _create_role_assignment(cmd.cli_ctx, 'Network Contributor',
4204+
cluster.identity.principal_id, scope=vnet_subnet_id,
4205+
resolve_assignee=False):
4206+
logger.warning('Could not create a role assignment for subnet. '
4207+
'Are you an Owner on this subscription?')
4208+
4209+
if enable_managed_identity and attach_acr:
4210+
# Attach ACR to cluster enabled managed identity
4211+
if cluster.identity_profile is None or \
4212+
cluster.identity_profile["kubeletidentity"] is None:
4213+
logger.warning('Your cluster is successfully created, but we failed to attach '
4214+
'acr to it, you can manually grant permission to the identity '
4215+
'named <ClUSTER_NAME>-agentpool in MC_ resource group to give '
4216+
'it permission to pull from ACR.')
4217+
else:
4218+
kubelet_identity_client_id = cluster.identity_profile["kubeletidentity"].client_id
4219+
_ensure_aks_acr(cmd.cli_ctx,
4220+
client_id=kubelet_identity_client_id,
4221+
acr_name_or_id=attach_acr,
4222+
subscription_id=subscription_id)
4223+
else:
4224+
cluster = sdk_no_wait(no_wait, client.create_or_update,
4225+
resource_group_name=resource_group_name,
4226+
resource_name=name,
4227+
parameters=managed_cluster,
4228+
custom_headers=headers)
4229+
4230+
return cluster

0 commit comments

Comments
 (0)