Skip to content

Conversation

@william051200
Copy link
Member

Related command

az vmss identity assign
az vmss identity remove
az vmss 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 January 22, 2026 03:59
@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Jan 22, 2026

️✔️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
️✔️3.13
️✔️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
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️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
️✔️3.13

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

Hi @william051200,
Since the current milestone time is less than 7 days, this pr will be reviewed in the next milestone.

@yonzhan
Copy link
Collaborator

yonzhan commented Jan 22, 2026

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

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

azure-client-tools-bot-prd bot commented Jan 22, 2026

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

@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>

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 az vmss identity operations (assign, remove, show) from the mgmt SDK implementation to the aaz-based implementation and updates tests and utilities to match the new flow.

Changes:

  • Re-route vmss identity commands to custom/aaz-based implementations and adjust command wiring in commands.py.
  • Implement vmss identity show/assign/remove using AAZ-based helpers and new identity utilities (IdentityType, UpgradeMode), including VMSS-specific patch and identity-removal flows.
  • Update MSI-related VMSS tests to use specific VM sizes/OS images compatible with the new behavior.

Reviewed changes

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

Show a summary per file
File Description
src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.py Adjusts VMSS MSI tests (images and --vm-sku) and adds coverage for the new VMSS identity behavior.
src/azure-cli/azure/cli/command_modules/vm/operations/vmss.py Introduces VMSSPatch and VMSSIdentityRemove AAZ wrappers to normalize extension typing conflicts and implement AAZ-based VMSS identity removal.
src/azure-cli/azure/cli/command_modules/vm/custom.py Switches VMSS identity show/assign/remove to use AAZ-based helpers (get_vmss_by_aaz, _remove_identities_by_aaz, IdentityType, UpgradeMode) and normalizes identity payloads.
src/azure-cli/azure/cli/command_modules/vm/commands.py Rebinds vmss identity commands to a custom (non-SDK) command group while keeping other VMSS operations on the SDK-backed group.
src/azure-cli/azure/cli/command_modules/vm/_vm_utils.py Adds IdentityType and UpgradeMode enums and an assign_identity helper used by the new VMSS identity assign path.

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

if include_user_data:
command_args['expand'] = 'userData'

if instance_id is not None:
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

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

get_vmss_by_aaz accepts an instance_id argument but the instance_id is never added to command_args before calling VMSSVMSShow, so any future caller that passes instance_id will not get the expected per-instance result. Please include the instance_id in command_args when instance_id is not None, consistent with how get_vmss_modified_by_aaz and other helpers wire through this parameter.

Suggested change
if instance_id is not None:
if instance_id is not None:
command_args['instance_id'] = instance_id

Copilot uses AI. Check for mistakes.
Comment on lines 105 to 112
return None

for user_identity in identity.get('userAssignedIdentities', {}).keys():
if not identity['userAssignedIdentities'][user_identity].get('clientId'):
identity['userAssignedIdentities'][user_identity]['clientId'] = None
if not identity['userAssignedIdentities'][user_identity].get('principalId'):
identity['userAssignedIdentities'][user_identity]['principalId'] = None

Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

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

In VMSSIdentityRemove._output, when identity.get('userAssignedIdentities') is falsy you set identity['userAssignedIdentities'] = None and then return None, which causes callers like _remove_identities_by_aaz to see a None result even when a system-assigned identity still exists. This differs from the VM path (VMIdentityRemove._output in operations/vm.py:201-208), where the full result is always returned, and it prevents consumers from inspecting the remaining identity state after a successful removal; please return the result object in all success cases (after normalizing userAssignedIdentities) so that _remove_identities_by_aaz can consistently access result['identity'].

Suggested change
return None
for user_identity in identity.get('userAssignedIdentities', {}).keys():
if not identity['userAssignedIdentities'][user_identity].get('clientId'):
identity['userAssignedIdentities'][user_identity]['clientId'] = None
if not identity['userAssignedIdentities'][user_identity].get('principalId'):
identity['userAssignedIdentities'][user_identity]['principalId'] = None
else:
for user_identity in identity.get('userAssignedIdentities', {}).keys():
if not identity['userAssignedIdentities'][user_identity].get('clientId'):
identity['userAssignedIdentities'][user_identity]['clientId'] = None
if not identity['userAssignedIdentities'][user_identity].get('principalId'):
identity['userAssignedIdentities'][user_identity]['principalId'] = None

Copilot uses AI. Check for mistakes.
return client.virtual_machine_scale_sets.get(resource_group_name, vmss_name)
if vmss.get('identity') and vmss.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
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

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

The comment here refers to VMIdentityRemove._format_content, but the implementation actually uses VMSSIdentityRemove from operations.vmss, which can be confusing when maintaining the VMSS-specific flow. To avoid misleading future readers, please update the comment to reference the correct class/method (or to describe the behavior more generically) so it accurately documents the VMSS identity removal path.

Suggested change
# VMIdentityRemove._format_content, which uses it to apply special handling
# VMSSIdentityRemove._format_content, which uses it to apply special handling

Copilot uses AI. Check for mistakes.
Comment on lines +849 to +869
if not identity:
return None

user_assigned = identity.get('userAssignedIdentities', {})

if not user_assigned:
identity['userAssignedIdentities'] = None
else:
for user_identity in user_assigned.keys():
if not user_assigned.get(user_identity).get('clientId'):
user_assigned[user_identity]['clientId'] = None
if not user_assigned.get(user_identity).get('principalId'):
user_assigned[user_identity]['principalId'] = None

if not identity.get('principalId'):
identity['principalId'] = None

if not identity.get('tenantId'):
identity['tenantId'] = 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.

may I ask why we need to make these changes?

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 purpose of this change is to match the same output structure before migration.

For example:
Before set "None" to the field

{
  "systemAssignedIdentity": "",
  "userAssignedIdentities": {
    "/subscriptions/fe3bc2b7-22b0-4133-af8f-9439cb831236/resourceGroups/william-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id1": {}
  }
}

After set "None" to the field:

{
  "systemAssignedIdentity": "",
  "userAssignedIdentities": {
    "/subscriptions/fe3bc2b7-22b0-4133-af8f-9439cb831236/resourceGroups/william-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id1": {
      "clientId": null,
      "principalId": null
    }
  }
}

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.

4 participants