Skip to content

Conversation

@william051200
Copy link
Member

@william051200 william051200 commented Dec 23, 2025

Related command
az vm identity assign
az vm identity remove
az vm identity show

Description

Migration from mgmt.compute to aaz-based

Testing Guide

History Notes


This checklist is used to make sure that common guidelines for a pull request are followed.

Copilot AI review requested due to automatic review settings December 23, 2025 04:22
@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Dec 23, 2025

❌AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
❌core
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_help_loads The error message is too long, please check the pipeline log for details. src/azure-cli-core/azure/cli/core/tests/test_help.py:120
❌3.13
Type Test Case Error Message Line
Failed test_help_loads The error message is too long, please check the pipeline log for details. src/azure-cli-core/azure/cli/core/tests/test_help.py:120
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
❌network
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_mariadb_private_link_scenario The error message is too long, please check the pipeline log for details. azure/cli/command_modules/network/tests/latest/test_private_endpoint_commands.py:649
Failed test_mysql_private_link_scenario The error message is too long, please check the pipeline log for details. azure/cli/command_modules/network/tests/latest/test_private_endpoint_commands.py:656
❌3.13
Type Test Case Error Message Line
Failed test_mariadb_private_link_scenario The error message is too long, please check the pipeline log for details. azure/cli/command_modules/network/tests/latest/test_private_endpoint_commands.py:649
Failed test_mysql_private_link_scenario The error message is too long, please check the pipeline log for details. azure/cli/command_modules/network/tests/latest/test_private_endpoint_commands.py:656
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
❌rdbms
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_flexible_server_ssdv2_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:91
Failed test_flexible_server_ssdv2_restore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:106
Failed test_postgres_flexible_server_byok_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:116
Failed test_postgres_flexible_server_empty_rg_name The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:81
Failed test_postgres_flexible_server_georestore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:101
Failed test_postgres_flexible_server_ltr The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:111
Failed test_postgres_flexible_server_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:76
Failed test_postgres_flexible_server_mgmt_case_insensitive The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:86
Failed test_postgres_flexible_server_public_revivedropped_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:123
Failed test_postgres_flexible_server_restore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:96
Failed test_postgres_flexible_server_proxy_resource The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1014
Failed test_postgres_flexible_server_mgmt_update_validator The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1123
Failed test_postgres_flexible_server_auto_grow_replica_validator The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1263
Failed test_postgres_flexible_server_replica_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1257
Failed test_postgres_flexible_server_vnet_mgmt_supplied_subnet_id_in_different_rg The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1484
Failed test_postgres_flexible_server_vnet_mgmt_supplied_subnetid The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1473
Failed test_postgres_flexible_server_vnet_mgmt_supplied_vname_and_subnetname The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1479
Failed test_postgres_flexible_server_existing_private_dns_zone The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1879
Failed test_postgres_flexible_server_new_private_dns_zone The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1885
Failed test_postgres_flexible_server_upgrade_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2143
Failed test_postgres_flexible_server_backups_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2196
Failed test_postgresql_flexible_server_identity_microsoft_entra_admin_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2248
Failed test_postgresql_flexible_server_identity_microsoft_entra_admin_only_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2253
Failed test_postgres_flexible_server_advanced_threat_protection_setting_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2420
Failed test_postgres_flexible_server_logs_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2469
Failed test_postgres_flexible_server_private_endpoint_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2528
Failed test_postgres_flexible_server_fabric_mirroring_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2730
Failed test_elastic_clusters_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2784
Failed test_postgres_flexible_server_index_tuning_options The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2849
Failed test_postgres_flexible_server_autonomous_tuning_options The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2908
Failed test_postgres_flexible_server_migration The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands_postgres_migration.py:19
Failed test_postgres_flexible_server_onpremise_migration The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands_postgres_migration.py:23
❌3.13
Type Test Case Error Message Line
Failed test_flexible_server_ssdv2_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:91
Failed test_flexible_server_ssdv2_restore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:106
Failed test_postgres_flexible_server_byok_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:116
Failed test_postgres_flexible_server_empty_rg_name The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:81
Failed test_postgres_flexible_server_georestore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:101
Failed test_postgres_flexible_server_ltr The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:111
Failed test_postgres_flexible_server_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:76
Failed test_postgres_flexible_server_mgmt_case_insensitive The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:86
Failed test_postgres_flexible_server_public_revivedropped_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:123
Failed test_postgres_flexible_server_restore_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:96
Failed test_postgres_flexible_server_proxy_resource The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1014
Failed test_postgres_flexible_server_mgmt_update_validator The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1123
Failed test_postgres_flexible_server_auto_grow_replica_validator The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1263
Failed test_postgres_flexible_server_replica_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1257
Failed test_postgres_flexible_server_vnet_mgmt_supplied_subnet_id_in_different_rg The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1484
Failed test_postgres_flexible_server_vnet_mgmt_supplied_subnetid The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1473
Failed test_postgres_flexible_server_vnet_mgmt_supplied_vname_and_subnetname The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1479
Failed test_postgres_flexible_server_existing_private_dns_zone The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1879
Failed test_postgres_flexible_server_new_private_dns_zone The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:1885
Failed test_postgres_flexible_server_upgrade_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2143
Failed test_postgres_flexible_server_backups_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2196
Failed test_postgresql_flexible_server_identity_microsoft_entra_admin_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2248
Failed test_postgresql_flexible_server_identity_microsoft_entra_admin_only_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2253
Failed test_postgres_flexible_server_advanced_threat_protection_setting_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2420
Failed test_postgres_flexible_server_logs_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2469
Failed test_postgres_flexible_server_private_endpoint_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2528
Failed test_postgres_flexible_server_fabric_mirroring_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2730
Failed test_elastic_clusters_mgmt The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2784
Failed test_postgres_flexible_server_index_tuning_options The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2849
Failed test_postgres_flexible_server_autonomous_tuning_options The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py:2908
Failed test_postgres_flexible_server_migration The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands_postgres_migration.py:19
Failed test_postgres_flexible_server_onpremise_migration The error message is too long, please check the pipeline log for details. azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands_postgres_migration.py:23
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
️✔️sql
️✔️latest
️✔️3.12
️✔️3.13
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
❌vm
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_vm_explicit_msi self = <azure.cli.testsdk.base.ExecutionResult object at 0x7fe2e3c20cb0>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7fe2e67d6390>
command = 'vm create -g clitest.rg000001 -n vm1 --image Debian:debian-10:10:latest --assign-identity id1 [system] --role reader ...generate-ssh-keys --admin-username ubuntuadmin --subnet subnet1 --vnet-name vnet1 --nsg-rule NONE --size Standard_B2ms'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.12/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:133: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = InvalidTemplateError(''), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception InvalidTemplateError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:

self = <latest.test_vm_commands.MSIScenarioTest testMethod=test_vm_explicit_msi>
resource_group = 'clitest.rg000001'

    @AllowLargeResponse(size_kb=99999)
    @ResourceGroupPreparer(random_name_length=20)
    def test_vm_explicit_msi(self, resource_group):
    
        self.kwargs.update({
            'emsi': 'id1',
            'emsi2': 'id2',
            'vm': 'vm1',
            'sub': self.get_subscription_id(),
            'scope': '/subscriptions/{}/resourceGroups/{}'.format(self.get_subscription_id(), resource_group),
            'user': 'ubuntuadmin',
            'subnet': 'subnet1',
            'vnet': 'vnet1'
        })
    
        # create a managed identity
        emsi_result = self.cmd('identity create -g {rg} -n {emsi} --tags tag1=d1', checks=[
            self.check('name', '{emsi}'),
            self.check('tags.tag1', 'd1')]).get_output_in_json()
        emsi2_result = self.cmd('identity create -g {rg} -n {emsi2}').get_output_in_json()
    
        # create a vm with only user assigned identity
        result = self.cmd('vm create -g {rg} -n vm2 --image Debian:debian-10:10:latest '
                          '--assign-identity {emsi} --generate-ssh-keys --admin-username {user} '
                          '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                          '--size Standard_B2ms', checks=[
            self.check('identity.role', None),
            self.check('identity.scope', None),
        ]).get_output_in_json()
    
        # Disable default outbound access
        self.cmd(
            'network vnet subnet update -g {rg} --vnet-name {vnet} -n {subnet} --default-outbound-access false')
    
        emsis = [x.lower() for x in result['identity']['userAssignedIdentities'].keys()]
        self.assertEqual(emsis, [emsi_result['id'].lower()])
        self.assertFalse(result['identity']['systemAssignedIdentity'])
    
        # create a vm with system + user assigned identities
>       result = self.cmd('vm create -g {rg} -n {vm} --image Debian:debian-10:10:latest '
                          '--assign-identity {emsi} [system] --role reader --scope {scope} --generate-ssh-keys '
                          '--admin-username {user} --subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                          '--size Standard_B2ms').get_output_in_json()

src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6256: 
 
                                       
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.12/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:726: in run_job
    return cmd_copy.exception_handler(ex)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/arm.py:112: in handle_template_based_exception
    raise_subdivision_deployment_error(ex.response.internal_response.text, ex.error.code if ex.error else None)
 
 
 
 
                                   _ 

error_message = '', error_code = 'InvalidTemplateDeployment'

    def raise_subdivision_deployment_error(error_message, error_code=None):
        from azure.cli.core.azclierror import InvalidTemplateError, DeploymentError
    
        if error_code == 'InvalidTemplateDeployment':
>           raise InvalidTemplateError(error_message)
E           azure.cli.core.azclierror.InvalidTemplateError

src/azure-cli-core/azure/cli/core/commands/arm.py:102: InvalidTemplateError
azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6216
Failed test_vm_msi self = <azure.cli.testsdk.base.ExecutionResult object at 0x7fe2e3bfdcd0>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7fe2e62d2c00>
command = 'vm create -g cli_test_vm_msi000001 -n vm1 --image Debian:debian-10:10:latest --assign-identity --admin-username admin...roups/cli_test_vm_msi000001 --role Contributor --subnet subnet1 --vnet-name vnet1 --nsg-rule NONE --size Standard_B2ms'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.12/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:133: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = InvalidTemplateError(''), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception InvalidTemplateError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:

self = <latest.test_vm_commands.MSIScenarioTest testMethod=test_vm_msi>
resource_group = 'cli_test_vm_msi000001'

    @AllowLargeResponse(size_kb=99999)
    @ResourceGroupPreparer(name_prefix='cli_test_vm_msi')
    def test_vm_msi(self, resource_group):
        subscription_id = self.get_subscription_id()
    
        self.kwargs.update({
            'scope': '/subscriptions/{}/resourceGroups/{}'.format(subscription_id, resource_group),
            'vm1': 'vm1',
            'vm1_id': '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/vm1'.format(subscription_id, resource_group),
            'vm2': 'vm2',
            'vm3': 'vm3',
            'sub': subscription_id,
            'subnet': 'subnet1',
            'vnet': 'vnet1'
        })
    
        with self.assertRaisesRegex(ArgumentUsageError, "please specify both --role and --scope"):
            self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE --scope {scope} '
                     '--size Standard_B2ms')
    
        with self.assertRaisesRegex(ArgumentUsageError, "please specify both --role and --scope"):
            self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE --role Contributor '
                     '--size Standard_B2ms')
    
        with mock.patch('azure.cli.core.commands.arm.gen_guid', side_effect=self.create_guid):
            # create a linux vm with default configuration
>           self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--scope {scope} --role Contributor --subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                     '--size Standard_B2ms', checks=[
                self.check('identity.role', 'Contributor'),
                self.check('identity.scope', '/subscriptions/{sub}/resourceGroups/{rg}'),
            ])

src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6072: 
 
 
                                      
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.12/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:726: in run_job
    return cmd_copy.exception_handler(ex)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/arm.py:112: in handle_template_based_exception
    raise_subdivision_deployment_error(ex.response.internal_response.text, ex.error.code if ex.error else None)
 
 
 
 
 
                                   

error_message = '', error_code = 'InvalidTemplateDeployment'

    def raise_subdivision_deployment_error(error_message, error_code=None):
        from azure.cli.core.azclierror import InvalidTemplateError, DeploymentError
    
        if error_code == 'InvalidTemplateDeployment':
>           raise InvalidTemplateError(error_message)
E           azure.cli.core.azclierror.InvalidTemplateError

src/azure-cli-core/azure/cli/core/commands/arm.py:102: InvalidTemplateError
azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6041
❌3.13
Type Test Case Error Message Line
Failed test_vm_explicit_msi self = <azure.cli.testsdk.base.ExecutionResult object at 0x7f6f6b89f820>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7f6f6f16f750>
command = 'vm create -g clitest.rg000001 -n vm1 --image Debian:debian-10:10:latest --assign-identity id1 [system] --role reader ...generate-ssh-keys --admin-username ubuntuadmin --subnet subnet1 --vnet-name vnet1 --nsg-rule NONE --size Standard_B2ms'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.13/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:133: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = InvalidTemplateError(''), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception InvalidTemplateError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:

self = <latest.test_vm_commands.MSIScenarioTest testMethod=test_vm_explicit_msi>
resource_group = 'clitest.rg000001'

    @AllowLargeResponse(size_kb=99999)
    @ResourceGroupPreparer(random_name_length=20)
    def test_vm_explicit_msi(self, resource_group):
    
        self.kwargs.update({
            'emsi': 'id1',
            'emsi2': 'id2',
            'vm': 'vm1',
            'sub': self.get_subscription_id(),
            'scope': '/subscriptions/{}/resourceGroups/{}'.format(self.get_subscription_id(), resource_group),
            'user': 'ubuntuadmin',
            'subnet': 'subnet1',
            'vnet': 'vnet1'
        })
    
        # create a managed identity
        emsi_result = self.cmd('identity create -g {rg} -n {emsi} --tags tag1=d1', checks=[
            self.check('name', '{emsi}'),
            self.check('tags.tag1', 'd1')]).get_output_in_json()
        emsi2_result = self.cmd('identity create -g {rg} -n {emsi2}').get_output_in_json()
    
        # create a vm with only user assigned identity
        result = self.cmd('vm create -g {rg} -n vm2 --image Debian:debian-10:10:latest '
                          '--assign-identity {emsi} --generate-ssh-keys --admin-username {user} '
                          '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                          '--size Standard_B2ms', checks=[
            self.check('identity.role', None),
            self.check('identity.scope', None),
        ]).get_output_in_json()
    
        # Disable default outbound access
        self.cmd(
            'network vnet subnet update -g {rg} --vnet-name {vnet} -n {subnet} --default-outbound-access false')
    
        emsis = [x.lower() for x in result['identity']['userAssignedIdentities'].keys()]
        self.assertEqual(emsis, [emsi_result['id'].lower()])
        self.assertFalse(result['identity']['systemAssignedIdentity'])
    
        # create a vm with system + user assigned identities
>       result = self.cmd('vm create -g {rg} -n {vm} --image Debian:debian-10:10:latest '
                          '--assign-identity {emsi} [system] --role reader --scope {scope} --generate-ssh-keys '
                          '--admin-username {user} --subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                          '--size Standard_B2ms').get_output_in_json()

src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6256: 
 
                                       
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.13/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:726: in run_job
    return cmd_copy.exception_handler(ex)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/arm.py:112: in handle_template_based_exception
    raise_subdivision_deployment_error(ex.response.internal_response.text, ex.error.code if ex.error else None)
 
 
 
 
                                   _ 

error_message = '', error_code = 'InvalidTemplateDeployment'

    def raise_subdivision_deployment_error(error_message, error_code=None):
        from azure.cli.core.azclierror import InvalidTemplateError, DeploymentError
    
        if error_code == 'InvalidTemplateDeployment':
>           raise InvalidTemplateError(error_message)
E           azure.cli.core.azclierror.InvalidTemplateError

src/azure-cli-core/azure/cli/core/commands/arm.py:102: InvalidTemplateError
azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6216
Failed test_vm_msi self = <azure.cli.testsdk.base.ExecutionResult object at 0x7f6f6cd3ac10>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7f6f6f224050>
command = 'vm create -g cli_test_vm_msi000001 -n vm1 --image Debian:debian-10:10:latest --assign-identity --admin-username admin...roups/cli_test_vm_msi000001 --role Contributor --subnet subnet1 --vnet-name vnet1 --nsg-rule NONE --size Standard_B2ms'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.13/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:133: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
                                        

ex = InvalidTemplateError(''), args = (), kwargs = {}

    def handle_main_exception(ex, *args, **kwargs):  # pylint: disable=unused-argument
        if isinstance(ex, CannotOverwriteExistingCassetteException):
            # This exception usually caused by a no match HTTP request. This is a product error
            # that is caused by change of SDK invocation.
            raise ex
    
>       raise CliExecutionError(ex)
E       azure.cli.testsdk.exceptions.CliExecutionError: The CLI throws exception InvalidTemplateError during execution and fails the command.

src/azure-cli-testsdk/azure/cli/testsdk/patches.py:35: CliExecutionError

During handling of the above exception, another exception occurred:

self = <latest.test_vm_commands.MSIScenarioTest testMethod=test_vm_msi>
resource_group = 'cli_test_vm_msi000001'

    @AllowLargeResponse(size_kb=99999)
    @ResourceGroupPreparer(name_prefix='cli_test_vm_msi')
    def test_vm_msi(self, resource_group):
        subscription_id = self.get_subscription_id()
    
        self.kwargs.update({
            'scope': '/subscriptions/{}/resourceGroups/{}'.format(subscription_id, resource_group),
            'vm1': 'vm1',
            'vm1_id': '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Compute/virtualMachines/vm1'.format(subscription_id, resource_group),
            'vm2': 'vm2',
            'vm3': 'vm3',
            'sub': subscription_id,
            'subnet': 'subnet1',
            'vnet': 'vnet1'
        })
    
        with self.assertRaisesRegex(ArgumentUsageError, "please specify both --role and --scope"):
            self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE --scope {scope} '
                     '--size Standard_B2ms')
    
        with self.assertRaisesRegex(ArgumentUsageError, "please specify both --role and --scope"):
            self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--subnet {subnet} --vnet-name {vnet} --nsg-rule NONE --role Contributor '
                     '--size Standard_B2ms')
    
        with mock.patch('azure.cli.core.commands.arm.gen_guid', side_effect=self.create_guid):
            # create a linux vm with default configuration
>           self.cmd('vm create -g {rg} -n {vm1} --image Debian:debian-10:10:latest '
                     '--assign-identity --admin-username admin123 --admin-password PasswordPassword1! '
                     '--scope {scope} --role Contributor --subnet {subnet} --vnet-name {vnet} --nsg-rule NONE '
                     '--size Standard_B2ms', checks=[
                self.check('identity.role', 'Contributor'),
                self.check('identity.scope', '/subscriptions/{sub}/resourceGroups/{rg}'),
            ])

src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6072: 
 
 
                                      
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
src/azure-cli-testsdk/azure/cli/testsdk/base.py:315: in in_process_execute
    raise ex.exception
env/lib/python3.13/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:726: in run_job
    return cmd_copy.exception_handler(ex)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/arm.py:112: in handle_template_based_exception
    raise_subdivision_deployment_error(ex.response.internal_response.text, ex.error.code if ex.error else None)
 
 
 
 
 
                                   

error_message = '', error_code = 'InvalidTemplateDeployment'

    def raise_subdivision_deployment_error(error_message, error_code=None):
        from azure.cli.core.azclierror import InvalidTemplateError, DeploymentError
    
        if error_code == 'InvalidTemplateDeployment':
>           raise InvalidTemplateError(error_message)
E           azure.cli.core.azclierror.InvalidTemplateError

src/azure-cli-core/azure/cli/core/commands/arm.py:102: InvalidTemplateError
azure/cli/command_modules/vm/tests/latest/test_vm_commands.py:6041

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Dec 23, 2025

️✔️AzureCLI-BreakingChangeTest
️✔️Non Breaking Changes

@yonzhan
Copy link
Collaborator

yonzhan commented Dec 23, 2025

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@william051200
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the az vm identity commands (assign, remove, and show) from the older mgmt.compute SDK to the AAZ (Azure AutoRest) framework. The migration modernizes the command implementation while maintaining backward compatibility.

Key Changes

  • Migrated show_vm_identity, assign_vm_identity, and remove_vm_identity to use AAZ-based operations instead of mgmt.compute SDK
  • Added helper functions assign_identity and resolve_role_id to _vm_utils.py to support the new implementation
  • Introduced IdentityType enum for better type management
  • Created VMIdentityRemove class in operations/vm.py to handle identity removal logic
  • Updated test commands to include --size parameter for VM creation

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.

File Description
test_vm_commands.py Updated test cases with explicit VM size specifications and reformatted command strings for better readability. No functional test changes.
operations/vm.py Added VMIdentityRemove class extending _VMPatch to handle identity removal with custom content formatting logic.
custom.py Migrated three identity functions to use AAZ operations: show_vm_identity, assign_vm_identity, and remove_vm_identity. Added get_vm_by_aaz helper and _remove_identities_by_aaz function.
_vm_utils.py Added utility functions (assign_identity, resolve_role_id, _gen_guid) and IdentityType enum to support AAZ-based identity operations. Consolidated imports at the top.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

from ._vm_utils import IdentityType
if vm.get('identity') and vm.get('identity').get('type') == IdentityType.USER_ASSIGNED.value:
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a redundant 'UserAssigned' literal string being added to the mi_user_assigned list. This appears to be a workaround for special handling in VMIdentityRemove._format_content (line 194), but it creates confusion since 'UserAssigned' is not a valid identity resource ID. Consider documenting this special case with a comment explaining why 'UserAssigned' is added.

Suggested change
if vm.get('identity') and vm.get('identity').get('type') == IdentityType.USER_ASSIGNED.value:
if vm.get('identity') and vm.get('identity').get('type') == IdentityType.USER_ASSIGNED.value:
# NOTE: The literal 'UserAssigned' is intentionally appended as a marker for
# VMIdentityRemove._format_content, which uses it to apply special handling
# for purely user-assigned identities. It is not a real identity resource ID.

Copilot uses AI. Check for mistakes.
Comment on lines 2614 to 2621
([key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())] +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
[key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())]
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list() call is unnecessary here as dict.keys() already returns an iterable that can be directly used in the list comprehension. This creates an extra intermediate list.

Suggested change
([key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())] +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
[key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())]
(list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys()) +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())

Copilot uses AI. Check for mistakes.
Comment on lines 2614 to 2621
([key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())] +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
[key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())]
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list() call is unnecessary here as dict.keys() already returns an iterable that can be directly used in the list comprehension. This creates an extra intermediate list.

Suggested change
([key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())] +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
[key for key in list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())]
(list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys()) +
['UserAssigned'])
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED.value:
command_args['mi_user_assigned'] = []
command_args['mi_system_assigned'] = 'True'
elif vm.get('identity') and vm.get('identity').get('type') == IdentityType.SYSTEM_ASSIGNED_USER_ASSIGNED.value:
command_args['mi_user_assigned'] = \
list(vm.get('identity', {}).get('userAssignedIdentities', {}).keys())

Copilot uses AI. Check for mistakes.
for key in list(identities.keys()):
identities[key] = None

if len(content.get('identity', {}).get('userAssignedIdentities', {}).keys()) < 1:
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code checks if the length is less than 1, but this is equivalent to checking if the dictionary is empty. Using if not content.get('identity', {}).get('userAssignedIdentities', {}) would be more idiomatic and readable.

Suggested change
if len(content.get('identity', {}).get('userAssignedIdentities', {}).keys()) < 1:
if not content.get('identity', {}).get('userAssignedIdentities', {}):

Copilot uses AI. Check for mistakes.

# create role assignment:
if identity_scope:
principal_id = resource.get('identity', {}).get('principal_id')
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key name 'principal_id' should be 'principalId' to match the camelCase format used in AAZ-based responses. Throughout the codebase (such as in assign_vm_identity at line 900), 'principalId' is used consistently when accessing AAZ response dictionaries.

Suggested change
principal_id = resource.get('identity', {}).get('principal_id')
principal_id = resource.get('identity', {}).get('principalId') or \
resource.get('identity', {}).get('principal_id')

Copilot uses AI. Check for mistakes.
if identity and not identity.get('userAssignedIdentities'):
identity['userAssignedIdentities'] = None

return identity or None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to consider whether this should return an empty dictionary ({}) or None when there is no identity on the VM. We can refer to the SDK behavior for guidance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Here is the output from SDK behavior, it returns None instead of of ( { } ) for empty result.

identity_types = ResourceIdentityType.system_assigned_user_assigned
elif vm.identity and vm.identity.type == ResourceIdentityType.user_assigned and enable_local_identity:
identity_types = ResourceIdentityType.system_assigned_user_assigned
if vm.get('identity') and vm.get('identity').get('type') == system_assigned_user_assigned:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for your reference, adding a default value to the get() function of dict might be more concise in this case

Suggested change
if vm.get('identity') and vm.get('identity').get('type') == system_assigned_user_assigned:
if vm.get('identity', {}).get('type', None) == system_assigned_user_assigned:

Comment on lines 847 to 849
system_assigned = "SystemAssigned"
user_assigned = "UserAssigned"
system_assigned_user_assigned = "SystemAssigned, UserAssigned"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since they are already defined in _vm_utils.py file, it would be better to leverage the definition across all the places that need to read the values

Comment on lines 205 to 209
if not identity.get('principalId'):
identity['principalId'] = None

if not identity.get('tenantId'):
identity['tenantId'] = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may I ask why we need to add the None value to the result?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just validated, I was using them when running some test, it is now redundant. I will be removing those lines. Thank you.

Comment on lines +241 to +242
if 'UserAssigned' in identities.keys():
identities.pop('UserAssigned')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask what the identities look like in this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I have implemented this solution is because:
image

  1. VM Patch will always set userAssigned and systemAssigned action flag to create.
  2. VM Patch only accept a list of userAssigned identities, then AAZIdentityObject will always format the identity to create based on the create action.
  3. Therefore, I will only pass identity to be removed to the list as I did a little twist by inheriting the original VM Patch, then set the identities to be removed into this format {'identity':None}. (I found this is the only format to remove userAssigned identity from the list)
  4. It works until I need to remove systemAssigned identity from systemAssigned + userAssigned identities, this is because passing userAssigned as empty list (because I am not removing any userAssigned identity) and AAZIdentityObject will be converting the identity to None instead of UserAssigned, cause the removal of systemAssigned + userAssigned identities.
  5. The workaround is, I have added a placeholder into the userAssigned identity list and send to customized aaz in this format {'resource_group': 'william-rg', 'vm_name': 'william-vm1', 'mi_user_assigned': ['UserAssigned']}. This way, AAZIdentityObject will set the type of identity to UserAssigned, hence retain the userAssigned identities.
  6. Then I remove the placeholder afterwards in the inherited function.

Result:
image


return json.dumps(content)

def __call__(self, *args, **kwargs):
Copy link
Contributor

@yanzhudd yanzhudd Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to manually write this __call__() function here, as I remember it is already defined in the parent class

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the call function exists but is it not workable for 'removing identity using vm patch class'. Reason as:

This is _update.py, it does support pre_instance_update, which modifies the content of request before calling the update API.
image

This is _patch.py, it does not support pre_instance_update, and I do not find any other function to perform the same function as pre_instance_update
image

And that is why I have created __call__ function and added one function to modify the content
image

@yanzhudd yanzhudd changed the title [Compute] vm identity command migration {Compute} az vm identity: Migrate commands to aaz-based implementation Dec 31, 2025
@yanzhudd
Copy link
Contributor

since the changes are not visible to users, the title should be started with {Compute} instead of [Compute]
for more information, please refer to https://github.com/Azure/azure-cli/tree/dev/doc/authoring_command_modules#submitting-pull-requests

@yanzhudd
Copy link
Contributor

could you please double check the changes to the recording yaml files? We need to ensure there is no difference for the command behavior after migrating to aaz-based

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot Compute az vm/vmss/image/disk/snapshot

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants