diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst index 882d73f54aa..7fedabf3c9d 100644 --- a/src/azure-cli/HISTORY.rst +++ b/src/azure-cli/HISTORY.rst @@ -66,6 +66,10 @@ Release History * Fix #31108, #32073: `az eventhubs`: Regex updated for commands with `--namespace-name` arguments (#32472) +**Key Vault** + +* `az keyvault create`: Fix `RequestDisallowedByPolicy` error by explicitly setting `enableSoftDelete` in the request body to satisfy Azure Policy checks (#33265) + **NetAppFiles** * `az netapfiles volume create/update`: Add paramter `--desired-ransomware-protection-state` to support advanced ransomware reports (#32828) diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/custom.py b/src/azure-cli/azure/cli/command_modules/keyvault/custom.py index 9cc5f7e3916..fb345d93677 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/custom.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/custom.py @@ -649,6 +649,9 @@ def create_vault(cmd, client, # pylint: disable=too-many-locals, too-many-state enabled_for_disk_encryption=enabled_for_disk_encryption, enabled_for_template_deployment=enabled_for_template_deployment, enable_rbac_authorization=enable_rbac_authorization, + # Intentionally include this field in the request body to satisfy + # Azure Policy checks that require soft delete to be explicitly set. + enable_soft_delete=True, enable_purge_protection=enable_purge_protection, soft_delete_retention_in_days=int(retention_days), public_network_access=public_network_access) diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py index a1df39f8ac5..3b47dd5322d 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py @@ -106,6 +106,54 @@ def test_parse_asn1_date(self): self.assertEqual(_asn1_to_iso8601("20170424163720Z"), expected) +class CreateVaultSoftDeleteTest(unittest.TestCase): + """Verify that create_vault explicitly sets enable_soft_delete=True in the request body + so that Azure Policy checks requiring the property to be present are satisfied.""" + + @mock.patch('azure.cli.command_modules.keyvault.custom._create_network_rule_set', return_value=None) + @mock.patch('azure.cli.core._profile.Profile') + @mock.patch('azure.cli.core.util.sdk_no_wait') + def test_create_vault_sets_enable_soft_delete_true(self, mock_sdk_no_wait, mock_profile, _mock_network): + from azure.cli.command_modules.keyvault.custom import create_vault + + mock_profile.return_value.get_subscription.return_value = { + 'tenantId': '00000000-0000-0000-0000-000000000000' + } + + # Build a minimal cmd mock that returns simple model classes + cmd = mock.MagicMock() + cmd.cli_ctx.data = {} + + # get_models returns a simple class that records its kwargs + def fake_get_models(name, **kwargs): + return type(name, (), {'__init__': lambda self, **kw: self.__dict__.update(kw)}) + + cmd.get_models.side_effect = fake_get_models + + # Client whose get() raises so vault-already-exists check is skipped + client = mock.MagicMock() + client.get.side_effect = HttpResponseError() + + create_vault( + cmd, client, + resource_group_name='rg', + vault_name='testvault', + location='eastus', + retention_days='90', + no_self_perms=True, + ) + + # sdk_no_wait is called with the VaultCreateOrUpdateParameters as 'parameters' + mock_sdk_no_wait.assert_called_once() + call_kwargs = mock_sdk_no_wait.call_args + parameters = call_kwargs.kwargs.get('parameters') or call_kwargs[1].get('parameters') + vault_properties = parameters.properties + + self.assertIs(vault_properties.enable_soft_delete, True, + "create_vault must explicitly set enable_soft_delete=True in the request body " + "to satisfy Azure Policy checks") + + class KeyVaultPrivateLinkResourceScenarioTest(ScenarioTest): @ResourceGroupPreparer(name_prefix='cli_test_keyvault_plr') @KeyVaultPreparer(name_prefix='cli-test-kv-plr-', location='eastus2')