Describe the bug
A VM Scale Set can persist either placement.includeZones or placement.excludeZones. When az vmss update is used to switch to the opposite filter, Azure CLI preserves the filter loaded from the existing VMSS and adds the newly requested filter.
For example:
- The VMSS currently has
placement.includeZones.
- The user runs
az vmss update with only --exclude-zones.
- Generic update retains the existing
includeZones value and adds excludeZones.
- The model passed to the final VMSS create-or-update operation contains both mutually exclusive properties.
The reverse transition—existing excludeZones followed by --include-zones—has the same defect.
Actual behavior
A deterministic test of the real update handler observed both properties at the final VMSSCreate(command_args=...) boundary:
{
"zone_placement_policy": "Auto",
"include_zones": ["1"],
"exclude_zones": ["2"],
}
The current validator rejects both flags when they are supplied in one invocation, but it cannot see an opposite filter already present in the VMSS state loaded by generic update.
Related command
az vmss update
Errors
A live Azure request was not performed during this investigation, so no local service error or --debug log was captured.
#33473 documents the Compute response when both properties are sent:
BadRequest: Both 'placement.includeZones' and 'placement.excludeZones' cannot be set.
Issue script & Debug output
This was verified deterministically against current dev at commit 8bead7f93f086629efb160d56c25f508156925bf.
The regression test supplies an existing VMSS response containing one camelCase placement filter, invokes the actual update_vmss handler with only the opposite snake_case argument, mocks the final VMSSCreate operation boundary, and inspects the complete command_args. Both transition directions are covered.
Test command:
PYTHONPATH=src/azure-cli:src/azure-cli-core:src/azure-cli-testsdk \
python -m unittest \
azure.cli.command_modules.vm.tests.latest.test_custom_vm_commands.TestVmCustom.test_update_vmss_switches_zone_placement_filter
Before changing production code:
FF
Ran 1 test
FAILED (failures=2)
The failures specifically showed the stale opposite filter still present in the model passed to VMSSCreate. After the two-line normalization fix, the same test passes in both directions; the canonical azdev run reports 1 passed, 2 subtests passed.
Proposed test/fix commit: ryo-whaletech@8f26c3f
No live --debug output is available; no Azure resources were changed for this source-level reproduction.
Expected behavior
Updating a VMSS from one zone placement filter to the other should not cause Azure CLI to send both mutually exclusive placement filters in the same request.
Environment Summary
Latest released version affected by source inspection
azure-cli 2.90.0
core 2.90.0
telemetry 1.1.0
Extensions: None
The azure-cli-2.90.0 source tag (dc50d475a00ded4a1a1980d4a10a9fbd9a750a81) contains the same stale-filter update logic. This is source inspection only; a packaged 2.90.0 live failure was not claimed.
Development verification
Repository: Azure/azure-cli
Branch: dev
Commit: 8bead7f93f086629efb160d56c25f508156925bf
Python: 3.13.11
OS: macOS 26.6.2 arm64
Live reproduction environment
Not performed. No usable authenticated disposable Azure VMSS environment was available, so no live transition or sanitized --debug capture is included.
Additional context
Describe the bug
A VM Scale Set can persist either
placement.includeZonesorplacement.excludeZones. Whenaz vmss updateis used to switch to the opposite filter, Azure CLI preserves the filter loaded from the existing VMSS and adds the newly requested filter.For example:
placement.includeZones.az vmss updatewith only--exclude-zones.includeZonesvalue and addsexcludeZones.The reverse transition—existing
excludeZonesfollowed by--include-zones—has the same defect.Actual behavior
A deterministic test of the real update handler observed both properties at the final
VMSSCreate(command_args=...)boundary:{ "zone_placement_policy": "Auto", "include_zones": ["1"], "exclude_zones": ["2"], }The current validator rejects both flags when they are supplied in one invocation, but it cannot see an opposite filter already present in the VMSS state loaded by generic update.
Related command
az vmss updateErrors
A live Azure request was not performed during this investigation, so no local service error or
--debuglog was captured.#33473 documents the Compute response when both properties are sent:
Issue script & Debug output
This was verified deterministically against current
devat commit8bead7f93f086629efb160d56c25f508156925bf.The regression test supplies an existing VMSS response containing one camelCase placement filter, invokes the actual
update_vmsshandler with only the opposite snake_case argument, mocks the finalVMSSCreateoperation boundary, and inspects the completecommand_args. Both transition directions are covered.Test command:
Before changing production code:
The failures specifically showed the stale opposite filter still present in the model passed to
VMSSCreate. After the two-line normalization fix, the same test passes in both directions; the canonicalazdevrun reports1 passed, 2 subtests passed.Proposed test/fix commit: ryo-whaletech@8f26c3f
No live
--debugoutput is available; no Azure resources were changed for this source-level reproduction.Expected behavior
Updating a VMSS from one zone placement filter to the other should not cause Azure CLI to send both mutually exclusive placement filters in the same request.
Environment Summary
Latest released version affected by source inspection
The
azure-cli-2.90.0source tag (dc50d475a00ded4a1a1980d4a10a9fbd9a750a81) contains the same stale-filter update logic. This is source inspection only; a packaged 2.90.0 live failure was not claimed.Development verification
Live reproduction environment
Not performed. No usable authenticated disposable Azure VMSS environment was available, so no live transition or sanitized
--debugcapture is included.Additional context
includeZonesandexcludeZonesare mutually exclusive.az vmss update: Add new param--zone-placement-policy,--include-zonesand--exclude-zones#33639 implements update support and same-invocation validation, but independently assigns the new fields and does not cover transitions from persisted opposite state.az vmss update: Add new param--zone-placement-policy,--include-zonesand--exclude-zones" #33915 is a closed, unmerged draft revert of the entire update feature; it is not a fix for this transition defect.