From 2bc469a9ffb3a85a5af0f1294f2fb646d05a6952 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:55:37 +0000 Subject: [PATCH 1/2] Initial plan From 815239d16d0d13757dcd7201d3fcefc5673b4e7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 15:04:06 +0000 Subject: [PATCH 2/2] [VM] fix `az vmss extension set` not updating existing extension --- .../azure/cli/command_modules/vm/custom.py | 2 +- .../tests/latest/test_custom_vm_commands.py | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/azure-cli/azure/cli/command_modules/vm/custom.py b/src/azure-cli/azure/cli/command_modules/vm/custom.py index 0dcef36551a..5f4633ef42d 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/custom.py +++ b/src/azure-cli/azure/cli/command_modules/vm/custom.py @@ -5328,7 +5328,7 @@ def set_vmss_extension(cmd, resource_group_name, vmss_name, extension_name, publ if extensions: extension_profile['extensions'] = \ [x for x in extensions if - x.get('type_properties_type', '').lower() != extension_name.lower() or + x.get('type', '').lower() != extension_name.lower() or x.get('publisher', '').lower() != publisher.lower()] ext = { diff --git a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py index 0291e22490b..1a2ddd7bc93 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py +++ b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py @@ -196,6 +196,66 @@ class ErrorToExitCommandEarly(Exception): get_sdk_mock.assert_called_with(cli_ctx_mock, ResourceType.DATA_STORAGE_BLOB, '_blob_client#BlobClient') +class TestSetVmssExtension(unittest.TestCase): + + @mock.patch('azure.cli.command_modules.vm.custom.get_vmss_by_aaz') + @mock.patch('azure.cli.command_modules.vm.custom._normalize_extension_version') + @mock.patch('azure.cli.command_modules.vm.operations.vmss.VMSSCreate') + def test_set_vmss_extension_updates_existing(self, mock_vmss_create, mock_normalize_version, + mock_get_vmss): + """Test that set_vmss_extension replaces an existing extension instead of duplicating it.""" + from azure.cli.command_modules.vm.custom import set_vmss_extension + + mock_normalize_version.return_value = '2.1' + mock_vmss_create_instance = mock.MagicMock() + mock_vmss_create.return_value = mock_vmss_create_instance + + # Simulate a VMSS with an existing CustomScript extension (as returned by get_vmss_by_aaz) + mock_get_vmss.return_value = { + 'location': 'eastus', + 'virtualMachineProfile': { + 'storageProfile': {'imageReference': {}}, + 'extensionProfile': { + 'extensions': [ + { + 'name': 'myScript', + 'type': 'CustomScript', + 'publisher': 'Microsoft.Azure.Extensions', + 'typeHandlerVersion': '2.0', + 'autoUpgradeMinorVersion': True, + 'settings': {'commandToExecute': 'echo old'}, + } + ] + } + } + } + + cmd = mock.MagicMock() + cmd.cli_ctx = DummyCli() + + set_vmss_extension( + cmd, + resource_group_name='myRG', + vmss_name='myVMSS', + extension_name='CustomScript', + publisher='Microsoft.Azure.Extensions', + version='2.1', + settings={'commandToExecute': 'echo new'}, + extension_instance_name='myScript', + ) + + # Verify VMSSCreate was called with the updated extension list + call_args = mock_vmss_create_instance.call_args + command_args = call_args[1]['command_args'] + extensions = command_args['virtual_machine_profile']['extension_profile']['extensions'] + + # There should be exactly one extension (the old one replaced, not duplicated) + self.assertEqual(len(extensions), 1) + self.assertEqual(extensions[0]['name'], 'myScript') + self.assertEqual(extensions[0]['type'], 'CustomScript') + self.assertEqual(extensions[0]['settings'], {'commandToExecute': 'echo new'}) + + class FakedVM: # pylint: disable=too-few-public-methods def __init__(self, nics=None, disks=None, os_disk=None): self.network_profile = NetworkProfile(network_interfaces=nics)