From 45ab83f55b0dd490167a29ee8cb039ada313397e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:35:30 +0000 Subject: [PATCH 1/2] Initial plan From 3666d854f0ef26f9c3b68660fd30d37b08776662 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:52:48 +0000 Subject: [PATCH 2/2] Fix az sql server update failing with Invalid value given for parameter RetentionDays When the Azure SQL API returns retention_days=-1 for legacy servers (where soft delete was never configured), the server_update command was failing with "Invalid value given for parameter RetentionDays" because: 1. The previous fix set instance.retention_days=None (omitting the field from PUT) 2. The Azure SQL API then internally used the existing -1 value and rejected it Fix: normalize -1 to 0 (disabled) when user does not specify --soft-delete-retention-days. Closes #32741 --- .../azure/cli/command_modules/sql/custom.py | 8 ++- .../sql/tests/latest/test_sql_commands.py | 58 ++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/sql/custom.py b/src/azure-cli/azure/cli/command_modules/sql/custom.py index e5e31b78439..f63949c9448 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/sql/custom.py @@ -4672,11 +4672,13 @@ def server_update( # Handle soft delete retention days # 0 = disable soft delete, 1-7 = enable with specified retention days - # If not specified, set to None to avoid sending existing value to API + # The API may return -1 for legacy servers where soft delete was never configured. + # -1 is not a valid value for PUT requests, so normalize it to 0 (disabled) + # to avoid "Invalid value given for parameter RetentionDays" errors. if soft_delete_retention_days is not None: instance.retention_days = soft_delete_retention_days - else: - instance.retention_days = None + elif instance.retention_days is not None and instance.retention_days < 0: + instance.retention_days = 0 return instance diff --git a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py index 3318425bfe6..4901c0308ef 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py +++ b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py @@ -32,7 +32,8 @@ ClientAuthenticationType, ClientType, ComputeModelType, - ResourceIdType) + ResourceIdType, + server_update) from datetime import datetime, timedelta # Constants @@ -9846,3 +9847,58 @@ def test_sql_server_restore_to_different_resource_group(self): # Clean up both resource groups self.cmd('group delete -n {} --yes --no-wait'.format(rg1_name)) self.cmd('group delete -n {} --yes --no-wait'.format(rg2_name)) + + +class ServerUpdateRetentionDaysUnitTest(unittest.TestCase): + """Unit tests for server_update retention_days handling.""" + + def _make_instance(self, retention_days): + """Create a minimal mock server instance with the given retention_days value.""" + from unittest.mock import MagicMock + instance = MagicMock() + instance.identity = None + instance.retention_days = retention_days + return instance + + def test_retention_days_negative_one_normalized_to_zero(self): + """When the server GET returns retention_days=-1 (legacy 'not configured' value) + and the user does not specify --soft-delete-retention-days, + server_update should normalize -1 to 0 (disabled) to avoid an API error.""" + instance = self._make_instance(-1) + result = server_update(instance) + self.assertEqual(result.retention_days, 0) + + def test_retention_days_none_not_modified(self): + """When the server GET returns retention_days=None and the user does not specify + --soft-delete-retention-days, server_update should leave retention_days as None + (the field will be omitted from the PUT body).""" + instance = self._make_instance(None) + result = server_update(instance) + self.assertIsNone(result.retention_days) + + def test_retention_days_zero_preserved(self): + """When the server has retention_days=0 (soft delete disabled) and the user does + not specify --soft-delete-retention-days, the value should be preserved.""" + instance = self._make_instance(0) + result = server_update(instance) + self.assertEqual(result.retention_days, 0) + + def test_retention_days_positive_preserved(self): + """When the server has retention_days=7 (soft delete enabled) and the user does + not specify --soft-delete-retention-days, the value should be preserved.""" + instance = self._make_instance(7) + result = server_update(instance) + self.assertEqual(result.retention_days, 7) + + def test_soft_delete_retention_days_specified_sets_value(self): + """When the user specifies --soft-delete-retention-days, that value should be used + regardless of the existing retention_days.""" + instance = self._make_instance(-1) + result = server_update(instance, soft_delete_retention_days=5) + self.assertEqual(result.retention_days, 5) + + def test_soft_delete_retention_days_zero_disables(self): + """When the user specifies --soft-delete-retention-days 0, soft delete is disabled.""" + instance = self._make_instance(7) + result = server_update(instance, soft_delete_retention_days=0) + self.assertEqual(result.retention_days, 0)