diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 68fbadac67a..c18eedbe26e 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -10,14 +10,17 @@ If there is no rush to release a new version, please just add a description of t To release a new version, please select a new version number (usually plus 1 to last patch version, X.Y.Z -> Major.Minor.Patch, more details in `\doc `_), and then add a new section named as the new version number in this file, the content should include the new modifications and everything from the *Pending* section. Finally, update the `VERSION` variable in `setup.py` with this new version number. Pending + +19.0.0b10 +++++++ * Vendor new SDK and bump API version to 2025-08-02-preview. +* `az aks update`: Fix `--azure-keyvault-kms-key-vault-network-access` parameter not being correctly applied during cluster creation and updates. 19.0.0b9 +++++++ * `az aks create --enable-hosted-system`: no longer provision default system node pool when creating an automatic cluster with hosted system enabled. * `az aks machine update`: Add support for updating machine tags, node taints and node labels. -* Fix `az aks bastion` subshell defaulting to cmd on Windows when invoked from PowerShell by implementing grandparent process detection to identify the actual user shell. +* Fix `az aks bastion` subshell defaulting to cmd on Windows when invoked from PowerShell by implementing grandparent process detection to identify the actual user shell. 19.0.0b8 +++++++ diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index 462399d0233..559c1914944 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -3998,6 +3998,7 @@ def set_up_kms_pmk_and_cmk(self, mc: ManagedCluster) -> ManagedCluster: mc.security_profile.azure_key_vault_kms = self.models.AzureKeyVaultKms( enabled=True, key_id=key_id, + key_vault_network_access=self.context.get_azure_keyvault_kms_key_vault_network_access(), key_vault_resource_id=self.context.get_azure_keyvault_kms_key_vault_resource_id(), ) @@ -6039,6 +6040,7 @@ def update_kms_pmk_cmk(self, mc: ManagedCluster) -> ManagedCluster: mc.security_profile.azure_key_vault_kms = self.models.AzureKeyVaultKms( enabled=True, key_id=key_id, + key_vault_network_access=self.context.get_azure_keyvault_kms_key_vault_network_access(), key_vault_resource_id=self.context.get_azure_keyvault_kms_key_vault_resource_id(), ) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index fa31e569dfa..7ebf04a3afb 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -12230,6 +12230,7 @@ def test_aks_create_with_kms_pmk_and_update_cmk( update_cmd = ( "aks update --resource-group={resource_group} --name={name} " "--enable-azure-keyvault-kms --azure-keyvault-kms-key-id={key_id} " + "--azure-keyvault-kms-key-vault-network-access=Public " "--azure-keyvault-kms-key-vault-resource-id={kv_resource_id} " "-o json" ) @@ -12373,6 +12374,175 @@ def test_aks_create_with_kms_pmk_and_cmk_and_disable_cmk( "aks create --resource-group={resource_group} --name={name} " "--assign-identity {identity_id} " "--enable-azure-keyvault-kms --azure-keyvault-kms-key-id={key_id} " + "--azure-keyvault-kms-key-vault-network-access=Public " + "--azure-keyvault-kms-key-vault-resource-id={kv_resource_id} " + "--kms-infrastructure-encryption=Enabled " + "--kubernetes-version={k8s_version} " + "--ssh-key-value={ssh_key_value} " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/KMSPMKPreview " + "-o json" + ) + self.cmd( + create_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + self.check("securityProfile.azureKeyVaultKms.enabled", True), + self.check("securityProfile.azureKeyVaultKms.keyId", key_id_versionless), + self.check( + "securityProfile.kubernetesResourceObjectEncryptionProfile.infrastructureEncryption", + "Enabled" + ), + ], + ) + + # disable CMK + update_cmd = ( + "aks update --resource-group={resource_group} --name={name} " + "--disable-azure-keyvault-kms " + "-o json" + ) + self.cmd( + update_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + self.check("securityProfile.azureKeyVaultKms.enabled", False), + self.check( + "securityProfile.kubernetesResourceObjectEncryptionProfile.infrastructureEncryption", + "Enabled" + ), + ], + ) + + # delete + cmd = ( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait" + ) + self.cmd( + cmd, + checks=[ + self.is_empty(), + ], + ) + + @live_only() + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer( + random_name_length=17, + name_prefix="clitest", + location="eastus2euap", + ) + def test_aks_create_with_kms_pmk_and_cmk_and_disable_cmk_private( + self, resource_group, resource_group_location + ): + """Test PMK-enabled cluster creation with versionless key ID""" + aks_name = self.create_random_name("cliakstest", 16) + kv_name = self.create_random_name("cliakstestkv", 16) + identity_name = self.create_random_name("cliakstestidentity", 24) + k8s_version = self._get_version_in_range(location=resource_group_location, min_version="1.33.0", max_version="1.34.0") + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "kv_name": kv_name, + "identity_name": identity_name, + "ssh_key_value": self.generate_ssh_keys(), + "k8s_version": k8s_version, + } + ) + + # create user-assigned identity + identity_id = self._get_user_assigned_identity(resource_group) + identity_object_id = self._get_principal_id_of_user_assigned_identity(identity_id) + assert identity_id is not None + assert identity_object_id is not None + self.kwargs.update( + { + "identity_id": identity_id, + "identity_object_id": identity_object_id, + } + ) + + # create key vault and key + create_keyvault = ( + "keyvault create --resource-group={resource_group} --name={kv_name} --enable-rbac-authorization=false --no-self-perms -o json" + ) + self.cmd( + create_keyvault, + checks=[self.check("properties.provisioningState", "Succeeded")], + ) + + # set access policy for test identity + test_identity_object_id = self._get_test_identity_object_id() + test_identity_access_policy = 'keyvault set-policy --resource-group={resource_group} --name={kv_name} ' \ + '--key-permissions all --object-id ' + test_identity_object_id + self.cmd(test_identity_access_policy, checks=[ + self.check('properties.provisioningState', 'Succeeded') + ]) + + # create key and extract versionless key ID + create_key = "keyvault key create -n kms --vault-name {kv_name} -o json" + key = self.cmd( + create_key, checks=[self.check("attributes.enabled", True)] + ).get_output_in_json() + key_id_versioned = key["key"]["kid"] + # Extract versionless key ID (remove version part) + # Format: https://{vault}.vault.azure.net/keys/{name}/{version} + # We want: https://{vault}.vault.azure.net/keys/{name} + key_id_parts = key_id_versioned.rsplit('/', 1) + key_id_versionless = key_id_parts[0] + + assert key_id_versionless is not None + self.kwargs.update( + { + "key_id": key_id_versionless, + } + ) + + # Get key vault resource ID + kv_resource_id = self.cmd( + "keyvault show --resource-group={resource_group} --name={kv_name} --query id -o tsv" + ).output.strip() + self.kwargs.update( + { + "kv_resource_id": kv_resource_id, + } + ) + + # assign access policy + set_policy = ( + "keyvault set-policy --resource-group={resource_group} --name={kv_name} " + "--object-id {identity_object_id} --key-permissions encrypt decrypt -o json" + ) + self.cmd( + set_policy, checks=[self.check("properties.provisioningState", "Succeeded")] + ) + + # update key vault to disable public network access and enable trusted service + disable_public_network_access = ( + "keyvault update --resource-group={resource_group} --name={kv_name} " + "--public-network-access Disabled " + "--bypass AzureServices --default-action Deny " + "-o json" + ) + self.cmd( + disable_public_network_access, + checks=[self.check("properties.provisioningState", "Succeeded")], + ) + + # add "Key Vault Reader" role to the identity + create_role_assignment = ( + "role assignment create --role 21090545-7ca7-4776-b22c-e363652d74d2 " + '--assignee-object-id {identity_object_id} --assignee-principal-type "ServicePrincipal" ' + "--scope {kv_resource_id}" + ) + self.cmd(create_role_assignment) + + # create cluster with PMK enabled and versionless key ID + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} " + "--assign-identity {identity_id} " + "--enable-azure-keyvault-kms --azure-keyvault-kms-key-id={key_id} " + "--azure-keyvault-kms-key-vault-network-access=Private " "--azure-keyvault-kms-key-vault-resource-id={kv_resource_id} " "--kms-infrastructure-encryption=Enabled " "--kubernetes-version={k8s_version} " diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py index e23f6eb12ad..8000737d87a 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py @@ -5362,6 +5362,53 @@ def test_set_up_azure_keyvault_kms(self): self.assertEqual(dec_mc_3, ground_truth_mc_3) + # Test 4: Verify network access parameter is correctly passed through for Public access + dec_4 = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id_1, + "azure_keyvault_kms_key_vault_network_access": "Public", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_4 = self.models.ManagedCluster(location="test_location") + dec_4.context.attach_mc(mc_4) + dec_mc_4 = dec_4.set_up_azure_keyvault_kms(mc_4) + + # Verify that the network access is set to Public (not the default) + self.assertIsNotNone(dec_mc_4.security_profile) + self.assertIsNotNone(dec_mc_4.security_profile.azure_key_vault_kms) + self.assertEqual( + dec_mc_4.security_profile.azure_key_vault_kms.key_vault_network_access, + "Public" + ) + + # Test 5: Verify network access parameter is correctly passed through for Private access + dec_5 = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id_1, + "azure_keyvault_kms_key_vault_network_access": "Private", + "azure_keyvault_kms_key_vault_resource_id": "/subscriptions/8ecadfc9-d1a3-4ea4-b844-0d9f87e4d7c8/resourceGroups/foo/providers/Microsoft.KeyVault/vaults/foo", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_5 = self.models.ManagedCluster(location="test_location") + dec_5.context.attach_mc(mc_5) + dec_mc_5 = dec_5.set_up_azure_keyvault_kms(mc_5) + + # Verify that the network access is set to Private + self.assertIsNotNone(dec_mc_5.security_profile) + self.assertIsNotNone(dec_mc_5.security_profile.azure_key_vault_kms) + self.assertEqual( + dec_mc_5.security_profile.azure_key_vault_kms.key_vault_network_access, + "Private" + ) + def test_set_up_kms_pmk_and_cmk(self): # test default (no infrastructure encryption) dec_1 = AKSPreviewManagedClusterCreateDecorator( @@ -8348,6 +8395,65 @@ def test_update_azure_keyvault_kms(self): ) self.assertEqual(dec_mc_2, ground_truth_mc_2) + # Test 3: Verify network access parameter is correctly passed through during update for Public access + dec_3 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id_1, + "azure_keyvault_kms_key_vault_network_access": "Public", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_3 = self.models.ManagedCluster(location="test_location") + dec_3.context.attach_mc(mc_3) + dec_mc_3 = dec_3.update_azure_keyvault_kms(mc_3) + + # Verify that the network access is set to Public during update + self.assertIsNotNone(dec_mc_3.security_profile) + self.assertIsNotNone(dec_mc_3.security_profile.azure_key_vault_kms) + self.assertEqual( + dec_mc_3.security_profile.azure_key_vault_kms.key_vault_network_access, + "Public" + ) + + # Test 4: Verify updating from one network access type to another works correctly + dec_4 = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id_1, + "azure_keyvault_kms_key_vault_network_access": "Private", + "azure_keyvault_kms_key_vault_resource_id": "/subscriptions/8ecadfc9-d1a3-4ea4-b844-0d9f87e4d7c8/resourceGroups/foo/providers/Microsoft.KeyVault/vaults/foo", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + + # Start with a cluster that has Public access + existing_security_profile = self.models.ManagedClusterSecurityProfile( + azure_key_vault_kms=self.models.AzureKeyVaultKms( + enabled=True, + key_id=key_id_1, + key_vault_network_access="Public", + ) + ) + mc_4 = self.models.ManagedCluster( + location="test_location", + security_profile=existing_security_profile, + ) + dec_4.context.attach_mc(mc_4) + dec_mc_4 = dec_4.update_azure_keyvault_kms(mc_4) + + # Verify that the network access was updated from Public to Private + self.assertIsNotNone(dec_mc_4.security_profile) + self.assertIsNotNone(dec_mc_4.security_profile.azure_key_vault_kms) + self.assertEqual( + dec_mc_4.security_profile.azure_key_vault_kms.key_vault_network_access, + "Private" + ) + dec_5 = AKSPreviewManagedClusterUpdateDecorator( self.cmd, self.client, @@ -8582,7 +8688,7 @@ def test_update_kms_pmk_cmk(self): ground_truth_azure_key_vault_kms_6 = self.models.AzureKeyVaultKms( enabled=True, key_id="https://test-keyvault.vault.azure.net/keys/test-key", - key_vault_network_access="Public", + key_vault_network_access=None, key_vault_resource_id="/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-keyvault", ) ground_truth_kube_resource_encryption_profile_6 = self.models.KubernetesResourceObjectEncryptionProfile( @@ -8623,7 +8729,6 @@ def test_update_kms_pmk_cmk(self): ground_truth_azure_key_vault_kms_7 = self.models.AzureKeyVaultKms( enabled=True, key_id="https://test-keyvault.vault.azure.net/keys/test-key", - key_vault_network_access="Public", key_vault_resource_id="/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-keyvault", ) ground_truth_kube_resource_encryption_profile_7 = self.models.KubernetesResourceObjectEncryptionProfile( @@ -8672,7 +8777,6 @@ def test_update_kms_pmk_cmk(self): azure_key_vault_kms=self.models.AzureKeyVaultKms( enabled=True, key_id="https://test-keyvault.vault.azure.net/keys/test-key", - key_vault_network_access="Public", key_vault_resource_id="/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-keyvault", ), kubernetes_resource_object_encryption_profile=self.models.KubernetesResourceObjectEncryptionProfile( @@ -8710,6 +8814,7 @@ def test_update_kms_pmk_cmk(self): "kms_infrastructure_encryption": "Enabled", "enable_azure_keyvault_kms": True, "azure_keyvault_kms_key_id": "https://test-keyvault.vault.azure.net/keys/test-key", + "azure_keyvault_kms_key_vault_network_access": "Public", "azure_keyvault_kms_key_vault_resource_id": "/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-keyvault", }, CUSTOM_MGMT_AKS_PREVIEW, @@ -12630,5 +12735,126 @@ def test_enable_azure_monitor_app_monitoring_preserves_opentelemetry(self): self.assertTrue(dec_mc.azure_monitor_profile.app_monitoring.open_telemetry_logs.enabled) self.assertEqual(dec_mc.azure_monitor_profile.app_monitoring.open_telemetry_logs.port, 8081) + def test_azure_keyvault_kms_network_access_parameter_fix(self): + """Test that azure_keyvault_kms_key_vault_network_access parameter is correctly passed through. + + This test verifies the fix for the issue where --azure-keyvault-kms-key-vault-network-access + was always being set to "Public" regardless of user input. + """ + key_id = "https://fakekeyvault.vault.azure.net/secrets/fakekeyname/fakekeyversion" + + # Test CREATE scenario with Private network access + dec_create_private = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id, + "azure_keyvault_kms_key_vault_network_access": "Private", + "azure_keyvault_kms_key_vault_resource_id": "/subscriptions/test/resourceGroups/test/providers/Microsoft.KeyVault/vaults/test", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_create_private = self.models.ManagedCluster(location="test_location") + dec_create_private.context.attach_mc(mc_create_private) + result_create_private = dec_create_private.set_up_azure_keyvault_kms(mc_create_private) + + # Verify Private network access is correctly set during CREATE + self.assertEqual( + result_create_private.security_profile.azure_key_vault_kms.key_vault_network_access, + "Private" + ) + + # Test CREATE scenario with Public network access + dec_create_public = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id, + "azure_keyvault_kms_key_vault_network_access": "Public", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + mc_create_public = self.models.ManagedCluster(location="test_location") + dec_create_public.context.attach_mc(mc_create_public) + result_create_public = dec_create_public.set_up_azure_keyvault_kms(mc_create_public) + + # Verify Public network access is correctly set during CREATE + self.assertEqual( + result_create_public.security_profile.azure_key_vault_kms.key_vault_network_access, + "Public" + ) + + # Test UPDATE scenario - changing from Public to Private + dec_update_to_private = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id, + "azure_keyvault_kms_key_vault_network_access": "Private", + "azure_keyvault_kms_key_vault_resource_id": "/subscriptions/test/resourceGroups/test/providers/Microsoft.KeyVault/vaults/test", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + + # Start with existing cluster that has Public access + existing_kms_profile = self.models.AzureKeyVaultKms( + enabled=True, + key_id=key_id, + key_vault_network_access="Public", + ) + existing_security_profile = self.models.ManagedClusterSecurityProfile( + azure_key_vault_kms=existing_kms_profile + ) + mc_update_to_private = self.models.ManagedCluster( + location="test_location", + security_profile=existing_security_profile, + ) + dec_update_to_private.context.attach_mc(mc_update_to_private) + result_update_to_private = dec_update_to_private.update_azure_keyvault_kms(mc_update_to_private) + + # Verify network access was updated from Public to Private + self.assertEqual( + result_update_to_private.security_profile.azure_key_vault_kms.key_vault_network_access, + "Private" + ) + + # Test UPDATE scenario - changing from Private to Public + dec_update_to_public = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_azure_keyvault_kms": True, + "azure_keyvault_kms_key_id": key_id, + "azure_keyvault_kms_key_vault_network_access": "Public", + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + + # Start with existing cluster that has Private access + existing_kms_profile_private = self.models.AzureKeyVaultKms( + enabled=True, + key_id=key_id, + key_vault_network_access="Private", + key_vault_resource_id="/subscriptions/test/resourceGroups/test/providers/Microsoft.KeyVault/vaults/test", + ) + existing_security_profile_private = self.models.ManagedClusterSecurityProfile( + azure_key_vault_kms=existing_kms_profile_private + ) + mc_update_to_public = self.models.ManagedCluster( + location="test_location", + security_profile=existing_security_profile_private, + ) + dec_update_to_public.context.attach_mc(mc_update_to_public) + result_update_to_public = dec_update_to_public.update_azure_keyvault_kms(mc_update_to_public) + + # Verify network access was updated from Private to Public + self.assertEqual( + result_update_to_public.security_profile.azure_key_vault_kms.key_vault_network_access, + "Public" + ) + if __name__ == "__main__": unittest.main() diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 7f3497300cc..f1e3f7a293c 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "19.0.0b9" +VERSION = "19.0.0b10" CLASSIFIERS = [ "Development Status :: 4 - Beta",