From 729e3418f9dd2b91c036ae5c62cdc6dddaee94ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 05:52:02 +0000 Subject: [PATCH 1/2] Initial plan From 6f63f885683a443fe8fdeffc0c57993b5bda6bb5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 06:01:01 +0000 Subject: [PATCH 2/2] Fix az sql server update failing with Invalid value for RetentionDays=-1 When servers have retention_days=-1 (legacy 'not configured' value), az sql server update fails because the API now validates this field even when absent from the PUT body. Fix by explicitly setting it to 0 (disabled) when the existing value is negative and no --soft-delete-retention-days argument is specified. Add unit tests for the fix. Agent-Logs-Url: https://github.com/Azure/azure-cli/sessions/15743958-26bc-49c2-92fa-8889d09d2b71 Co-authored-by: a0x1ab <59631311+a0x1ab@users.noreply.github.com> --- .../azure/cli/command_modules/sql/custom.py | 9 ++- .../sql/tests/latest/test_sql_commands.py | 61 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) 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 4b1c9923dcd..7b93b8e8b96 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/sql/custom.py @@ -4672,10 +4672,17 @@ 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 if soft_delete_retention_days is not None: instance.retention_days = soft_delete_retention_days + elif instance.retention_days is not None and instance.retention_days < 0: + # Legacy servers may have retention_days=-1 (meaning "not configured"). + # The API now validates this field and rejects negative values, so + # explicitly set to 0 (disabled) to avoid a validation error when + # other server properties are being updated. + instance.retention_days = 0 else: + # If not specified and existing value is valid (0-7) or None, + # set to None to avoid sending the existing value to the API. instance.retention_days = None 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..70cad0b25d8 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 @@ -9409,6 +9409,67 @@ def test_sql_server_soft_delete_complete_recovery_workflow(self, resource_group, # Note: ResourceGroupPreparer automatically handles cleanup of the resource group + +class SqlServerUpdateRetentionDaysUnitTest(unittest.TestCase): + """Unit tests for server_update retention_days handling.""" + + def _make_server_instance(self, retention_days): + """Create a minimal mock server instance with the given retention_days.""" + from unittest.mock import MagicMock + instance = MagicMock() + instance.retention_days = retention_days + instance.identity = None + instance.administrator_login_password = None + instance.minimal_tls_version = None + instance.public_network_access = None + instance.primary_user_assigned_identity_id = None + instance.key_id = None + instance.federated_client_id = None + return instance + + def test_server_update_negative_retention_days_is_reset_to_zero(self): + """ + When server has retention_days=-1 (legacy 'not configured' value) and + --soft-delete-retention-days is NOT passed, server_update should set + retention_days to 0 to avoid 'Invalid value given for parameter RetentionDays' + from the API. + """ + from azure.cli.command_modules.sql.custom import server_update + instance = self._make_server_instance(retention_days=-1) + result = server_update(instance) + self.assertEqual(result.retention_days, 0) + + def test_server_update_valid_retention_days_is_cleared_when_not_specified(self): + """ + When server has a valid retention_days value and --soft-delete-retention-days + is NOT passed, server_update should set retention_days to None (omit from PUT). + """ + from azure.cli.command_modules.sql.custom import server_update + instance = self._make_server_instance(retention_days=5) + result = server_update(instance) + self.assertIsNone(result.retention_days) + + def test_server_update_specified_retention_days_overrides_existing(self): + """ + When --soft-delete-retention-days IS passed, server_update should use + the specified value regardless of the existing value. + """ + from azure.cli.command_modules.sql.custom import server_update + instance = self._make_server_instance(retention_days=-1) + result = server_update(instance, soft_delete_retention_days=3) + self.assertEqual(result.retention_days, 3) + + def test_server_update_zero_specified_retention_days(self): + """ + When --soft-delete-retention-days 0 is passed, server_update should + explicitly disable soft delete by setting retention_days to 0. + """ + from azure.cli.command_modules.sql.custom import server_update + instance = self._make_server_instance(retention_days=7) + result = server_update(instance, soft_delete_retention_days=0) + self.assertEqual(result.retention_days, 0) + + class SqlServerDeletedServerScenarioTest(ScenarioTest): def test_sql_server_restore_non_existent_deleted_server(self):