From 72e5c3ab697274b59919c490807e2c4875d2aa30 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Wed, 27 Aug 2025 16:25:46 -0400 Subject: [PATCH 1/8] update error message --- src/fleet/azext_fleet/_helpers.py | 4 ++-- src/fleet/azext_fleet/_params.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fleet/azext_fleet/_helpers.py b/src/fleet/azext_fleet/_helpers.py index 77d5b9ddee2..c2b256a5794 100644 --- a/src/fleet/azext_fleet/_helpers.py +++ b/src/fleet/azext_fleet/_helpers.py @@ -164,5 +164,5 @@ def assign_network_contributor_role_to_subnet(cmd, subnet_id): raise CLIError("The Microsoft.ContainerService resource provider is not registered." "Run `az provider register -n Microsoft.ContainerService --wait`.") if not add_role_assignment(cmd, 'Network Contributor', FLEET_1P_APP_ID, scope=subnet_id): - raise CLIError("failed to create role assignment for Fleet RP.\n" - f"Do you have owner permissions on the subnet {subnet_id}?\n") + raise CLIError("Failed to create Network Contributor role assignment for Fleet RP on the subnet.\n" + f"Please ensure you have sufficient permissions to assign roles on subnet {subnet_id}.") \ No newline at end of file diff --git a/src/fleet/azext_fleet/_params.py b/src/fleet/azext_fleet/_params.py index 1e2eab45846..3ff13af947a 100644 --- a/src/fleet/azext_fleet/_params.py +++ b/src/fleet/azext_fleet/_params.py @@ -43,8 +43,8 @@ def load_arguments(self, _): c.argument('tags', tags_type) c.argument('dns_name_prefix', options_list=['--dns-name-prefix', '-p'], help='Prefix for host names that are created. If not specified, generate a host name using the managed cluster and resource group names.') c.argument('enable_private_cluster', action='store_true', help='Whether to create the Fleet hub as a private cluster or not.') - c.argument('enable_vnet_integration', action='store_true', is_preview=True, help='Whether to enable apiserver vnet integration for the Fleet hub or not.') - c.argument('apiserver_subnet_id', validator=validate_apiserver_subnet_id, is_preview=True, help='The subnet to be used when apiserver vnet integration is enabled.') + c.argument('enable_vnet_integration', action='store_true', help='Whether to enable apiserver vnet integration for the Fleet hub or not.') + c.argument('apiserver_subnet_id', validator=validate_apiserver_subnet_id, help='The subnet to be used when apiserver vnet integration is enabled.') c.argument('agent_subnet_id', validator=validate_agent_subnet_id, help='The ID of the subnet which the Fleet hub node will join on startup.') c.argument('enable_managed_identity', action='store_true', help='Enable system assigned managed identity (MSI) on the Fleet resource.') c.argument('assign_identity', validator=validate_assign_identity, help='With --enable-managed-identity, enable user assigned managed identity (MSI) on the Fleet resource by specifying the user assigned identity\'s resource Id.') From 8eb2b2c00fe19671bcf01d5254b9168f33c093a0 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Wed, 10 Sep 2025 22:23:02 -0400 Subject: [PATCH 2/8] grant vnet perm --- src/fleet/azext_fleet/_client_factory.py | 5 ++++ src/fleet/azext_fleet/_helpers.py | 29 ++++++++++++++++++++---- src/fleet/azext_fleet/custom.py | 8 ++++++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/fleet/azext_fleet/_client_factory.py b/src/fleet/azext_fleet/_client_factory.py index 96ea797fe7a..b371ef68952 100644 --- a/src/fleet/azext_fleet/_client_factory.py +++ b/src/fleet/azext_fleet/_client_factory.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- from azure.cli.core.commands.client_factory import get_mgmt_service_client +from azure.mgmt.msi import ManagedServiceIdentityClient from azure.cli.core.profiles import ( CustomResourceType, ResourceType @@ -53,3 +54,7 @@ def cf_auto_upgrade_profile_operations(cli_ctx, *_): def get_provider_client(cli_ctx): return get_mgmt_service_client( cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + + +def get_msi_client(cli_ctx, subscription_id=None): + return get_mgmt_service_client(cli_ctx, ManagedServiceIdentityClient, subscription_id=subscription_id) diff --git a/src/fleet/azext_fleet/_helpers.py b/src/fleet/azext_fleet/_helpers.py index c2b256a5794..008f0477747 100644 --- a/src/fleet/azext_fleet/_helpers.py +++ b/src/fleet/azext_fleet/_helpers.py @@ -14,9 +14,11 @@ from knack.prompting import NoTTYException, prompt_y_n from knack.util import CLIError from azure.cli.command_modules.acs._roleassignments import add_role_assignment +from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id + -from azext_fleet.constants import FLEET_1P_APP_ID from azext_fleet._client_factory import get_provider_client +from azext_fleet._client_factory import get_msi_client logger = get_logger(__name__) @@ -154,7 +156,7 @@ def _load_kubernetes_configuration(filename): raise CLIError(f'Error parsing {filename} ({str(ex)})') from ex -def assign_network_contributor_role_to_subnet(cmd, subnet_id): +def assign_network_contributor_role_to_subnet(cmd, objectId, subnet_id): resource_client = get_provider_client(cmd.cli_ctx) provider = resource_client.providers.get("Microsoft.ContainerService") @@ -163,6 +165,23 @@ def assign_network_contributor_role_to_subnet(cmd, subnet_id): if provider.registration_state != 'Registered': raise CLIError("The Microsoft.ContainerService resource provider is not registered." "Run `az provider register -n Microsoft.ContainerService --wait`.") - if not add_role_assignment(cmd, 'Network Contributor', FLEET_1P_APP_ID, scope=subnet_id): - raise CLIError("Failed to create Network Contributor role assignment for Fleet RP on the subnet.\n" - f"Please ensure you have sufficient permissions to assign roles on subnet {subnet_id}.") \ No newline at end of file + + if not add_role_assignment(cmd, 'Network Contributor', objectId, scope=subnet_id): + logger.warning("Failed to create Network Contributor role assignment on the subnet.\n" + "Please ensure you have sufficient permissions to assign roles on subnet %s.", subnet_id) + + +def get_msi_object_id(cmd, msi_resource_id): + try: + if not is_valid_resource_id(msi_resource_id): + raise CLIError(f"The provided managed identity resource ID '{msi_resource_id}' is not valid.") + parsed = parse_resource_id(msi_resource_id) + subscription_id = parsed['subscription'] + resource_group_name = parsed['resource_group'] + msi_name = parsed['resource_name'] + msi_client = get_msi_client(cmd.cli_ctx, subscription_id=subscription_id) + msi = msi_client.user_assigned_identities.get(resource_name=msi_name, + resource_group_name=resource_group_name) + return msi.principal_id + except Exception as ex: + raise CLIError(f"Failed to get object ID for managed identity {msi_resource_id}: {str(ex)}") from ex diff --git a/src/fleet/azext_fleet/custom.py b/src/fleet/azext_fleet/custom.py index 1a9df1eb632..279dcd4ef3a 100644 --- a/src/fleet/azext_fleet/custom.py +++ b/src/fleet/azext_fleet/custom.py @@ -13,12 +13,14 @@ from azext_fleet._client_factory import CUSTOM_MGMT_FLEET from azext_fleet._helpers import print_or_merge_credentials from azext_fleet._helpers import assign_network_contributor_role_to_subnet +from azext_fleet._helpers import get_msi_object_id from azext_fleet.constants import UPGRADE_TYPE_CONTROLPLANEONLY from azext_fleet.constants import UPGRADE_TYPE_FULL from azext_fleet.constants import UPGRADE_TYPE_NODEIMAGEONLY from azext_fleet.constants import UPGRADE_TYPE_ERROR_MESSAGES from azext_fleet.constants import SUPPORTED_GATE_STATES_FILTERS from azext_fleet.constants import SUPPORTED_GATE_STATES_PATCH +from azext_fleet.constants import FLEET_1P_APP_ID # pylint: disable=too-many-locals @@ -112,7 +114,11 @@ def create_fleet(cmd, ) if enable_private_cluster: - assign_network_contributor_role_to_subnet(cmd, agent_subnet_id) + assign_network_contributor_role_to_subnet(cmd, FLEET_1P_APP_ID, agent_subnet_id) + + if enable_vnet_integration: + assign_network_contributor_role_to_subnet(cmd, get_msi_object_id(cmd, assign_identity), apiserver_subnet_id) + assign_network_contributor_role_to_subnet(cmd, get_msi_object_id(cmd, assign_identity), agent_subnet_id) return sdk_no_wait(no_wait, client.begin_create_or_update, From 08a7867518baef63cb5bd884c6460b27a8e4ff05 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 12 Sep 2025 13:09:34 -0400 Subject: [PATCH 3/8] address comments --- src/fleet/azext_fleet/_helpers.py | 42 +++++++++++++------------------ src/fleet/azext_fleet/custom.py | 20 ++++++++++++--- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/fleet/azext_fleet/_helpers.py b/src/fleet/azext_fleet/_helpers.py index 008f0477747..8497ad0e18f 100644 --- a/src/fleet/azext_fleet/_helpers.py +++ b/src/fleet/azext_fleet/_helpers.py @@ -14,7 +14,7 @@ from knack.prompting import NoTTYException, prompt_y_n from knack.util import CLIError from azure.cli.command_modules.acs._roleassignments import add_role_assignment -from azure.mgmt.core.tools import is_valid_resource_id, parse_resource_id +from azure.mgmt.core.tools import parse_resource_id from azext_fleet._client_factory import get_provider_client @@ -156,32 +156,24 @@ def _load_kubernetes_configuration(filename): raise CLIError(f'Error parsing {filename} ({str(ex)})') from ex -def assign_network_contributor_role_to_subnet(cmd, objectId, subnet_id): - resource_client = get_provider_client(cmd.cli_ctx) - provider = resource_client.providers.get("Microsoft.ContainerService") - - # provider registration state being is checked to ensure that the Fleet service principal is available - # to create the role assignment on the subnet - if provider.registration_state != 'Registered': - raise CLIError("The Microsoft.ContainerService resource provider is not registered." - "Run `az provider register -n Microsoft.ContainerService --wait`.") - - if not add_role_assignment(cmd, 'Network Contributor', objectId, scope=subnet_id): +def assign_network_contributor_role_to_subnet(cmd, object_id, subnet_id): + if not add_role_assignment(cmd, 'Network Contributor', object_id, scope=subnet_id): logger.warning("Failed to create Network Contributor role assignment on the subnet.\n" "Please ensure you have sufficient permissions to assign roles on subnet %s.", subnet_id) def get_msi_object_id(cmd, msi_resource_id): - try: - if not is_valid_resource_id(msi_resource_id): - raise CLIError(f"The provided managed identity resource ID '{msi_resource_id}' is not valid.") - parsed = parse_resource_id(msi_resource_id) - subscription_id = parsed['subscription'] - resource_group_name = parsed['resource_group'] - msi_name = parsed['resource_name'] - msi_client = get_msi_client(cmd.cli_ctx, subscription_id=subscription_id) - msi = msi_client.user_assigned_identities.get(resource_name=msi_name, - resource_group_name=resource_group_name) - return msi.principal_id - except Exception as ex: - raise CLIError(f"Failed to get object ID for managed identity {msi_resource_id}: {str(ex)}") from ex + parsed = parse_resource_id(msi_resource_id) + subscription_id = parsed['subscription'] + resource_group_name = parsed['resource_group'] + msi_name = parsed['resource_name'] + msi_client = get_msi_client(cmd.cli_ctx, subscription_id=subscription_id) + msi = msi_client.user_assigned_identities.get(resource_name=msi_name, + resource_group_name=resource_group_name) + return msi.principal_id + + +def is_rp_registered(cmd): + resource_client = get_provider_client(cmd.cli_ctx) + provider = resource_client.providers.get("Microsoft.ContainerService") + return provider.registration_state == 'Registered' diff --git a/src/fleet/azext_fleet/custom.py b/src/fleet/azext_fleet/custom.py index 279dcd4ef3a..133b759341c 100644 --- a/src/fleet/azext_fleet/custom.py +++ b/src/fleet/azext_fleet/custom.py @@ -11,7 +11,7 @@ from azure.cli.core.util import sdk_no_wait, get_file_json, shell_safe_json_parse from azext_fleet._client_factory import CUSTOM_MGMT_FLEET -from azext_fleet._helpers import print_or_merge_credentials +from azext_fleet._helpers import is_rp_registered, print_or_merge_credentials from azext_fleet._helpers import assign_network_contributor_role_to_subnet from azext_fleet._helpers import get_msi_object_id from azext_fleet.constants import UPGRADE_TYPE_CONTROLPLANEONLY @@ -92,6 +92,7 @@ def create_fleet(cmd, resource_type=CUSTOM_MGMT_FLEET, operation_group="fleets" ) + managed_service_identity = fleet_managed_service_identity_model(type="None") if enable_managed_identity: managed_service_identity.type = "SystemAssigned" @@ -106,6 +107,11 @@ def create_fleet(cmd, elif assign_identity is not None: raise CLIError("Cannot assign identity without enabling managed identity.") + if enable_vnet_integration: + if not enable_managed_identity and assign_identity is None: + raise CLIError("When vnet integration is enabled, either system-assigned or " + "user-assigned identity must be provided.") + fleet = fleet_model( location=location, tags=tags, @@ -114,11 +120,17 @@ def create_fleet(cmd, ) if enable_private_cluster: + # provider registration state being is checked to ensure that the Fleet service principal is available + # to create the role assignment on the subnet + if not is_rp_registered(cmd): + raise CLIError("The Microsoft.ContainerService resource provider is not registered." + "Run `az provider register -n Microsoft.ContainerService --wait`.") assign_network_contributor_role_to_subnet(cmd, FLEET_1P_APP_ID, agent_subnet_id) - if enable_vnet_integration: - assign_network_contributor_role_to_subnet(cmd, get_msi_object_id(cmd, assign_identity), apiserver_subnet_id) - assign_network_contributor_role_to_subnet(cmd, get_msi_object_id(cmd, assign_identity), agent_subnet_id) + if enable_vnet_integration and assign_identity is not None: + object_id = get_msi_object_id(cmd, assign_identity) + assign_network_contributor_role_to_subnet(cmd, object_id, apiserver_subnet_id) + assign_network_contributor_role_to_subnet(cmd, object_id, agent_subnet_id) return sdk_no_wait(no_wait, client.begin_create_or_update, From 5d9f6357fe1349fe58361b06df668d6091774476 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 12 Sep 2025 13:23:10 -0400 Subject: [PATCH 4/8] setup.py --- src/fleet/HISTORY.rst | 4 ++++ src/fleet/setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/fleet/HISTORY.rst b/src/fleet/HISTORY.rst index b7f5f2ec132..0eadbc4efb6 100644 --- a/src/fleet/HISTORY.rst +++ b/src/fleet/HISTORY.rst @@ -150,3 +150,7 @@ Release History 1.6.4 ++++++ * Fix help text for `fleet list` command. + +1.6.5 +++++++ +* create_fleet now creates a role assignment on both the agent and API server subnets when the fleet is set to privateV2 and a user-assigned MSI is used. diff --git a/src/fleet/setup.py b/src/fleet/setup.py index 97691ea8b81..b27e4a3bf81 100644 --- a/src/fleet/setup.py +++ b/src/fleet/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '1.6.2' +VERSION = '1.6.5' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 283ee3e4b904ddba2df235bb3a43e7b1e356d960 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Wed, 17 Sep 2025 15:57:43 -0400 Subject: [PATCH 5/8] update --- src/fleet/azext_fleet/_helpers.py | 7 +++++-- src/fleet/azext_fleet/_params.py | 5 +++-- src/fleet/azext_fleet/_validators.py | 5 +++++ src/fleet/azext_fleet/custom.py | 5 ----- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/fleet/azext_fleet/_helpers.py b/src/fleet/azext_fleet/_helpers.py index 8497ad0e18f..81f274b1a01 100644 --- a/src/fleet/azext_fleet/_helpers.py +++ b/src/fleet/azext_fleet/_helpers.py @@ -158,8 +158,11 @@ def _load_kubernetes_configuration(filename): def assign_network_contributor_role_to_subnet(cmd, object_id, subnet_id): if not add_role_assignment(cmd, 'Network Contributor', object_id, scope=subnet_id): - logger.warning("Failed to create Network Contributor role assignment on the subnet.\n" - "Please ensure you have sufficient permissions to assign roles on subnet %s.", subnet_id) + logger.warning("Failed to create Network Contributor role assignment on the subnet %s.\n" + "This role assignment is required for the managed identity to access the subnet.\n" + "Please ensure you have sufficient permissions, or ask an administrator to run:\n" + "az role assignment create --assignee %s --role 'Network Contributor' --scope %s", + subnet_id, object_id, subnet_id) def get_msi_object_id(cmd, msi_resource_id): diff --git a/src/fleet/azext_fleet/_params.py b/src/fleet/azext_fleet/_params.py index 3ff13af947a..7e7552558cc 100644 --- a/src/fleet/azext_fleet/_params.py +++ b/src/fleet/azext_fleet/_params.py @@ -24,7 +24,8 @@ validate_vm_size, validate_targets, validate_update_strategy_id, - validate_labels + validate_labels, + validate_enable_vnet_integration ) labels_type = CLIArgumentType( @@ -43,7 +44,7 @@ def load_arguments(self, _): c.argument('tags', tags_type) c.argument('dns_name_prefix', options_list=['--dns-name-prefix', '-p'], help='Prefix for host names that are created. If not specified, generate a host name using the managed cluster and resource group names.') c.argument('enable_private_cluster', action='store_true', help='Whether to create the Fleet hub as a private cluster or not.') - c.argument('enable_vnet_integration', action='store_true', help='Whether to enable apiserver vnet integration for the Fleet hub or not.') + c.argument('enable_vnet_integration', validator=validate_enable_vnet_integration, action='store_true', help='Whether to enable apiserver vnet integration for the Fleet hub or not.') c.argument('apiserver_subnet_id', validator=validate_apiserver_subnet_id, help='The subnet to be used when apiserver vnet integration is enabled.') c.argument('agent_subnet_id', validator=validate_agent_subnet_id, help='The ID of the subnet which the Fleet hub node will join on startup.') c.argument('enable_managed_identity', action='store_true', help='Enable system assigned managed identity (MSI) on the Fleet resource.') diff --git a/src/fleet/azext_fleet/_validators.py b/src/fleet/azext_fleet/_validators.py index d87aa97a80c..099983d86e3 100644 --- a/src/fleet/azext_fleet/_validators.py +++ b/src/fleet/azext_fleet/_validators.py @@ -60,6 +60,11 @@ def validate_assign_identity(namespace): "--assign-identity is not a valid Azure resource ID.") +def validate_enable_vnet_integration(namespace): + if namespace.enable_vnet_integration and not namespace.enable_managed_identity: + raise CLIError("--enable-vnet-integration requires managed identity to be enabled. " + "Please add --enable-managed-identity to your command.") + def validate_targets(namespace): ts = namespace.targets if not ts: diff --git a/src/fleet/azext_fleet/custom.py b/src/fleet/azext_fleet/custom.py index 133b759341c..1713776841c 100644 --- a/src/fleet/azext_fleet/custom.py +++ b/src/fleet/azext_fleet/custom.py @@ -107,11 +107,6 @@ def create_fleet(cmd, elif assign_identity is not None: raise CLIError("Cannot assign identity without enabling managed identity.") - if enable_vnet_integration: - if not enable_managed_identity and assign_identity is None: - raise CLIError("When vnet integration is enabled, either system-assigned or " - "user-assigned identity must be provided.") - fleet = fleet_model( location=location, tags=tags, From 19456f68a5747301fadfe3d784f120d24a137af0 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Wed, 17 Sep 2025 16:04:28 -0400 Subject: [PATCH 6/8] update style --- src/fleet/azext_fleet/_validators.py | 5 +++-- src/fleet/azext_fleet/custom.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fleet/azext_fleet/_validators.py b/src/fleet/azext_fleet/_validators.py index 099983d86e3..d9e5b21d384 100644 --- a/src/fleet/azext_fleet/_validators.py +++ b/src/fleet/azext_fleet/_validators.py @@ -62,8 +62,9 @@ def validate_assign_identity(namespace): def validate_enable_vnet_integration(namespace): if namespace.enable_vnet_integration and not namespace.enable_managed_identity: - raise CLIError("--enable-vnet-integration requires managed identity to be enabled. " - "Please add --enable-managed-identity to your command.") + raise CLIError("--enable-vnet-integration requires managed identity to be enabled. " + "Please add --enable-managed-identity to your command.") + def validate_targets(namespace): ts = namespace.targets diff --git a/src/fleet/azext_fleet/custom.py b/src/fleet/azext_fleet/custom.py index 1713776841c..acbce9d757f 100644 --- a/src/fleet/azext_fleet/custom.py +++ b/src/fleet/azext_fleet/custom.py @@ -92,7 +92,6 @@ def create_fleet(cmd, resource_type=CUSTOM_MGMT_FLEET, operation_group="fleets" ) - managed_service_identity = fleet_managed_service_identity_model(type="None") if enable_managed_identity: managed_service_identity.type = "SystemAssigned" From 735393d723c8185a9668281bd9eb4b04a77dc68d Mon Sep 17 00:00:00 2001 From: Muhammad Date: Wed, 17 Sep 2025 19:02:55 -0400 Subject: [PATCH 7/8] release version block --- src/fleet/HISTORY.rst | 4 ---- src/fleet/setup.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/fleet/HISTORY.rst b/src/fleet/HISTORY.rst index 0eadbc4efb6..b7f5f2ec132 100644 --- a/src/fleet/HISTORY.rst +++ b/src/fleet/HISTORY.rst @@ -150,7 +150,3 @@ Release History 1.6.4 ++++++ * Fix help text for `fleet list` command. - -1.6.5 -++++++ -* create_fleet now creates a role assignment on both the agent and API server subnets when the fleet is set to privateV2 and a user-assigned MSI is used. diff --git a/src/fleet/setup.py b/src/fleet/setup.py index b27e4a3bf81..97691ea8b81 100644 --- a/src/fleet/setup.py +++ b/src/fleet/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '1.6.5' +VERSION = '1.6.2' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 54307d2a174ca085f875f3063414ed865f34c9a3 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Thu, 18 Sep 2025 20:51:40 -0400 Subject: [PATCH 8/8] update --- src/fleet/azext_fleet/_helpers.py | 3 ++- src/fleet/azext_fleet/_validators.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/fleet/azext_fleet/_helpers.py b/src/fleet/azext_fleet/_helpers.py index 81f274b1a01..76fbb440719 100644 --- a/src/fleet/azext_fleet/_helpers.py +++ b/src/fleet/azext_fleet/_helpers.py @@ -161,7 +161,8 @@ def assign_network_contributor_role_to_subnet(cmd, object_id, subnet_id): logger.warning("Failed to create Network Contributor role assignment on the subnet %s.\n" "This role assignment is required for the managed identity to access the subnet.\n" "Please ensure you have sufficient permissions, or ask an administrator to run:\n" - "az role assignment create --assignee %s --role 'Network Contributor' --scope %s", + "az role assignment create --assignee-principal-type ServicePrincipal --assignee-object-id %s " + "--role 'Network Contributor' --scope %s", subnet_id, object_id, subnet_id) diff --git a/src/fleet/azext_fleet/_validators.py b/src/fleet/azext_fleet/_validators.py index d9e5b21d384..377ddd7972a 100644 --- a/src/fleet/azext_fleet/_validators.py +++ b/src/fleet/azext_fleet/_validators.py @@ -61,9 +61,10 @@ def validate_assign_identity(namespace): def validate_enable_vnet_integration(namespace): - if namespace.enable_vnet_integration and not namespace.enable_managed_identity: - raise CLIError("--enable-vnet-integration requires managed identity to be enabled. " - "Please add --enable-managed-identity to your command.") + if namespace.enable_vnet_integration: + if not namespace.enable_managed_identity or namespace.assign_identity is None: + raise CLIError("--enable-vnet-integration requires user assigned managed identity to be enabled. " + "Please add --enable-managed-identity and --assign-identity to your command.") def validate_targets(namespace):