From 4b0612e2dacdf8cd596a8423409fac8f5a6215c3 Mon Sep 17 00:00:00 2001 From: Jane Jung Date: Tue, 29 Sep 2026 10:51:25 +1300 Subject: [PATCH] [AKS] Enable Windows2022 to Windows2025 node pool upgrades Port the aks-preview Windows OS SKU update choices and Windows2025 create-time FIPS defaults into core CLI. Document explicit FIPS enablement for migration and cover the request behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 35c75d7c-9ed3-432d-9b78-0de1f30a3339 --- .../azure/cli/command_modules/acs/_help.py | 11 ++- .../azure/cli/command_modules/acs/_params.py | 2 +- .../acs/agentpool_decorator.py | 9 +- .../tests/latest/test_agentpool_decorator.py | 88 +++++++++++++++++++ .../acs/tests/latest/test_validators.py | 9 ++ 5 files changed, 116 insertions(+), 3 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/acs/_help.py b/src/azure-cli/azure/cli/command_modules/acs/_help.py index 714eb07304e..a1020fb25a4 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_help.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_help.py @@ -2191,6 +2191,7 @@ - name: --enable-fips-image type: bool short-summary: Use FIPS-enabled OS on agent nodes. + long-summary: Automatically enabled for Windows2025 node pools because Windows2025 requires a FIPS-enabled OS image. - name: --snapshot-id type: string short-summary: The source snapshot id used to create this nodepool. @@ -2401,7 +2402,10 @@ short-summary: Enable Managed DRANET on the node pool. - name: --os-sku type: string - short-summary: The os-sku of the agent node pool. + short-summary: The OS SKU of the agent node pool. + long-summary: | + Windows2022 node pools can be upgraded to Windows2025. Specify --enable-fips-image when upgrading a non-FIPS node pool. + Downgrading from Windows2025 to Windows2022 is not supported. - name: --enable-fips-image type: bool short-summary: Switch to use FIPS-enabled OS on agent nodes. @@ -2447,6 +2451,11 @@ text: az aks nodepool update --disable-cluster-autoscaler -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster - name: Update min-count or max-count for cluster autoscaler. text: az aks nodepool update --update-cluster-autoscaler --min-count 1 --max-count 10 -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster + - name: Upgrade a Windows2022 node pool to Windows2025 with a FIPS-enabled OS image. + text: | + az aks nodepool update --resource-group MyResourceGroup \\ + --cluster-name MyManagedCluster --name npwin \\ + --os-sku Windows2025 --enable-fips-image """ helps["aks nodepool upgrade"] = """ diff --git a/src/azure-cli/azure/cli/command_modules/acs/_params.py b/src/azure-cli/azure/cli/command_modules/acs/_params.py index 76f47a3e26a..b10f93f16d7 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_params.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_params.py @@ -202,7 +202,7 @@ node_mode_types = [CONST_NODEPOOL_MODE_SYSTEM, CONST_NODEPOOL_MODE_USER, CONST_NODEPOOL_MODE_GATEWAY] node_os_skus_create = [CONST_OS_SKU_AZURELINUX, CONST_OS_SKU_AZURELINUX3, CONST_OS_SKU_AZURECONTAINERLINUX, CONST_OS_SKU_UBUNTU, CONST_OS_SKU_CBLMARINER, CONST_OS_SKU_MARINER, CONST_OS_SKU_UBUNTU2204, CONST_OS_SKU_UBUNTU2404] node_os_skus = node_os_skus_create + [CONST_OS_SKU_WINDOWS2019, CONST_OS_SKU_WINDOWS2022, CONST_OS_SKU_WINDOWS2025] -node_os_skus_update = [CONST_OS_SKU_AZURELINUX, CONST_OS_SKU_AZURELINUX3, CONST_OS_SKU_AZURECONTAINERLINUX, CONST_OS_SKU_UBUNTU, CONST_OS_SKU_UBUNTU2204, CONST_OS_SKU_UBUNTU2404] +node_os_skus_update = [CONST_OS_SKU_AZURELINUX, CONST_OS_SKU_AZURELINUX3, CONST_OS_SKU_AZURECONTAINERLINUX, CONST_OS_SKU_UBUNTU, CONST_OS_SKU_UBUNTU2204, CONST_OS_SKU_UBUNTU2404, CONST_OS_SKU_WINDOWS2022, CONST_OS_SKU_WINDOWS2025] scale_down_modes = [CONST_SCALE_DOWN_MODE_DELETE, CONST_SCALE_DOWN_MODE_DEALLOCATE] pod_ip_allocation_modes = [CONST_NETWORK_POD_IP_ALLOCATION_MODE_DYNAMIC_INDIVIDUAL, CONST_NETWORK_POD_IP_ALLOCATION_MODE_STATIC_BLOCK] diff --git a/src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py index 47f37a758cf..bd75a445063 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/agentpool_decorator.py @@ -1482,7 +1482,7 @@ def get_enable_ultra_ssd(self) -> bool: # Mutable Fips now allows changes after create def get_enable_fips_image(self) -> bool: - """Obtain the value of enable_fips_image, default value is False. + """Obtain enable_fips_image, defaulting to False except for new Windows2025 pools. :return: bool """ @@ -1496,6 +1496,13 @@ def get_enable_fips_image(self) -> bool: self.agentpool.enable_fips is not None ): enable_fips_image = self.agentpool.enable_fips + elif self.get_os_sku() == CONST_OS_SKU_WINDOWS2025: + if self.get_disable_fips_image(): + raise ArgumentUsageError( + '"--disable-fips-image" cannot be used with "--os-sku Windows2025", ' + "which requires a FIPS-enabled OS image." + ) + enable_fips_image = True # Verify both flags have not been set if enable_fips_image and self.get_disable_fips_image(): diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_agentpool_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_agentpool_decorator.py index 6f1b3f76212..c0b4e6a13fd 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_agentpool_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_agentpool_decorator.py @@ -1431,6 +1431,41 @@ def common_get_enable_fips_image(self): # Update takes directly from flag value not from agentpool property self.assertEqual(ctx_2.get_enable_fips_image(), False) + def common_get_enable_fips_image_windows2025(self): + cases = [ + (DecoratorMode.CREATE, {"os_sku": CONST_OS_SKU_WINDOWS2025}, True), + (DecoratorMode.CREATE, {"os_sku": CONST_OS_SKU_WINDOWS2025, "enable_fips_image": True}, True), + (DecoratorMode.CREATE, {"os_sku": CONST_OS_SKU_WINDOWS2022}, False), + (DecoratorMode.CREATE, {"os_sku": "Ubuntu"}, False), + (DecoratorMode.CREATE, {"os_sku": "Ubuntu", "enable_fips_image": True}, True), + (DecoratorMode.UPDATE, {"os_sku": CONST_OS_SKU_WINDOWS2025}, False), + (DecoratorMode.UPDATE, {"os_sku": CONST_OS_SKU_WINDOWS2025, "enable_fips_image": True}, True), + ] + for decorator_mode, raw_parameters, expected in cases: + with self.subTest(decorator_mode=decorator_mode, raw_parameters=raw_parameters): + ctx = AKSAgentPoolContext( + self.cmd, + AKSAgentPoolParamDict(raw_parameters), + self.models, + decorator_mode, + self.agentpool_decorator_mode, + ) + self.assertEqual(ctx.get_enable_fips_image(), expected) + ctx.attach_agentpool( + self.create_initialized_agentpool_instance(os_sku=raw_parameters["os_sku"]) + ) + self.assertEqual(ctx.get_enable_fips_image(), expected) + + ctx = AKSAgentPoolContext( + self.cmd, + AKSAgentPoolParamDict({"os_sku": CONST_OS_SKU_WINDOWS2025, "disable_fips_image": True}), + self.models, + DecoratorMode.CREATE, + self.agentpool_decorator_mode, + ) + with self.assertRaisesRegex(ArgumentUsageError, "Windows2025"): + ctx.get_enable_fips_image() + def common_get_disable_fips_image(self): # default ctx_1 = AKSAgentPoolContext( @@ -2108,6 +2143,9 @@ def test_get_enable_ultra_ssd(self): def test_get_enable_fips_image(self): self.common_get_enable_fips_image() + def test_get_enable_fips_image_windows2025(self): + self.common_get_enable_fips_image_windows2025() + def test_get_disable_fips_image(self): self.common_get_disable_fips_image() @@ -2319,6 +2357,9 @@ def test_get_enable_ultra_ssd(self): def test_get_enable_fips_image(self): self.common_get_enable_fips_image() + def test_get_enable_fips_image_windows2025(self): + self.common_get_enable_fips_image_windows2025() + def test_get_enable_artifact_streaming(self): self.common_get_enable_artifact_streaming() @@ -4093,6 +4134,53 @@ def test_update_agentpool(self): headers={}, ) + def test_update_agentpool_windows2025(self): + for current_fips, enable_fips_image, expected_fips in [ + (False, True, True), + (True, False, True), + (False, False, False), + ]: + with self.subTest(current_fips=current_fips, enable_fips_image=enable_fips_image): + dec = AKSAgentPoolUpdateDecorator( + self.cmd, + self.client, + { + "resource_group_name": "test_rg_name", + "cluster_name": "test_cluster_name", + "nodepool_name": "test_nodepool_name", + "os_sku": CONST_OS_SKU_WINDOWS2025, + "enable_fips_image": enable_fips_image, + }, + self.resource_type, + self.agentpool_decorator_mode, + ) + self.client.get = Mock( + return_value=self.create_initialized_agentpool_instance( + nodepool_name="test_nodepool_name", + os_type="Windows", + os_sku=CONST_OS_SKU_WINDOWS2022, + enable_fips=current_fips, + ) + ) + with patch( + "azure.cli.command_modules.acs.agentpool_decorator.cf_agent_pools", + return_value=Mock(list=Mock(return_value=[])), + ): + agentpool = dec.update_agentpool_profile_default() + self.assertEqual(agentpool.os_sku, CONST_OS_SKU_WINDOWS2025) + self.assertEqual(agentpool.enable_fips, expected_fips) + with patch("azure.cli.command_modules.acs.agentpool_decorator.sdk_no_wait") as put_agentpool: + dec.update_agentpool(agentpool) + put_agentpool.assert_called_once_with( + False, + self.client.begin_create_or_update, + "test_rg_name", + "test_cluster_name", + "test_nodepool_name", + agentpool, + headers={}, + ) + def test_update_localdns_profile(self): self.common_update_localdns_profile() diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_validators.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_validators.py index 54956c0cc95..f84f4e107ad 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_validators.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_validators.py @@ -2442,5 +2442,14 @@ def test_managed_ip_counts_and_byo_ips_mutually_exclusive(self): validators.validate_nat_gateway_v2_params(self._ns(nat_gateway_outbound_ip_ids="/sub/ip", nat_gateway_outbound_ip_prefix_ids="/sub/pfx", outbound_type="managedNATGateway", nat_gateway_sku="StandardV2")) +class TestValidateOsSku(unittest.TestCase): + def test_nodepool_update_allows_windows2022_and_windows2025(self): + from azure.cli.command_modules.acs._params import node_os_skus_update + + self.assertIn("Windows2022", node_os_skus_update) + self.assertIn("Windows2025", node_os_skus_update) + self.assertNotIn("Windows2019", node_os_skus_update) + + if __name__ == "__main__": unittest.main()