diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index f9ac7020dac..93951d73371 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,6 +12,10 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +0.5.137 ++++++++ +* Fix role assignment failure caused by the breaking change of default API version bump of the auth SDK + 0.5.136 +++++++ * fix: remove uneeded location check for DCR, DCRA in azure monitor metrics addon (aks) diff --git a/src/aks-preview/azcli_aks_live_test/configs/ext_matrix_default.json b/src/aks-preview/azcli_aks_live_test/configs/ext_matrix_default.json index 358efc4fc18..b8e5a0aaee5 100644 --- a/src/aks-preview/azcli_aks_live_test/configs/ext_matrix_default.json +++ b/src/aks-preview/azcli_aks_live_test/configs/ext_matrix_default.json @@ -29,11 +29,6 @@ "overlay migration, missing toggle": [ "test_aks_azure_cni_overlay_migration" ], - "pod identity, missing feature registration": [ - "test_aks_create_with_pod_identity_enabled", - "test_aks_create_using_azurecni_with_pod_identity_enabled", - "test_aks_pod_identity_usage" - ], "service mesh, missing feature registration": [ "test_aks_azure_service_mesh_enable_disable", "test_aks_azure_service_mesh_with_ingress_gateway" diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index ca705cb27be..fe011d628b1 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -759,6 +759,7 @@ def load_arguments(self, _): with self.argument_context('aks pod-identity') as c: c.argument('cluster_name', help='The cluster name.') + c.argument('aks_custom_headers', help='Send custom headers. When specified, format should be Key1=Value1,Key2=Value2.') with self.argument_context('aks pod-identity add') as c: c.argument('identity_name', options_list=['--name', '-n'], default=None, required=False, diff --git a/src/aks-preview/azext_aks_preview/_podidentity.py b/src/aks-preview/azext_aks_preview/_podidentity.py index 3eb48e1c99a..ec39ad3f7e0 100644 --- a/src/aks-preview/azext_aks_preview/_podidentity.py +++ b/src/aks-preview/azext_aks_preview/_podidentity.py @@ -106,7 +106,7 @@ def _update_addon_pod_identity( instance.pod_identity_profile.user_assigned_identity_exceptions = pod_identity_exceptions or [] -def _ensure_managed_identity_operator_permission(cli_ctx, instance, scope): +def _ensure_managed_identity_operator_permission(cmd, instance, scope): cluster_identity_object_id = None if instance.identity.type.lower() == 'userassigned': for identity in instance.identity.user_assigned_identities.values(): @@ -120,7 +120,7 @@ def _ensure_managed_identity_operator_permission(cli_ctx, instance, scope): if cluster_identity_object_id is None: raise CLIError('unable to resolve cluster identity') - factory = get_auth_management_client(cli_ctx, scope) + factory = get_auth_management_client(cmd.cli_ctx, scope) assignments_client = factory.role_assignments cluster_identity_object_id = cluster_identity_object_id.lower() scope = scope.lower() @@ -143,7 +143,7 @@ def _ensure_managed_identity_operator_permission(cli_ctx, instance, scope): logger.debug('Managed Identity Opereator role has been assigned to {}'.format(i.scope)) return - if not add_role_assignment(cli_ctx, CONST_MANAGED_IDENTITY_OPERATOR_ROLE, cluster_identity_object_id, + if not add_role_assignment(cmd, CONST_MANAGED_IDENTITY_OPERATOR_ROLE, cluster_identity_object_id, is_service_principal=False, scope=scope): raise CLIError( 'Could not grant Managed Identity Operator permission for cluster') diff --git a/src/aks-preview/azext_aks_preview/_roleassignments.py b/src/aks-preview/azext_aks_preview/_roleassignments.py index 4396f07288a..4edad68824a 100644 --- a/src/aks-preview/azext_aks_preview/_roleassignments.py +++ b/src/aks-preview/azext_aks_preview/_roleassignments.py @@ -1,146 +1,188 @@ -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- - -import time -import uuid - -from azext_aks_preview._client_factory import ( - get_auth_management_client, - get_graph_rbac_management_client, -) -from azure.core.exceptions import HttpResponseError, ResourceExistsError -from azure.graphrbac.models import GetObjectsParameters -from knack.log import get_logger -from knack.util import CLIError -from msrestazure.azure_exceptions import CloudError - -logger = get_logger(__name__) - - -def _get_object_stubs(graph_client, assignees): - params = GetObjectsParameters(include_directory_object_references=True, - object_ids=assignees) - return list(graph_client.objects.get_objects_by_object_ids(params)) - - -def resolve_object_id(cli_ctx, assignee): - client = get_graph_rbac_management_client(cli_ctx) - result = None - if assignee is None: - raise ValueError('Inputted parameter "assignee" is None.') - if assignee.find('@') >= 0: # looks like a user principal name - result = list(client.users.list( - filter="userPrincipalName eq '{}'".format(assignee))) - if not result: - result = list(client.service_principals.list( - filter="servicePrincipalNames/any(c:c eq '{}')".format(assignee))) - if not result: # assume an object id, let us verify it - result = _get_object_stubs(client, [assignee]) - - # 2+ matches should never happen, so we only check 'no match' here - if not result: - raise CLIError( - "No matches in graph database for '{}'".format(assignee)) - - return result[0].object_id - - -def resolve_role_id(role, scope, definitions_client): - role_id = None - try: - uuid.UUID(role) - role_id = role - except ValueError: - pass - if not role_id: # retrieve role id - role_defs = list(definitions_client.list( - scope, "roleName eq '{}'".format(role))) - if len(role_defs) == 0: - raise CLIError("Role '{}' doesn't exist.".format(role)) - if len(role_defs) > 1: - ids = [r.id for r in role_defs] - err = "More than one role matches the given name '{}'. Please pick a value from '{}'" - raise CLIError(err.format(role, ids)) - role_id = role_defs[0].id - return role_id - - -def build_role_scope(resource_group_name: str, scope: str, subscription_id): - subscription_scope = '/subscriptions/' + subscription_id - if scope is not None: - if resource_group_name: - err = 'Resource group "{}" is redundant because scope is supplied' - raise CLIError(err.format(resource_group_name)) - elif resource_group_name: - scope = subscription_scope + '/resourceGroups/' + resource_group_name - else: - scope = subscription_scope - return scope - - -def create_role_assignment(cli_ctx, role, assignee, - is_service_principal=True, resource_group_name=None, scope=None, resolve_assignee=True): - return _create_role_assignment(cli_ctx, - role, assignee, resource_group_name, - scope, resolve_assignee=(is_service_principal and resolve_assignee)) - - -def _create_role_assignment(cli_ctx, role, assignee, - resource_group_name=None, scope=None, resolve_assignee=True): - from azure.cli.core.profiles import ResourceType, get_sdk - factory = get_auth_management_client(cli_ctx, scope) - assignments_client = factory.role_assignments - definitions_client = factory.role_definitions - - if assignments_client.config is None: - raise CLIError("Assignments client config is undefined.") - - scope = build_role_scope( - resource_group_name, scope, assignments_client.config.subscription_id) - - # XXX: if role is uuid, this function's output cannot be used as role assignment defintion id - # ref: https://github.com/Azure/azure-cli/issues/2458 - role_id = resolve_role_id(role, scope, definitions_client) - - # If the cluster has service principal resolve the service principal client id to get the object id, - # if not use MSI object id. - object_id = resolve_object_id( - cli_ctx, assignee) if resolve_assignee else assignee - RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION, - 'RoleAssignmentCreateParameters', mod='models', - operation_group='role_assignments') - parameters = RoleAssignmentCreateParameters( - role_definition_id=role_id, principal_id=object_id) - assignment_name = uuid.uuid4() - custom_headers = None - return assignments_client.create(scope, assignment_name, parameters, custom_headers=custom_headers) - - -def add_role_assignment(cli_ctx, role, service_principal_msi_id, is_service_principal=True, delay=2, scope=None): - # AAD can have delays in propagating data, so sleep and retry - hook = cli_ctx.get_progress_controller(True) - hook.add(message='Waiting for AAD role to propagate', - value=0, total_val=1.0) - logger.info('Waiting for AAD role to propagate') - for x in range(0, 10): - hook.add(message='Waiting for AAD role to propagate', - value=0.1 * x, total_val=1.0) - try: - # TODO: break this out into a shared utility library - create_role_assignment( - cli_ctx, role, service_principal_msi_id, is_service_principal, scope=scope) - break - except (CloudError, HttpResponseError) as ex: - if isinstance(ex, ResourceExistsError) or "The role assignment already exists." in ex.message: - break - logger.info(ex.message) - except Exception as ex: # pylint: disable=broad-except - logger.error(str(ex)) - time.sleep(delay + delay * x) - else: - return False - hook.add(message='AAD role propagation done', value=1.0, total_val=1.0) - logger.info('AAD role propagation done') - return True +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import time +import uuid + +from azure.cli.command_modules.acs._client_factory import ( + get_auth_management_client, +) +from azure.cli.command_modules.acs._graph import resolve_object_id +from azure.cli.command_modules.acs._roleassignments import build_role_scope, resolve_role_id +from azure.cli.core.azclierror import AzCLIError +from azure.cli.core.profiles import ResourceType, get_sdk +from azure.core.exceptions import HttpResponseError, ResourceExistsError +from knack.log import get_logger +from msrestazure.azure_exceptions import CloudError + +logger = get_logger(__name__) + +# pylint: disable=protected-access + + +# temp workaround for the breaking change caused by default API version bump of the auth SDK +def add_role_assignment(cmd, role, service_principal_msi_id, is_service_principal=True, delay=2, scope=None): + from azure.cli.core import __version__ as core_version + if core_version <= "2.45.0": + return _add_role_assignment_old(cmd, role, service_principal_msi_id, is_service_principal, delay, scope) + else: + return _add_role_assignment_new(cmd, role, service_principal_msi_id, is_service_principal, delay, scope) + + +# TODO(fuming): remove and replaced by import from azure.cli.command_modules.acs once dependency bumped to 2.47.0 +def _add_role_assignment_executor_new(cmd, role, assignee, resource_group_name=None, scope=None, resolve_assignee=True): + factory = get_auth_management_client(cmd.cli_ctx, scope) + assignments_client = factory.role_assignments + definitions_client = factory.role_definitions + + # FIXME: is this necessary? + if assignments_client._config is None: + raise AzCLIError("Assignments client config is undefined.") + + scope = build_role_scope(resource_group_name, scope, assignments_client._config.subscription_id) + + # XXX: if role is uuid, this function's output cannot be used as role assignment defintion id + # ref: https://github.com/Azure/azure-cli/issues/2458 + role_id = resolve_role_id(role, scope, definitions_client) + + # If the cluster has service principal resolve the service principal client id to get the object id, + # if not use MSI object id. + object_id = resolve_object_id(cmd.cli_ctx, assignee) if resolve_assignee else assignee + + assignment_name = uuid.uuid4() + custom_headers = None + + RoleAssignmentCreateParameters = get_sdk( + cmd.cli_ctx, + ResourceType.MGMT_AUTHORIZATION, + "RoleAssignmentCreateParameters", + mod="models", + operation_group="role_assignments", + ) + if cmd.supported_api_version(min_api="2018-01-01-preview", resource_type=ResourceType.MGMT_AUTHORIZATION): + parameters = RoleAssignmentCreateParameters(role_definition_id=role_id, principal_id=object_id, + principal_type=None) + return assignments_client.create(scope, assignment_name, parameters, headers=custom_headers) + + # for backward compatibility + RoleAssignmentProperties = get_sdk( + cmd.cli_ctx, + ResourceType.MGMT_AUTHORIZATION, + "RoleAssignmentProperties", + mod="models", + operation_group="role_assignments", + ) + properties = RoleAssignmentProperties(role_definition_id=role_id, principal_id=object_id) + return assignments_client.create(scope, assignment_name, properties, headers=custom_headers) + + +# TODO(fuming): remove and replaced by import from azure.cli.command_modules.acs once dependency bumped to 2.47.0 +def _add_role_assignment_new(cmd, role, service_principal_msi_id, is_service_principal=True, delay=2, scope=None): + # AAD can have delays in propagating data, so sleep and retry + hook = cmd.cli_ctx.get_progress_controller(True) + hook.add(message="Waiting for AAD role to propagate", value=0, total_val=1.0) + logger.info("Waiting for AAD role to propagate") + for x in range(0, 10): + hook.add(message="Waiting for AAD role to propagate", value=0.1 * x, total_val=1.0) + try: + # TODO: break this out into a shared utility library + _add_role_assignment_executor_new( + cmd, + role, + service_principal_msi_id, + scope=scope, + resolve_assignee=is_service_principal, + ) + break + except (CloudError, HttpResponseError) as ex: + if isinstance(ex, ResourceExistsError) or "The role assignment already exists." in ex.message: + break + logger.info(ex.message) + except Exception as ex: # pylint: disable=broad-except + logger.error(str(ex)) + time.sleep(delay + delay * x) + else: + return False + hook.add(message="AAD role propagation done", value=1.0, total_val=1.0) + logger.info("AAD role propagation done") + return True + + +# TODO(fuming): remove this once dependency bumped to 2.47.0 +def _add_role_assignment_executor_old(cmd, role, assignee, resource_group_name=None, scope=None, resolve_assignee=True): + factory = get_auth_management_client(cmd.cli_ctx, scope) + assignments_client = factory.role_assignments + definitions_client = factory.role_definitions + + # FIXME: is this necessary? + if assignments_client.config is None: + raise AzCLIError("Assignments client config is undefined.") + + scope = build_role_scope(resource_group_name, scope, assignments_client.config.subscription_id) + + # XXX: if role is uuid, this function's output cannot be used as role assignment defintion id + # ref: https://github.com/Azure/azure-cli/issues/2458 + role_id = resolve_role_id(role, scope, definitions_client) + + # If the cluster has service principal resolve the service principal client id to get the object id, + # if not use MSI object id. + object_id = resolve_object_id(cmd.cli_ctx, assignee) if resolve_assignee else assignee + + assignment_name = uuid.uuid4() + custom_headers = None + + RoleAssignmentCreateParameters = get_sdk( + cmd.cli_ctx, + ResourceType.MGMT_AUTHORIZATION, + "RoleAssignmentCreateParameters", + mod="models", + operation_group="role_assignments", + ) + if cmd.supported_api_version(min_api="2018-01-01-preview", resource_type=ResourceType.MGMT_AUTHORIZATION): + parameters = RoleAssignmentCreateParameters(role_definition_id=role_id, principal_id=object_id) + return assignments_client.create(scope, assignment_name, parameters, custom_headers=custom_headers) + + # for backward compatibility + RoleAssignmentProperties = get_sdk( + cmd.cli_ctx, + ResourceType.MGMT_AUTHORIZATION, + "RoleAssignmentProperties", + mod="models", + operation_group="role_assignments", + ) + properties = RoleAssignmentProperties(role_definition_id=role_id, principal_id=object_id) + return assignments_client.create(scope, assignment_name, properties, custom_headers=custom_headers) + + +# TODO(fuming): remove this once dependency bumped to 2.47.0 +def _add_role_assignment_old(cmd, role, service_principal_msi_id, is_service_principal=True, delay=2, scope=None): + # AAD can have delays in propagating data, so sleep and retry + hook = cmd.cli_ctx.get_progress_controller(True) + hook.add(message="Waiting for AAD role to propagate", value=0, total_val=1.0) + logger.info("Waiting for AAD role to propagate") + for x in range(0, 10): + hook.add(message="Waiting for AAD role to propagate", value=0.1 * x, total_val=1.0) + try: + # TODO: break this out into a shared utility library + _add_role_assignment_executor_old( + cmd, + role, + service_principal_msi_id, + scope=scope, + resolve_assignee=is_service_principal, + ) + break + except (CloudError, HttpResponseError) as ex: + if ex.message == "The role assignment already exists.": + break + logger.info(ex.message) + except Exception as ex: # pylint: disable=broad-except + logger.error(str(ex)) + time.sleep(delay + delay * x) + else: + return False + hook.add(message="AAD role propagation done", value=1.0, total_val=1.0) + logger.info("AAD role propagation done") + return True diff --git a/src/aks-preview/azext_aks_preview/addonconfiguration.py b/src/aks-preview/azext_aks_preview/addonconfiguration.py index 8da93e424d0..509e478cb07 100644 --- a/src/aks-preview/azext_aks_preview/addonconfiguration.py +++ b/src/aks-preview/azext_aks_preview/addonconfiguration.py @@ -372,7 +372,7 @@ def add_monitoring_role_assignment(result, cluster_resource_id, cmd): is_service_principal = False if service_principal_msi_id is not None: - if not add_role_assignment(cmd.cli_ctx, 'Monitoring Metrics Publisher', + if not add_role_assignment(cmd, 'Monitoring Metrics Publisher', service_principal_msi_id, is_service_principal, scope=cluster_resource_id): logger.warning('Could not create a role assignment for Monitoring addon. ' 'Are you an Owner on this subscription?') @@ -411,14 +411,14 @@ def add_ingress_appgw_addon_role_assignment(result, cmd): parsed_appgw_id = parse_resource_id(appgw_id) appgw_group_id = resource_id(subscription=parsed_appgw_id["subscription"], resource_group=parsed_appgw_id["resource_group"]) - if not add_role_assignment(cmd.cli_ctx, 'Contributor', + if not add_role_assignment(cmd, 'Contributor', service_principal_msi_id, is_service_principal, scope=appgw_group_id): logger.warning('Could not create a role assignment for application gateway: %s ' 'specified in %s addon. ' 'Are you an Owner on this subscription?', appgw_id, CONST_INGRESS_APPGW_ADDON_NAME) if CONST_INGRESS_APPGW_SUBNET_ID in config: subnet_id = config[CONST_INGRESS_APPGW_SUBNET_ID] - if not add_role_assignment(cmd.cli_ctx, 'Network Contributor', + if not add_role_assignment(cmd, 'Network Contributor', service_principal_msi_id, is_service_principal, scope=subnet_id): logger.warning('Could not create a role assignment for subnet: %s ' 'specified in %s addon. ' @@ -432,7 +432,7 @@ def add_ingress_appgw_addon_role_assignment(result, cmd): namespace="Microsoft.Network", type="virtualNetworks", name=parsed_subnet_vnet_id["name"]) - if not add_role_assignment(cmd.cli_ctx, 'Contributor', + if not add_role_assignment(cmd, 'Contributor', service_principal_msi_id, is_service_principal, scope=vnet_id): logger.warning('Could not create a role assignment for virtual network: %s ' 'specified in %s addon. ' @@ -469,7 +469,7 @@ def add_virtual_node_role_assignment(cmd, result, vnet_subnet_id): is_service_principal = False if service_principal_msi_id is not None: - if not add_role_assignment(cmd.cli_ctx, 'Contributor', + if not add_role_assignment(cmd, 'Contributor', service_principal_msi_id, is_service_principal, scope=vnet_id): logger.warning('Could not create a role assignment for virtual node addon. ' 'Are you an Owner on this subscription?') diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index f7221722e62..32a24a6032b 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -19,11 +19,8 @@ from azext_aks_preview._client_factory import ( CUSTOM_MGMT_AKS_PREVIEW, cf_agent_pools, - get_auth_management_client, - get_container_registry_client, get_graph_rbac_management_client, get_msi_client, - get_resource_by_name, ) from azext_aks_preview._consts import ( ADDONS, @@ -63,12 +60,6 @@ _update_addon_pod_identity, ) from azext_aks_preview._resourcegroup import get_rg_location -from azext_aks_preview._roleassignments import ( - add_role_assignment, - build_role_scope, - resolve_object_id, - resolve_role_id, -) from azext_aks_preview.addonconfiguration import ( add_ingress_appgw_addon_role_assignment, add_monitoring_role_assignment, @@ -160,32 +151,6 @@ def _ssl_context(): return ssl.create_default_context() -def _delete_role_assignments(cli_ctx, role, service_principal, delay=2, scope=None): - # AAD can have delays in propagating data, so sleep and retry - hook = cli_ctx.get_progress_controller(True) - hook.add(message='Waiting for AAD role to delete', value=0, total_val=1.0) - logger.info('Waiting for AAD role to delete') - for x in range(0, 10): - hook.add(message='Waiting for AAD role to delete', - value=0.1 * x, total_val=1.0) - try: - delete_role_assignments(cli_ctx, - role=role, - assignee=service_principal, - scope=scope) - break - except CLIError as ex: - raise ex - except CloudError as ex: - logger.info(ex) - time.sleep(delay + delay * x) - else: - return False - hook.add(message='AAD role deletion done', value=1.0, total_val=1.0) - logger.info('AAD role deletion done') - return True - - # pylint: disable=too-many-locals def store_acs_service_principal(subscription_id, client_secret, service_principal, file_name='acsServicePrincipal.json'): @@ -303,117 +268,6 @@ def create_service_principal(cli_ctx, identifier, resolve_app=True, rbac_client= return rbac_client.service_principals.create(ServicePrincipalCreateParameters(app_id=app_id, account_enabled=True)) -def delete_role_assignments(cli_ctx, ids=None, assignee=None, role=None, resource_group_name=None, - scope=None, include_inherited=False, yes=None): - factory = get_auth_management_client(cli_ctx, scope) - assignments_client = factory.role_assignments - definitions_client = factory.role_definitions - ids = ids or [] - if ids: - if assignee or role or resource_group_name or scope or include_inherited: - raise CLIError( - 'When assignment ids are used, other parameter values are not required') - for i in ids: - assignments_client.delete_by_id(i) - return - if not any([ids, assignee, role, resource_group_name, scope, assignee, yes]): - msg = 'This will delete all role assignments under the subscription. Are you sure?' - if not prompt_y_n(msg, default="n"): - return - - scope = build_role_scope(resource_group_name, scope, - assignments_client.config.subscription_id) - assignments = _search_role_assignments(cli_ctx, assignments_client, definitions_client, - scope, assignee, role, include_inherited, - include_groups=False) - - if assignments: - for a in assignments: - assignments_client.delete_by_id(a.id) - - -def _delete_role_assignments(cli_ctx, role, service_principal, delay=2, scope=None): - # AAD can have delays in propagating data, so sleep and retry - hook = cli_ctx.get_progress_controller(True) - hook.add(message='Waiting for AAD role to delete', value=0, total_val=1.0) - logger.info('Waiting for AAD role to delete') - for x in range(0, 10): - hook.add(message='Waiting for AAD role to delete', - value=0.1 * x, total_val=1.0) - try: - delete_role_assignments(cli_ctx, - role=role, - assignee=service_principal, - scope=scope) - break - except CLIError as ex: - raise ex - except CloudError as ex: - logger.info(ex) - time.sleep(delay + delay * x) - else: - return False - hook.add(message='AAD role deletion done', value=1.0, total_val=1.0) - logger.info('AAD role deletion done') - return True - - -def _search_role_assignments(cli_ctx, assignments_client, definitions_client, - scope, assignee, role, include_inherited, include_groups): - assignee_object_id = None - if assignee: - assignee_object_id = resolve_object_id(cli_ctx, assignee) - - # always use "scope" if provided, so we can get assignments beyond subscription e.g. management groups - if scope: - assignments = list(assignments_client.list_for_scope( - scope=scope, filter='atScope()')) - elif assignee_object_id: - if include_groups: - f = "assignedTo('{}')".format(assignee_object_id) - else: - f = "principalId eq '{}'".format(assignee_object_id) - assignments = list(assignments_client.list(filter=f)) - else: - assignments = list(assignments_client.list()) - - if assignments: - assignments = [a for a in assignments if ( - not scope or - include_inherited and re.match(_get_role_property(a, 'scope'), scope, re.I) or - _get_role_property(a, 'scope').lower() == scope.lower() - )] - - if role: - role_id = resolve_role_id(role, scope, definitions_client) - assignments = [i for i in assignments if _get_role_property( - i, 'role_definition_id') == role_id] - - if assignee_object_id: - assignments = [i for i in assignments if _get_role_property( - i, 'principal_id') == assignee_object_id] - - return assignments - - -def _get_role_property(obj, property_name): - if isinstance(obj, dict): - return obj[property_name] - return getattr(obj, property_name) - - -def subnet_role_assignment_exists(cli_ctx, scope): - network_contributor_role_id = "4d97b98b-1d4f-4787-a291-c67834d212e7" - - factory = get_auth_management_client(cli_ctx, scope) - assignments_client = factory.role_assignments - - for i in assignments_client.list_for_scope(scope=scope, filter='atScope()'): - if i.scope == scope and i.role_definition_id.endswith(network_contributor_role_id): - return True - return False - - _re_user_assigned_identity_resource_id = re.compile( r'/subscriptions/(.*?)/resourcegroups/(.*?)/providers/microsoft.managedidentity/userassignedidentities/(.*)', flags=re.IGNORECASE) @@ -1122,64 +976,6 @@ def _upgrade_single_nodepool_image_version(no_wait, client, resource_group_name, return sdk_no_wait(no_wait, client.begin_upgrade_node_image_version, resource_group_name, cluster_name, nodepool_name, headers=headers) -def _ensure_aks_acr(cli_ctx, - client_id, - acr_name_or_id, - subscription_id, # pylint: disable=unused-argument - detach=False): - from msrestazure.tools import is_valid_resource_id, parse_resource_id - - # Check if the ACR exists by resource ID. - if is_valid_resource_id(acr_name_or_id): - try: - parsed_registry = parse_resource_id(acr_name_or_id) - acr_client = get_container_registry_client( - cli_ctx, subscription_id=parsed_registry['subscription']) - registry = acr_client.registries.get( - parsed_registry['resource_group'], parsed_registry['name']) - except CloudError as ex: - raise CLIError(ex.message) - _ensure_aks_acr_role_assignment( - cli_ctx, client_id, registry.id, detach) - return - - # Check if the ACR exists by name accross all resource groups. - registry_name = acr_name_or_id - registry_resource = 'Microsoft.ContainerRegistry/registries' - try: - registry = get_resource_by_name( - cli_ctx, registry_name, registry_resource) - except CloudError as ex: - if 'was not found' in ex.message: - raise CLIError( - "ACR {} not found. Have you provided the right ACR name?".format(registry_name)) - raise CLIError(ex.message) - _ensure_aks_acr_role_assignment(cli_ctx, client_id, registry.id, detach) - return - - -def _ensure_aks_acr_role_assignment(cli_ctx, - client_id, - registry_id, - detach=False): - if detach: - if not _delete_role_assignments(cli_ctx, - 'acrpull', - client_id, - scope=registry_id): - raise CLIError('Could not delete role assignments for ACR. ' - 'Are you an Owner on this subscription?') - return - - if not add_role_assignment(cli_ctx, - 'acrpull', - client_id, - scope=registry_id): - raise CLIError('Could not create a role assignment for ACR. ' - 'Are you an Owner on this subscription?') - return - - def aks_agentpool_show(cmd, # pylint: disable=unused-argument client, resource_group_name, @@ -2106,10 +1902,18 @@ def aks_kanalyze(cmd, client, resource_group_name, name): aks_kanalyze_cmd(cmd, client, resource_group_name, name) -def aks_pod_identity_add(cmd, client, resource_group_name, cluster_name, - identity_name, identity_namespace, identity_resource_id, - binding_selector=None, - no_wait=False): # pylint: disable=unused-argument +def aks_pod_identity_add( + cmd, + client, + resource_group_name, + cluster_name, + identity_name, + identity_namespace, + identity_resource_id, + binding_selector=None, + no_wait=False, + aks_custom_headers=None, +): # pylint: disable=unused-argument ManagedClusterPodIdentity = cmd.get_models( "ManagedClusterPodIdentity", resource_type=CUSTOM_MGMT_AKS_PREVIEW, @@ -2127,7 +1931,7 @@ def aks_pod_identity_add(cmd, client, resource_group_name, cluster_name, user_assigned_identity = _get_user_assigned_identity( cmd.cli_ctx, identity_resource_id) _ensure_managed_identity_operator_permission( - cmd.cli_ctx, instance, user_assigned_identity.id) + cmd, instance, user_assigned_identity.id) pod_identities = [] if instance.pod_identity_profile.user_assigned_identities: @@ -2156,13 +1960,21 @@ def aks_pod_identity_add(cmd, client, resource_group_name, cluster_name, models=pod_identity_models ) + headers = get_aks_custom_headers(aks_custom_headers) # send the managed cluster represeentation to update the pod identity addon - return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance) + return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance, headers=headers) -def aks_pod_identity_delete(cmd, client, resource_group_name, cluster_name, - identity_name, identity_namespace, - no_wait=False): # pylint: disable=unused-argument +def aks_pod_identity_delete( + cmd, + client, + resource_group_name, + cluster_name, + identity_name, + identity_namespace, + no_wait=False, + aks_custom_headers=None, +): # pylint: disable=unused-argument instance = client.get(resource_group_name, cluster_name) _ensure_pod_identity_addon_is_enabled(instance) @@ -2185,8 +1997,9 @@ def aks_pod_identity_delete(cmd, client, resource_group_name, cluster_name, models=pod_identity_models ) + headers = get_aks_custom_headers(aks_custom_headers) # send the managed cluster represeentation to update the pod identity addon - return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance) + return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance, headers=headers) def aks_pod_identity_list(cmd, client, resource_group_name, cluster_name): # pylint: disable=unused-argument @@ -2194,8 +2007,17 @@ def aks_pod_identity_list(cmd, client, resource_group_name, cluster_name): # py return _remove_nulls([instance])[0] -def aks_pod_identity_exception_add(cmd, client, resource_group_name, cluster_name, - exc_name, exc_namespace, pod_labels, no_wait=False): # pylint: disable=unused-argument +def aks_pod_identity_exception_add( + cmd, + client, + resource_group_name, + cluster_name, + exc_name, + exc_namespace, + pod_labels, + no_wait=False, + aks_custom_headers=None, +): # pylint: disable=unused-argument ManagedClusterPodIdentityException = cmd.get_models( "ManagedClusterPodIdentityException", resource_type=CUSTOM_MGMT_AKS_PREVIEW, @@ -2223,12 +2045,21 @@ def aks_pod_identity_exception_add(cmd, client, resource_group_name, cluster_nam models=pod_identity_models ) + headers = get_aks_custom_headers(aks_custom_headers) # send the managed cluster represeentation to update the pod identity addon - return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance) + return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance, headers=headers) -def aks_pod_identity_exception_delete(cmd, client, resource_group_name, cluster_name, - exc_name, exc_namespace, no_wait=False): # pylint: disable=unused-argument +def aks_pod_identity_exception_delete( + cmd, + client, + resource_group_name, + cluster_name, + exc_name, + exc_namespace, + no_wait=False, + aks_custom_headers=None, +): # pylint: disable=unused-argument instance = client.get(resource_group_name, cluster_name) _ensure_pod_identity_addon_is_enabled(instance) @@ -2251,12 +2082,22 @@ def aks_pod_identity_exception_delete(cmd, client, resource_group_name, cluster_ models=pod_identity_models ) + headers = get_aks_custom_headers(aks_custom_headers) # send the managed cluster represeentation to update the pod identity addon - return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance) + return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance, headers=headers) -def aks_pod_identity_exception_update(cmd, client, resource_group_name, cluster_name, - exc_name, exc_namespace, pod_labels, no_wait=False): # pylint: disable=unused-argument +def aks_pod_identity_exception_update( + cmd, + client, + resource_group_name, + cluster_name, + exc_name, + exc_namespace, + pod_labels, + no_wait=False, + aks_custom_headers=None, +): # pylint: disable=unused-argument ManagedClusterPodIdentityException = cmd.get_models( "ManagedClusterPodIdentityException", resource_type=CUSTOM_MGMT_AKS_PREVIEW, @@ -2293,8 +2134,9 @@ def aks_pod_identity_exception_update(cmd, client, resource_group_name, cluster_ models=pod_identity_models ) + headers = get_aks_custom_headers(aks_custom_headers) # send the managed cluster represeentation to update the pod identity addon - return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance) + return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, cluster_name, instance, headers=headers) def aks_pod_identity_exception_list(cmd, client, resource_group_name, cluster_name): diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 6ce5da2d453..3a5f67211a7 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -3406,7 +3406,8 @@ def test_aks_create_with_pod_identity_enabled(self, resource_group, resource_gro create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} ' \ '--enable-managed-identity ' \ '--enable-pod-identity --enable-pod-identity-with-kubenet ' \ - '--ssh-key-value={ssh_key_value}' + '--ssh-key-value={ssh_key_value} ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3414,14 +3415,16 @@ def test_aks_create_with_pod_identity_enabled(self, resource_group, resource_gro ]) # update: disable - cmd = 'aks update --resource-group={resource_group} --name={name} --disable-pod-identity' + cmd = 'aks update --resource-group={resource_group} --name={name} --disable-pod-identity ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', None) ]) # update: enable - cmd = 'aks update --resource-group={resource_group} --name={name} --enable-pod-identity --enable-pod-identity-with-kubenet' + cmd = 'aks update --resource-group={resource_group} --name={name} --enable-pod-identity --enable-pod-identity-with-kubenet ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3430,7 +3433,8 @@ def test_aks_create_with_pod_identity_enabled(self, resource_group, resource_gro # pod identity exception: add cmd = ('aks pod-identity exception add --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name --pod-labels foo=bar') + '--namespace test-namespace --name test-name --pod-labels foo=bar ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3444,7 +3448,8 @@ def test_aks_create_with_pod_identity_enabled(self, resource_group, resource_gro # pod identity exception: update cmd = ('aks pod-identity exception update --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name --pod-labels foo=bar a=b') + '--namespace test-namespace --name test-name --pod-labels foo=bar a=b ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3460,7 +3465,8 @@ def test_aks_create_with_pod_identity_enabled(self, resource_group, resource_gro # pod identity exception: delete cmd = ('aks pod-identity exception delete --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name') + '--namespace test-namespace --name test-name ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3489,21 +3495,24 @@ def test_aks_create_using_azurecni_with_pod_identity_enabled(self, resource_grou create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} ' \ '--enable-managed-identity ' \ '--enable-pod-identity --network-plugin azure ' \ - '--ssh-key-value={ssh_key_value}' + '--ssh-key-value={ssh_key_value} ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True) ]) # update: disable - cmd = 'aks update --resource-group={resource_group} --name={name} --disable-pod-identity' + cmd = 'aks update --resource-group={resource_group} --name={name} --disable-pod-identity ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', None) ]) # update: enable - cmd = 'aks update --resource-group={resource_group} --name={name} --enable-pod-identity' + cmd = 'aks update --resource-group={resource_group} --name={name} --enable-pod-identity ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True) @@ -3511,7 +3520,8 @@ def test_aks_create_using_azurecni_with_pod_identity_enabled(self, resource_grou # pod identity exception: add cmd = ('aks pod-identity exception add --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name --pod-labels foo=bar') + '--namespace test-namespace --name test-name --pod-labels foo=bar ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3525,7 +3535,8 @@ def test_aks_create_using_azurecni_with_pod_identity_enabled(self, resource_grou # pod identity exception: update cmd = ('aks pod-identity exception update --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name --pod-labels foo=bar a=b') + '--namespace test-namespace --name test-name --pod-labels foo=bar a=b ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3541,7 +3552,8 @@ def test_aks_create_using_azurecni_with_pod_identity_enabled(self, resource_grou # pod identity exception: delete cmd = ('aks pod-identity exception delete --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name') + '--namespace test-namespace --name test-name ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3586,7 +3598,8 @@ def test_aks_pod_identity_usage(self, resource_group, resource_group_location): create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} ' \ '--enable-managed-identity ' \ '--enable-pod-identity --enable-pod-identity-with-kubenet ' \ - '--ssh-key-value={ssh_key_value}' + '--ssh-key-value={ssh_key_value} ' \ + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview' self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True) @@ -3594,7 +3607,8 @@ def test_aks_pod_identity_usage(self, resource_group, resource_group_location): # pod identity: add cmd = ('aks pod-identity add --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name --identity-resource-id={application_identity_id}') + '--namespace test-namespace --name test-name --identity-resource-id={application_identity_id} ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3614,7 +3628,8 @@ def test_aks_pod_identity_usage(self, resource_group, resource_group_location): # pod identity: delete cmd = ('aks pod-identity delete --cluster-name={name} --resource-group={resource_group} ' - '--namespace test-namespace --name test-name') + '--namespace test-namespace --name test-name ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), @@ -3624,7 +3639,8 @@ def test_aks_pod_identity_usage(self, resource_group, resource_group_location): # pod identity: add with binding selector cmd = ('aks pod-identity add --cluster-name={name} --resource-group={resource_group} ' '--namespace test-namespace-binding-selector --name test-name-binding-selector ' - '--identity-resource-id={application_identity_id} --binding-selector={binding_selector}') + '--identity-resource-id={application_identity_id} --binding-selector={binding_selector} ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/EnablePodIdentityPreview') self.cmd(cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('podIdentityProfile.enabled', True), diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 05f75973e24..916a7ee01ef 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import setup, find_packages -VERSION = "0.5.136" +VERSION = "0.5.137" CLASSIFIERS = [ "Development Status :: 4 - Beta",