Repository navigation
Could not associate Load Balancer in VMSS when using az cli 2.87.0+ versions #34133
Description
Activity
- addedbugThis issue requires a change to an existing behavior in the product in order to be resolved.This issue requires a change to an existing behavior in the product in order to be resolved.
on Sep 25, 2026 - addedcustomer-reportedIssues that are reported by GitHub users external to the Azure organization.Issues that are reported by GitHub users external to the Azure organization.Computeaz vm/vmss/image/disk/snapshotaz vm/vmss/image/disk/snapshot
on Sep 25, 2026 Thank you for opening this issue, we will look into it.
- addedAuto-AssignAuto assign by botAuto assign by botAzure CLI TeamThe command of the issue is owned by Azure CLI teamThe command of the issue is owned by Azure CLI teamquestionThe issue doesn't require a change to the product in order to be resolved. Most issues start as thatThe issue doesn't require a change to the product in order to be resolved. Most issues start as that
on Sep 25, 2026 x-engineering-agent commented
on Sep 25, 2026 ContributorMore actionsBug analysis
Triage: sufficient information. The exact command, error/stack trace, expected attachment, and version/context are supplied. The report identifies CLI 2.75.0 as working and 2.87.0+ as failing, with an actual CLI/core 2.90.0 environment on Linux/Python 3.14.6. No additional requirements are needed to investigate this client-side failure.
Routing: core
vmmodule inAzure/azure-cli, target branchdev; no extension tracker is needed. Thevmsscommand group is registered in this module, despite its different command-group name.Observed and expected behavior
The failing operation is
az vmss update --resource-group ... --name ... --addtargetingvirtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0].loadBalancerBackendAddressPools, withid=<backend-pool-resource-id>. The captured trace reachesGenericUpdateCommandOperation.handler->add_properties->_find_property->_update_instance, then raisesCouldn't find 'loadBalancerBackendAddressPools'. The available options list contains only the keys present in that IP-configuration dictionary.Adding the first backend-pool association should initialize the optional collection and submit the update, rather than requiring the collection to already exist. The companion
az vmss update --removeon the same property is part of the cutover workflow and must continue to detach pools correctly. The supplied traceback proves the--addfailure; it does not independently establish a separate--removefailure.Governed current-source evidence and likely cause
Inspected
devat7bf31a4fd49c252209732a8b0cf874ecaccdf06a:vm/commands.pylines 276-297 registersvmss updateas a legacy generic-update command usingget_vmss_modified_by_aazandupdate_vmss.custom.pylines 4314-4339 returns the AAZ show result, whileVMSSShow._outputdeserializes it into a client-flattened dictionary. The update getter does not restore omitted optional backend-pool properties.arm.add_propertiesfirst looks up the complete collection path. Its existing initialization path only runs when lookup returnsNone. However,_update_instance/_find_propertyuseinstance[part]for dictionaries; a missing final key raises before initialization. An unset model attribute or an explicit dictionaryNonecan therefore work where an omitted dictionary key cannot. This directly explains the reported stack and is consistent with a representation-boundary regression.- The current generated response schema still defines
loadBalancerBackendAddressPools. The handwritten converter already maps it toload_balancer_backend_address_poolswhen present. This is not evidence that the service removed the field. The failure happens before the generic handler calls the setter, so changing onlyupdate_vmssor that converter cannot fix this traceback.
Concrete fix requirements
- Restore absent-terminal-collection handling at the handwritten generic-update boundary: when
--addaddresses an omitted final dictionary collection key, resolve and validate its existing parent first, then initialize the requested list. Retain support for explicitNoneand empty lists, and append to populated lists without replacing existing pool IDs. Keep the change confined to the relevant shared helper and VMSS integration/tests. - Do not make
_find_propertyglobally returnNonefor unknown paths or swallow arbitraryKeyErrors. Missing intermediate objects, invalid indexes, non-unique filters, and non-list targets must still fail with useful errors. Preserve existing model-object behavior, generic argument ordering,--set, and unrelated VMSS/instance/no-wait behavior. - Exercise the companion removal through the complete request/response path.
remove_propertiesdeletes dictionary keys but clears model-list attributes to[]; verify that the resulting VMSS request actually clears the association rather than treating omission as preservation. Do not redefine all missing-property removals as successful no-ops. A remove/re-add cutover must not lose unrelated IP-configuration fields. - This evidence points to handwritten client compatibility logic, not a missing generated schema member. Do not hand-edit
vm/aaz/latest/vmss/or regenerate unrelated commands. If implementation uncovers a genuine durable-model/specification defect, use the mandatory source-first Codegen protocol below.
Focused regression and recording recommendations
- Extend
src/azure-cli-core/azure/cli/core/tests/test_generic_update.pywith a dictionary-shaped VMSS fixture whose final backend-pool collection is omitted. Cover absent, explicitNone, empty, and populated collections; ordered remove/add; preserving existing entries; and the negative path/index/type cases above. Existing coverage exercisesemptyProp=None/empty lists and missing-property removal errors, which must remain protected. - Add a VMSS adapter/command regression in
src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py: mock the AAZ getter's flattened IP-configuration dictionary, apply the reported generic update, and capture the setter arguments to confirm the backend-pool ID survives camelCase-to-snake_case conversion. Include multiple IP configurations and an unrelated-field preservation assertion. - Extend
VMSSUpdateTestsinsrc/azure-cli/azure/cli/command_modules/vm/tests/latest/test_vm_commands.pywith a re-recordable no-LB -> attach -> detach -> reattach scenario. Use a supported Uniform VMSS and Standard load balancer, test the supplied full backend-pool resource-ID form (including a separate LB resource group), and check both the outgoing request and a freshvmss showafter each change. Resource preparers must supply resource names/IDs; keep recordings sanitized and replayable. Do not rely only on creating a VMSS that already has a load balancer, because that starts with a populated collection and misses this failure. - During implementation, run the repository's focused generic-update and VM scenario tests plus scoped style/linter checks; re-record only affected scenarios using the approved workflow. No tests, implementation, or Codegen were executed during this triage. The exact first-bad release and live service behavior have not been independently reproduced here.
Mandatory Codegen execution protocol
Before editing implementation files, determine whether the affected
vmcommand is AAZ-generated. Files underaaz/<profile>/are generated output and must never be patched directly, including by an AI agent. Check outAzure/aazbesideAzure/azure-rest-api-specs,Azure/aaz-dev-tools, and the downstream repository. API-schema defects start in the specification; command naming, grouping, arguments, API-version selection, help, and examples belong in the durableAzure/aazcommand model; non-modelable client behavior belongs in a handwritten subclass or wrapper incustom.py, registered fromcommands.py. X Engineering Agent creates and promotes the corresponding durableAzure/aazsource pull request before it promotes downstream generated output.Follow the Azure CLI repository's Codegen workflow and the aaz-dev setup documentation. Set up the checked-out repositories with
azdev setup. Usegenerateonly when importing or redesigning command models from Swagger/TypeSpec. For an existing module whose durableAzure/aazmodel has been updated, render that model withregenerate:aaz-dev cli regenerate --name vm --cli-path <azure-cli> # New/imported command model only: aaz-dev cli generate --spec <specification-name> --module vm
You MUST actually run the generator; do not merely describe it or imitate its output. If the AAZ/specification checkout, local source change, credentials, or generator is unavailable, stop and report the blocker instead of editing generated files. Inspect
_aaz_infoprovenance and the complete regenerated diff, then run focusedazdev style,azdev linter, andazdev testvalidation. For an extension, also update its version andHISTORY.rst, preserveazext_metadata.jsoncompatibility, and let release automation updatesrc/index.json.PR title & description format (required)
This repo enforces a PR format (guide). Please author the PR exactly as follows or CI's Check the Format of Pull Request Title and Content will fail.
Use this EXACT PR title (copy verbatim, do not reword):
[VM] Fix #34133: `az vmss update`: Restore adding load balancer backend pools when the property is absentKeep the backticks around the command and the
Fix #34133:prefix. You may only adjust the wording after the command (the final summary) if the fix changes; the[VM]prefix, issue link, and backticked command must stay.Description — follow the PR template and fill in:
- Link the issue — start the Description with a closing keyword so the PR auto-links and closes it:
Fixes Azure/azure-cli#34133. - Related command — the
az ...command this affects. - Description (mandatory) — why the bug happens, what you changed, and the resulting behavior.
- Testing Guide — example command(s) showing the fix works.
- History Notes — leave the title to drive the history note, or add extra lines in the same format (component in brackets + the command in backticks), e.g.
[VM] `az <command>`: <note>. - Keep the template checklist and tick the items you've satisfied.
- removedquestionThe issue doesn't require a change to the product in order to be resolved. Most issues start as thatThe issue doesn't require a change to the product in order to be resolved. Most issues start as that
on Sep 25, 2026 Hello! Any updates on this issue?
We have fixed the issue, expect to be released in Azure CLI 2.92.0.
For now, please use alternative way:
az vmss update --set
Describe the bug
We are observing an issue after upgrading Azure CLI from 2.75.0 to 2.87.0 and higher versions.
Our Azure DevOps BlueGreen deployment pipeline performs traffic cutover between VM Scale Sets by updating Azure Load Balancer backend pool associations. The deployment works successfully when using Azure CLI 2.75.0, but consistently fails when using Azure CLI 2.87.0+.
Related command
Commands used:
az vmss update --resource-group "${RGNAME_APP}" --name "${VMSSNAME}" --add virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0].loadBalancerBackendAddressPools id="/subscriptions/${SUBID}/resourceGroups/${RGNAME_LB}/providers/Microsoft.Network/loadBalancers/${LBNAME}/backendAddressPools/${VMSSNAME}-pool"
az vmss update --resource-group "${ACTIVE_RGNAME}" --name "${VMSSNAME_ACTIVE}" --remove virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0].loadBalancerBackendAddressPools
Errors
Errors observed:
ERROR: Couldn't find 'loadBalancerBackendAddressPools' in
'virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0]'
Issue script & Debug output
With debug flag:
DEBUG: cli.azure.cli.core.azclierror: Traceback (most recent call last):
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/arm.py", line 742, in _update_instance
return instance[part]
~~~~~~~~^^^^^^
KeyError: 'loadBalancerBackendAddressPools'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/opt/az/lib/python3.14/site-packages/knack/cli.py", line 233, in invoke
cmd_result = self.invocation.execute(args)
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/init.py", line 677, in execute
raise ex
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/init.py", line 820, in _run_jobs_serially
results.append(self._run_job(expanded_arg, cmd_copy))
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/init.py", line 789, in _run_job
result = cmd_copy(params)
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/init.py", line 335, in call
return self.handler(*args, **kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/command_operation.py", line 214, in handler
add_properties(instance, arg_values, force_string)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/arm.py", line 576, in add_properties
list_to_add_to = _find_property(instance, list_attribute_path)
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/arm.py", line 756, in _find_property
instance = _update_instance(instance, part, path)
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/arm.py", line 751, in _update_instance
throw_and_show_options(instance, part, path)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "/opt/az/lib/python3.14/site-packages/azure/cli/core/commands/arm.py", line 661, in throw_and_show_options
raise CLIError(error_message)
knack.util.CLIError: Couldn't find 'loadBalancerBackendAddressPools' in 'virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0]'. Available options:['applicationSecurityGroups', 'name', 'privateIPAddressVersion', 'subnet']
ERROR: cli.azure.cli.core.azclierror: Couldn't find 'loadBalancerBackendAddressPools' in 'virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0]'. Available options: ['applicationSecurityGroups', 'name', 'privateIPAddressVersion', 'subnet']
ERROR: az_command_data_logger: Couldn't find 'loadBalancerBackendAddressPools' in 'virtualMachineProfile.networkProfile.networkInterfaceConfigurations[0].ipConfigurations[0]'. Available options: ['applicationSecurityGroups', 'name', 'privateIPAddressVersion', 'subnet']
DEBUG: cli.knack.cli: Event: Cli.PostExecute [<function AzCliLogging.deinit_cmd_metadata_logging at 0x7f0c44258670>]
Expected behavior
Expected behavior is for the load balancer to be attached successfully to the VMSS
Environment Summary
azure-cli 2.90.0
core 2.90.0
telemetry 1.1.0
Extensions:
azure-devops 1.0.4
Dependencies:
msal 1.36.0
azure-mgmt-resource 24.0.0
Python location '/opt/az/bin/python3'
Config directory '/agent/_work/_temp/.azclitask-2cLsE4'
Extensions directory '/opt/az/azcliextensions'
Python (Linux) 3.14.6 (main, Aug 26 2026, 01:26:36) [GCC 11.4.0]
Additional context
No response