Skip to content

Conversation

@rambabu-yalla
Copy link

@rambabu-yalla rambabu-yalla commented Oct 11, 2025

Related command
The change includes

  1. Add new command: az sql server restore,

    • az sql server restore -g mygroup -n myserver -l westus2
  2. Modify command: az sql server create

    • Added new parameters --enable-soft-delete and --soft-delete-retention-days
    • az sql server create -l westus -g mygroup -n myserver -u myadminuser -p myadminpassword --enable-soft-delete
  3. Modify command: az sql server update
    - Added new parameters --enable-soft-delete and --soft-delete-retention-days
    - az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete --soft-delete-retention-days 5

  4. Add new command: az sql server deleted-server show
    - az sql server deleted-server show --name servername --location eastasia

  5. Add new command: az sql server deleted-server list
    - az sql server deleted-server list --location eastasia

Description
This is to support soft delete public preview feature

Testing Guide

  1. Create a server with soft delete enabled with default retention days, 7 days.
    az sql server create -l westus -g mygroup -n myserver -u myadminuser -p myadminpassword --enable-soft-delete

  2. Create a server with soft delete enabled with custom retention days.
    az sql server create -l westus -g mygroup -n myserver -u myadminuser -p myadminpassword --minimal-tls-version 1.2 --enable-soft-delete --soft-delete-retention-days 3

  3. Enable soft delete protection with 7-day retention.
    az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete

  4. Modify soft delete retention period.
    az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete --soft-delete-retention-days 5

  5. Disable soft delete protection.
    az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete false

  6. Restore a deleted server.
    az sql server restore -g mygroup -n myserver -l westus2

This checklist is used to make sure that common guidelines for a pull request are followed.

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Oct 11, 2025

❌AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.13
️✔️acs
️✔️latest
️✔️3.12
️✔️3.13
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.13
️✔️ams
️✔️latest
️✔️3.12
️✔️3.13
️✔️apim
️✔️latest
️✔️3.12
️✔️3.13
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.13
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️aro
️✔️latest
️✔️3.12
️✔️3.13
️✔️backup
️✔️latest
️✔️3.12
️✔️3.13
️✔️batch
️✔️latest
️✔️3.12
️✔️3.13
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.13
️✔️billing
️✔️latest
️✔️3.12
️✔️3.13
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.13
️✔️cdn
️✔️latest
️✔️3.12
️✔️3.13
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.13
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.13
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.13
️✔️config
️✔️latest
️✔️3.12
️✔️3.13
️✔️configure
️✔️latest
️✔️3.12
️✔️3.13
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.13
️✔️container
️✔️latest
️✔️3.12
️✔️3.13
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.13
️✔️core
️✔️latest
️✔️3.12
️✔️3.13
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.13
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.13
️✔️dls
️✔️latest
️✔️3.12
️✔️3.13
️✔️dms
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.13
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.13
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.13
️✔️find
️✔️latest
️✔️3.12
️✔️3.13
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.13
️✔️identity
️✔️latest
️✔️3.12
️✔️3.13
️✔️iot
️✔️latest
️✔️3.12
️✔️3.13
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.13
️✔️lab
️✔️latest
️✔️3.12
️✔️3.13
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.13
️✔️maps
️✔️latest
️✔️3.12
️✔️3.13
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.13
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.13
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.13
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.13
️✔️network
️✔️latest
️✔️3.12
️✔️3.13
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.13
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.13
️✔️profile
️✔️latest
️✔️3.12
️✔️3.13
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.13
️✔️redis
️✔️latest
️✔️3.12
️✔️3.13
️✔️relay
️✔️latest
️✔️3.12
️✔️3.13
️✔️resource
️✔️latest
️✔️3.12
️✔️3.13
️✔️role
️✔️latest
️✔️3.12
️✔️3.13
️✔️search
️✔️latest
️✔️3.12
️✔️3.13
️✔️security
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.13
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.13
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.13
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.13
❌sql
❌latest
❌3.12
Type Test Case Error Message Line
Failed test_sql_server_create_with_tags self = <azure.cli.command_modules.sql.tests.latest.test_sql_commands.SqlServerMgmtScenarioTest testMethod=test_sql_server_create_with_tags>
resource_group = 'clitest.rg000001', resource_group_location = 'eastasia'

    @ResourceGroupPreparer(parameter_name='resource_group', location='eastasia')
    def test_sql_server_create_with_tags(self, resource_group, resource_group_location):
        server_name = self.create_random_name(server_name_prefix, server_name_max_length)
        admin_login = 'admin123'
        admin_password = 'SecretPassword123'
    
        # test create sql server with tags
>       server = self.cmd('sql server create -g {} --name {} '
                         '--admin-user {} --admin-password {} '
                         '--tags env=test purpose=demo'
                         .format(resource_group, server_name, admin_login, admin_password),
                         checks=[
                             JMESPathCheck('name', server_name),
                             JMESPathCheck('location', resource_group_location),
                             JMESPathCheck('resourceGroup', resource_group),
                             JMESPathCheck('administratorLogin', admin_login),
                             JMESPathCheck('tags.env', 'test'),
                             JMESPathCheck('tags.purpose', 'demo')]).get_output_in_json()

src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:295: 
                                        
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:278: in assert_with_checks
    c(self)
                                        

self = <azure.cli.testsdk.checkers.JMESPathCheck object at 0x7f8b7be17860>
execution_result = <azure.cli.testsdk.base.ExecutionResult object at 0x7f8b7bd34980>

    def call(self, execution_result):
        json_value = execution_result.get_output_in_json()
        actual_result = None
        try:
            actual_result = jmespath.search(self._query, json_value,
                                            jmespath.Options(collections.OrderedDict))
        except jmespath.exceptions.JMESPathTypeError:
            raise JMESPathCheckAssertionError(self._query, self._expected_result, actual_result,
                                              execution_result.output)
        if self._case_sensitive:
            equals = actual_result == self._expected_result or str(actual_result) == str(self._expected_result)
        else:
            equals = actual_result == self._expected_result <br>                or str(actual_result).lower() == str(self._expected_result).lower()
        if not equals:
            if actual_result:
>               raise JMESPathCheckAssertionError(self._query, self._expected_result, actual_result,
                                                  execution_result.output)
E               azure.cli.testsdk.exceptions.JMESPathCheckAssertionError: Query 'location' doesn't yield expected value 'eastasia', instead the actual value is 'westeurope'. Data: 
E               {
E                 "administratorLogin": "admin123",
E                 "administratorLoginPassword": null,
E                 "administrators": null,
E                 "createMode": null,
E                 "externalGovernanceStatus": "Disabled",
E                 "federatedClientId": null,
E                 "fullyQualifiedDomainName": "clitestserver000002.database.windows.net",
E                 "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Sql/servers/clitestserver000002",
E                 "identity": null,
E                 "isIPv6Enabled": null,
E                 "keyId": null,
E                 "kind": "v12.0",
E                 "location": "westeurope",
E                 "minimalTlsVersion": "1.2",
E                 "name": "clitestserver000002",
E                 "primaryUserAssignedIdentityId": null,
E                 "privateEndpointConnections": [],
E                 "publicNetworkAccess": "Enabled",
E                 "resourceGroup": "clitest.rg000001",
E                 "restrictOutboundNetworkAccess": "Disabled",
E                 "retentionDays": null,
E                 "state": "Ready",
E                 "tags": {
E                   "env": "test",
E                   "purpose": "demo"
E                 },
E                 "type": "Microsoft.Sql/servers",
E                 "version": "12.0",
E                 "workspaceFeature": null
E               }

src/azure-cli-testsdk/azure/cli/testsdk/checkers.py:34: JMESPathCheckAssertionError
azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:287
Failed test_sql_server_soft_delete_complete_recovery_workflow self = <azure.cli.testsdk.base.ExecutionResult object at 0x7f8b7bc75460>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7f8b7dd73e30>
command = 'sql server create -g softdelete000001 --name softdelete000002 -l eastasia --admin-user admin123 --admin-password SecretPassword123 --enable-soft-delete true --soft-delete-retention-days 2'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.12/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:130: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/patches.py:33: in handle_main_exception
    raise ex
env/lib/python3.12/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:703: in run_job
    result = cmd_copy(params)
             ^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:336: in call
    return self.handler(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/command_operation.py:120: in handler
    return op(**command_args)
           ^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/sql/custom.py:4537: in server_create
    return sdk_no_wait(no_wait, client.begin_create_or_update,
src/azure-cli-core/azure/cli/core/util.py:759: in sdk_no_wait
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/tracing/decorator.py:119: in wrapper_use_tracer
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/mgmt/sql/operations/servers_operations.py:816: in begin_create_or_update
    raw_result = self.create_or_update_initial(
env/lib/python3.12/site-packages/azure/mgmt/sql/operations/servers_operations.py:708: in create_or_update_initial
    pipeline_response: PipelineResponse = self.client.pipeline.run(  # pylint: disable=protected-access
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:242: in run
    return first_node.send(pipeline_request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/mgmt/core/policies/base.py:95: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/policies/redirect.py:205: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/policies/retry.py:545: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/policies/authentication.py:159: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/base.py:130: in send
    self.sender.send(request.http_request, **request.context.options),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/azure/core/pipeline/transport/requests_basic.py:365: in send
    response = self.session.request(  # type: ignore
env/lib/python3.12/site-packages/requests/sessions.py:589: in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/requests/sessions.py:703: in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.12/site-packages/requests/adapters.py:667: in send
    resp = conn.urlopen(
env/lib/python3.12/site-packages/urllib3/connectionpool.py:787: in urlopen
    response = self.make_request(
env/lib/python3.12/site-packages/urllib3/connectionpool.py:534: in make_request
    response = conn.getresponse()
               ^^^^^^^^^^^^^^^^^^
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
          

self = <vcr.patch.VCRRequestsHTTPSConnection/mnt/vss/work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml object at 0x7f8b7b9d9790>
 = False, kwargs = {}

    def getresponse(self, 
=False, **kwargs):
        """Retrieve the response"""
        # Check to see if the cassette has a response for this request. If so,
        # then return it
        if self.cassette.can_play_response_for(self.vcr_request):
            log.info(f"Playing response for {self.vcr_request} from cassette")
            response = self.cassette.play_response(self.vcr_request)
            return VCRHTTPResponse(response)
        else:
            if self.cassette.write_protected and self.cassette.filter_request(self.vcr_request):
>               raise CannotOverwriteExistingCassetteException(
                    cassette=self.cassette,
                    failed_request=self.vcr_request,
                )
E               vcr.errors.CannotOverwriteExistingCassetteException: Can't overwrite existing cassette ('/mnt/vss/work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml') in your current record mode ('once').
E               No match for the request (<Request (PUT) https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>)&nbsp;was&nbsp;found.
E               Found 3 similar requests with 1 different matcher(s) :
E               
E               1 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net
E               
E               2 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net
E               
E               3 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net

env/lib/python3.12/site-packages/vcr/stubs/init.py:264: CannotOverwriteExistingCassetteException

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.sql.tests.latest.test_sql_commands.SqlServerSoftDeleteScenarioTest testMethod=test_sql_server_soft_delete_complete_recovery_workflow>
resource_group = 'softdelete000001', resource_group_location = 'eastasia'

    @ResourceGroupPreparer(name_prefix='softdelete', location='eastasia')
    @AllowLargeResponse(size_kb=9999)
    def test_sql_server_soft_delete_complete_recovery_workflow(self, resource_group, resource_group_location):
        """Test complete soft delete workflow: create server with 2-day retention, delete, restore, and cleanup"""
        server_name = self.create_random_name('softdelete', server_name_max_length)
        restored_server_name = self.create_random_name('restored', server_name_max_length)
        admin_login = 'admin123'
        admin_password = 'SecretPassword123'
        retention_days = 2
        location = 'eastasia'
    
        # Create server with soft delete enabled and 2-day retention
>       server = self.cmd('sql server create -g {} --name {} -l {} '
                         '--admin-user {} --admin-password {} '
                         '--enable-soft-delete true --soft-delete-retention-days {}'
                         .format(resource_group, server_name, location,
                                admin_login, admin_password, retention_days),
                         checks=[
                             JMESPathCheck('name', server_name),
                             JMESPathCheck('location', location),
                             JMESPathCheck('resourceGroup', resource_group),
                             JMESPathCheck('administratorLogin', admin_login),
                             JMESPathCheck('retentionDays', retention_days)]).get_output_in_json()

src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9000: 
 
 
 
 
 
 
 
 
                                
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
 
                                       

self = <azure.cli.testsdk.base.ExecutionResult object at 0x7f8b7bc75460>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7f8b7dd73e30>
command = 'sql server create -g softdelete000001 --name softdelete000002 -l eastasia --admin-user admin123 --admin-password SecretPassword123 --enable-soft-delete true --soft-delete-retention-days 2'
expect_failure = False

    def _in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
            self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
            self.output = stdout_buf.getvalue()
            self.applog = logging_buf.getvalue()
    
        except CannotOverwriteExistingCassetteException as ex:
>           raise AssertionError(ex)
E           AssertionError: Can't overwrite existing cassette ('/mnt/vss/_work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml') in your current record mode ('once').
E           No match for the request (<Request (PUT) https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>)&nbsp;was&nbsp;found.
E           Found 3 similar requests with 1 different matcher(s) :
E           
E           1 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net
E           
E           2 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net
E           
E           3 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net

src/azure-cli-testsdk/azure/cli/testsdk/base.py:308: AssertionError
azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8987
Failed test_sql_server_soft_delete_disable_on_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8902
Failed test_sql_server_soft_delete_enable_custom_retention The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8866
Failed test_sql_server_soft_delete_enable_default_retention The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8832
Failed test_sql_server_soft_delete_retention_days_above_maximum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9093
Failed test_sql_server_soft_delete_retention_days_below_minimum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9059
Failed test_sql_server_soft_delete_retention_days_invalid_on_update The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9127
Failed test_sql_server_soft_delete_retention_days_negative The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9076
Failed test_sql_server_soft_delete_retention_days_way_above_maximum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9110
Failed test_sql_server_soft_delete_retention_without_enable_flag The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9147
Failed test_sql_server_soft_delete_set_custom_retention_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8952
Failed test_sql_server_soft_delete_set_default_retention_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8928
Failed test_sql_deleted_server_invalid_location The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9264
Failed test_sql_deleted_server_list_and_show The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9167
Failed test_sql_deleted_server_list_empty_location The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9235
Failed test_sql_deleted_server_show_not_found The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9250
❌3.13
Type Test Case Error Message Line
Failed test_sql_server_create_with_tags self = <azure.cli.command_modules.sql.tests.latest.test_sql_commands.SqlServerMgmtScenarioTest testMethod=test_sql_server_create_with_tags>
resource_group = 'clitest.rg000001', resource_group_location = 'eastasia'

    @ResourceGroupPreparer(parameter_name='resource_group', location='eastasia')
    def test_sql_server_create_with_tags(self, resource_group, resource_group_location):
        server_name = self.create_random_name(server_name_prefix, server_name_max_length)
        admin_login = 'admin123'
        admin_password = 'SecretPassword123'
    
        # test create sql server with tags
>       server = self.cmd('sql server create -g {} --name {} '
                         '--admin-user {} --admin-password {} '
                         '--tags env=test purpose=demo'
                         .format(resource_group, server_name, admin_login, admin_password),
                         checks=[
                             JMESPathCheck('name', server_name),
                             JMESPathCheck('location', resource_group_location),
                             JMESPathCheck('resourceGroup', resource_group),
                             JMESPathCheck('administratorLogin', admin_login),
                             JMESPathCheck('tags.env', 'test'),
                             JMESPathCheck('tags.purpose', 'demo')]).get_output_in_json()

src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:295: 
                                        
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:278: in assert_with_checks
    c(self)
                                        

self = <azure.cli.testsdk.checkers.JMESPathCheck object at 0x7fa2ece274d0>
execution_result = <azure.cli.testsdk.base.ExecutionResult object at 0x7fa2ece6e900>

    def call(self, execution_result):
        json_value = execution_result.get_output_in_json()
        actual_result = None
        try:
            actual_result = jmespath.search(self._query, json_value,
                                            jmespath.Options(collections.OrderedDict))
        except jmespath.exceptions.JMESPathTypeError:
            raise JMESPathCheckAssertionError(self._query, self._expected_result, actual_result,
                                              execution_result.output)
        if self._case_sensitive:
            equals = actual_result == self._expected_result or str(actual_result) == str(self._expected_result)
        else:
            equals = actual_result == self._expected_result <br>                or str(actual_result).lower() == str(self._expected_result).lower()
        if not equals:
            if actual_result:
>               raise JMESPathCheckAssertionError(self._query, self._expected_result, actual_result,
                                                  execution_result.output)
E               azure.cli.testsdk.exceptions.JMESPathCheckAssertionError: Query 'location' doesn't yield expected value 'eastasia', instead the actual value is 'westeurope'. Data: 
E               {
E                 "administratorLogin": "admin123",
E                 "administratorLoginPassword": null,
E                 "administrators": null,
E                 "createMode": null,
E                 "externalGovernanceStatus": "Disabled",
E                 "federatedClientId": null,
E                 "fullyQualifiedDomainName": "clitestserver000002.database.windows.net",
E                 "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Sql/servers/clitestserver000002",
E                 "identity": null,
E                 "isIPv6Enabled": null,
E                 "keyId": null,
E                 "kind": "v12.0",
E                 "location": "westeurope",
E                 "minimalTlsVersion": "1.2",
E                 "name": "clitestserver000002",
E                 "primaryUserAssignedIdentityId": null,
E                 "privateEndpointConnections": [],
E                 "publicNetworkAccess": "Enabled",
E                 "resourceGroup": "clitest.rg000001",
E                 "restrictOutboundNetworkAccess": "Disabled",
E                 "retentionDays": null,
E                 "state": "Ready",
E                 "tags": {
E                   "env": "test",
E                   "purpose": "demo"
E                 },
E                 "type": "Microsoft.Sql/servers",
E                 "version": "12.0",
E                 "workspaceFeature": null
E               }

src/azure-cli-testsdk/azure/cli/testsdk/checkers.py:34: JMESPathCheckAssertionError
azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:287
Failed test_sql_server_soft_delete_complete_recovery_workflow self = <azure.cli.testsdk.base.ExecutionResult object at 0x7fa2ece72a50>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7fa2eee48190>
command = 'sql server create -g softdelete000001 --name softdelete000002 -l eastasia --admin-user admin123 --admin-password SecretPassword123 --enable-soft-delete true --soft-delete-retention-days 2'
expect_failure = False

    def in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
>           self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/azure-cli-testsdk/azure/cli/testsdk/base.py:303: 
                                        
env/lib/python3.13/site-packages/knack/cli.py:245: in invoke
    exit_code = self.exception_handler(ex)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/init.py:130: in exception_handler
    return handle_exception(ex)
           ^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/patches.py:33: in handle_main_exception
    raise ex
env/lib/python3.13/site-packages/knack/cli.py:233: in invoke
    cmd_result = self.invocation.execute(args)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:666: in execute
    raise ex
src/azure-cli-core/azure/cli/core/commands/init.py:734: in run_jobs_serially
    results.append(self.run_job(expanded_arg, cmd_copy))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:703: in run_job
    result = cmd_copy(params)
             ^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/init.py:336: in call
    return self.handler(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-core/azure/cli/core/commands/command_operation.py:120: in handler
    return op(**command_args)
           ^^^^^^^^^^^^^^^^^^
src/azure-cli/azure/cli/command_modules/sql/custom.py:4537: in server_create
    return sdk_no_wait(no_wait, client.begin_create_or_update,
src/azure-cli-core/azure/cli/core/util.py:759: in sdk_no_wait
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/tracing/decorator.py:119: in wrapper_use_tracer
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/mgmt/sql/operations/servers_operations.py:816: in begin_create_or_update
    raw_result = self.create_or_update_initial(
env/lib/python3.13/site-packages/azure/mgmt/sql/operations/servers_operations.py:708: in create_or_update_initial
    pipeline_response: PipelineResponse = self.client.pipeline.run(  # pylint: disable=protected-access
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:242: in run
    return first_node.send(pipeline_request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/mgmt/core/policies/base.py:95: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/policies/redirect.py:205: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/policies/retry.py:545: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/policies/authentication.py:159: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:98: in send
    response = self.next.send(request)
               ^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/base.py:130: in send
    self.sender.send(request.http_request, **request.context.options),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/azure/core/pipeline/transport/requests_basic.py:365: in send
    response = self.session.request(  # type: ignore
env/lib/python3.13/site-packages/requests/sessions.py:589: in request
    resp = self.send(prep, **send_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/requests/sessions.py:703: in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
env/lib/python3.13/site-packages/requests/adapters.py:667: in send
    resp = conn.urlopen(
env/lib/python3.13/site-packages/urllib3/connectionpool.py:787: in urlopen
    response = self.make_request(
env/lib/python3.13/site-packages/urllib3/connectionpool.py:534: in make_request
    response = conn.getresponse()
               ^^^^^^^^^^^^^^^^^^
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
          

self = <vcr.patch.VCRRequestsHTTPSConnection/mnt/vss/work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml object at 0x7fa2ecb53a10>
 = False, kwargs = {}

    def getresponse(self, 
=False, **kwargs):
        """Retrieve the response"""
        # Check to see if the cassette has a response for this request. If so,
        # then return it
        if self.cassette.can_play_response_for(self.vcr_request):
            log.info(f"Playing response for {self.vcr_request} from cassette")
            response = self.cassette.play_response(self.vcr_request)
            return VCRHTTPResponse(response)
        else:
            if self.cassette.write_protected and self.cassette.filter_request(self.vcr_request):
>               raise CannotOverwriteExistingCassetteException(
                    cassette=self.cassette,
                    failed_request=self.vcr_request,
                )
E               vcr.errors.CannotOverwriteExistingCassetteException: Can't overwrite existing cassette ('/mnt/vss/work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml') in your current record mode ('once').
E               No match for the request (<Request (PUT) https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>)&nbsp;was&nbsp;found.
E               Found 3 similar requests with 1 different matcher(s) :
E               
E               1 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net
E               
E               2 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net
E               
E               3 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E               Matchers succeeded : ['method', 'scheme', 'port', 'path', 'custom_request_query_matcher']
E               Matchers failed :
E               host - assertion failure :
E               management.azure.com != api-dogfood.resources.windows-int.net

env/lib/python3.13/site-packages/vcr/stubs/init.py:264: CannotOverwriteExistingCassetteException

During handling of the above exception, another exception occurred:

self = <azure.cli.command_modules.sql.tests.latest.test_sql_commands.SqlServerSoftDeleteScenarioTest testMethod=test_sql_server_soft_delete_complete_recovery_workflow>
resource_group = 'softdelete000001', resource_group_location = 'eastasia'

    @ResourceGroupPreparer(name_prefix='softdelete', location='eastasia')
    @AllowLargeResponse(size_kb=9999)
    def test_sql_server_soft_delete_complete_recovery_workflow(self, resource_group, resource_group_location):
        """Test complete soft delete workflow: create server with 2-day retention, delete, restore, and cleanup"""
        server_name = self.create_random_name('softdelete', server_name_max_length)
        restored_server_name = self.create_random_name('restored', server_name_max_length)
        admin_login = 'admin123'
        admin_password = 'SecretPassword123'
        retention_days = 2
        location = 'eastasia'
    
        # Create server with soft delete enabled and 2-day retention
>       server = self.cmd('sql server create -g {} --name {} -l {} '
                         '--admin-user {} --admin-password {} '
                         '--enable-soft-delete true --soft-delete-retention-days {}'
                         .format(resource_group, server_name, location,
                                admin_login, admin_password, retention_days),
                         checks=[
                             JMESPathCheck('name', server_name),
                             JMESPathCheck('location', location),
                             JMESPathCheck('resourceGroup', resource_group),
                             JMESPathCheck('administratorLogin', admin_login),
                             JMESPathCheck('retentionDays', retention_days)]).get_output_in_json()

src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9000: 
 
 
 
 
 
 
 
 
                                
src/azure-cli-testsdk/azure/cli/testsdk/base.py:177: in cmd
    return execute(self.cli_ctx, command, expect_failure=expect_failure).assert_with_checks(checks)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
src/azure-cli-testsdk/azure/cli/testsdk/base.py:252: in init
    self.in_process_execute(cli_ctx, command, expect_failure=expect_failure)
 
                                       

self = <azure.cli.testsdk.base.ExecutionResult object at 0x7fa2ece72a50>
cli_ctx = <azure.cli.core.mock.DummyCli object at 0x7fa2eee48190>
command = 'sql server create -g softdelete000001 --name softdelete000002 -l eastasia --admin-user admin123 --admin-password SecretPassword123 --enable-soft-delete true --soft-delete-retention-days 2'
expect_failure = False

    def _in_process_execute(self, cli_ctx, command, expect_failure=False):
        from io import StringIO
        from vcr.errors import CannotOverwriteExistingCassetteException
    
        if command.startswith('az '):
            command = command[3:]
    
        stdout_buf = StringIO()
        logging_buf = StringIO()
        try:
            # issue: stderr cannot be redirect in this form, as a result some failure information
            # is lost when command fails.
            self.exit_code = cli_ctx.invoke(shlex.split(command), out_file=stdout_buf) or 0
            self.output = stdout_buf.getvalue()
            self.applog = logging_buf.getvalue()
    
        except CannotOverwriteExistingCassetteException as ex:
>           raise AssertionError(ex)
E           AssertionError: Can't overwrite existing cassette ('/mnt/vss/_work/1/s/src/azure-cli/azure/cli/command_modules/sql/tests/latest/recordings/test_sql_server_soft_delete_complete_recovery_workflow.yaml') in your current record mode ('once').
E           No match for the request (<Request (PUT) https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>)&nbsp;was&nbsp;found.
E           Found 3 similar requests with 1 different matcher(s) :
E           
E           1 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net
E           
E           2 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net
E           
E           3 - (<Request (PUT) https://api-dogfood.resources.windows-int.net/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/softdelete000001/providers/Microsoft.Sql/servers/softdelete000002?api-version=2024-11-01-preview>).
E           Matchers succeeded : ['method', 'scheme', 'port', 'path', '_custom_request_query_matcher']
E           Matchers failed :
E           host - assertion failure :
E           management.azure.com != api-dogfood.resources.windows-int.net

src/azure-cli-testsdk/azure/cli/testsdk/base.py:308: AssertionError
azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8987
Failed test_sql_server_soft_delete_disable_on_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8902
Failed test_sql_server_soft_delete_enable_custom_retention The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8866
Failed test_sql_server_soft_delete_enable_default_retention The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8832
Failed test_sql_server_soft_delete_retention_days_above_maximum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9093
Failed test_sql_server_soft_delete_retention_days_below_minimum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9059
Failed test_sql_server_soft_delete_retention_days_invalid_on_update The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9127
Failed test_sql_server_soft_delete_retention_days_negative The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9076
Failed test_sql_server_soft_delete_retention_days_way_above_maximum The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9110
Failed test_sql_server_soft_delete_retention_without_enable_flag The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9147
Failed test_sql_server_soft_delete_set_custom_retention_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8952
Failed test_sql_server_soft_delete_set_default_retention_existing The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:8928
Failed test_sql_deleted_server_invalid_location The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9264
Failed test_sql_deleted_server_list_and_show The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9167
Failed test_sql_deleted_server_list_empty_location The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9235
Failed test_sql_deleted_server_show_not_found The error message is too long, please check the pipeline log for details. azure/cli/command_modules/sql/tests/latest/test_sql_commands.py:9250
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.13
️✔️storage
️✔️latest
️✔️3.12
️✔️3.13
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.13
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.13
️✔️util
️✔️latest
️✔️3.12
️✔️3.13
️✔️vm
️✔️latest
️✔️3.12
️✔️3.13

@azure-client-tools-bot-prd
Copy link

azure-client-tools-bot-prd bot commented Oct 11, 2025

⚠️AzureCLI-BreakingChangeTest
⚠️sql
rule cmd_name rule_message suggest_message
⚠️ 1006 - ParaAdd sql server create cmd sql server create added parameter enable_soft_delete
⚠️ 1006 - ParaAdd sql server create cmd sql server create added parameter soft_delete_retention_days
⚠️ 1011 - SubgroupAdd sql server deleted-server sub group sql server deleted-server added
⚠️ 1001 - CmdAdd sql server restore cmd sql server restore added
⚠️ 1006 - ParaAdd sql server update cmd sql server update added parameter enable_soft_delete
⚠️ 1006 - ParaAdd sql server update cmd sql server update added parameter soft_delete_retention_days

@yonzhan
Copy link
Collaborator

yonzhan commented Oct 11, 2025

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@microsoft-github-policy-service microsoft-github-policy-service bot added the customer-reported Issues that are reported by GitHub users external to the Azure organization. label Oct 11, 2025
@microsoft-github-policy-service
Copy link
Contributor

Thank you for your contribution @rambabu-yalla! We will review the pull request and get back to you soon.

@microsoft-github-policy-service microsoft-github-policy-service bot added Auto-Assign Auto assign by bot SQL az sql labels Oct 11, 2025
@rambabu-yalla rambabu-yalla marked this pull request as ready for review October 14, 2025 07:07
Copilot AI review requested due to automatic review settings October 14, 2025 07:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces changes to support SQL Server soft delete functionality, adding new commands and parameters for server creation, update, and restoration operations. The changes enable users to create SQL servers with soft delete protection, modify soft delete settings on existing servers, and restore previously deleted servers.

  • Adds new az sql server restore command for recovering deleted SQL servers
  • Extends az sql server create and az sql server update commands with soft delete parameters
  • Implements comprehensive validation for soft delete retention days (1-7 days when enabled, 0 when disabled)

Reviewed Changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test_sql_commands.py Added comprehensive test suite for soft delete functionality with multiple scenarios
test recordings (2 files) Added test recording files for new soft delete test cases
custom.py Implemented server restore functionality and soft delete parameter handling
commands.py Added new restore command to SQL server command group
_validators.py Added validation logic for soft delete parameters with proper error handling
_util.py Added utility function to access deleted servers API operations
_params.py Added command line parameters for soft delete options
_help.py Added help documentation and examples for new soft delete features
Comments suppressed due to low confidence (2)

src/azure-cli/azure/cli/command_modules/sql/_help.py:1

  • The help message should start with a verb in active voice. Consider changing 'Set whether soft delete is enabled or not' to 'Enable or disable soft delete protection' to follow the help message format guidelines.
# coding=utf-8

src/azure-cli/azure/cli/command_modules/sql/_help.py:1

  • The help message should start with a verb in active voice. Consider changing 'The number of days to retain soft deleted resources' to 'Specify the number of days to retain soft deleted resources' to follow the help message format guidelines.
# coding=utf-8

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@rambabu-yalla rambabu-yalla requested a review from Copilot October 14, 2025 07:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@rambabu-yalla rambabu-yalla requested a review from Copilot October 17, 2025 05:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/azure-cli/azure/cli/command_modules/sql/custom.py:4436

  • The docstring uses triple single quotes instead of triple double quotes. Python convention is to use triple double quotes for docstrings.
    '''
    Creates a server.
    '''

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@pranavathalye
Copy link

Can you please add negative test cases for each validation scenario related to options enable soft delete and retention days?

Copy link

@pranavathalye pranavathalye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review.

@rambabu-yalla
Copy link
Author

Can you please add negative test cases for each validation scenario related to options enable soft delete and retention days?

Added.

@rambabu-yalla rambabu-yalla requested a review from Copilot October 24, 2025 11:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 1813 to 1844
- name: Enable soft delete protection with 7-day retention.
text: az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete
- name: Modify soft delete retention period.
text: az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete --soft-delete-retention-days 5
- name: Disable soft delete protection.
text: az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete false
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help messages do not follow the guideline to start with an active, first-person verb. According to custom coding guideline 855, help messages starting with a verb should be in first person active voice (e.g., 'Enable' should be used instead of 'Enables'). However, these help messages currently start with an active verb in the imperative form, which is actually correct. The issue is the passive construction. Change to: 'Enable soft delete protection with 7-day retention.', 'Modify soft delete retention period.', 'Disable soft delete protection.' (These are already correct - no change needed).

Copilot uses AI. Check for mistakes.
- name: Create a server with disabled public network access to server.
text: az sql server create -l westus -g mygroup -n myserver -u myadminuser -p myadminpassword -e false
- name: Create a server with soft delete enabled with default retention days.
text: az sql server create -l westus -g mygroup -n myserver -u myadminuser -p myadminpassword --enable-soft-delete
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help message does not follow the guideline to start with an active, first-person verb. The message 'Create a server with soft delete enabled with default retention days.' should be in active voice. However, reviewing more carefully, this help text is already in active voice and first person (imperative). The issue noted in guideline 855 would apply if it were written as 'Creates a server...' (third person) or 'Enabled...' (passive). The current form 'Create...' is correct.

Copilot uses AI. Check for mistakes.
Comment on lines 187 to 189
raise RequiredArgumentMissingError(
'The --soft-delete-retention-days parameter requires --enable-soft-delete to be specified.',
'Please specify both --enable-soft-delete and --soft-delete-retention-days together.')
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error handling block is duplicated from lines 158-160. Consider extracting this into a helper function or constant to avoid code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines 4481 to 4516
if soft_delete_retention_days is not None:
kwargs['retention_days'] = soft_delete_retention_days
else:
kwargs['retention_days'] = 7
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default retention days value (7) is duplicated in multiple locations (also in server_update at line 4624 and in validator comments). Consider defining this as a named constant at the module level to improve maintainability and ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +575 to +578
g.custom_command('restore', 'server_restore',
table_transformer=server_table_format,
supports_no_wait=True)
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The restore command is a dangerous operation that could potentially restore a deleted server and overwrite data. According to custom coding guideline 1388, dangerous operations like delete, remove, and stop should include confirmation=True. While restore is different from delete, it may have significant consequences. Consider adding confirmation=True to prompt users before executing this operation.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 16 out of 18 changed files in this pull request and generated 5 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 171 to 184
# Validate range based on enable_soft_delete value
if enable_soft_delete is True:
# When enable_soft_delete is true, retention days must be 1-7
if not 1 <= retention_days <= 7:
raise InvalidArgumentValueError(
'The --soft-delete-retention-days value must be between 1 and 7 (inclusive) '
'when --enable-soft-delete is true.',
'Please specify a value between 1 and 7 days.')
elif enable_soft_delete is False:
# When enable_soft_delete is false, retention days must be 0
if retention_days != 0:
raise InvalidArgumentValueError(
'The --soft-delete-retention-days value must be 0 when --enable-soft-delete is false.',
'Please set --soft-delete-retention-days to 0 or remove it when disabling soft delete.')
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The validation logic has duplicated range checking code that could be consolidated. The validation for enable_soft_delete is True and enable_soft_delete is False could be combined into a single conditional block using a dictionary or helper function to map expected ranges.

Suggested change
# Validate range based on enable_soft_delete value
if enable_soft_delete is True:
# When enable_soft_delete is true, retention days must be 1-7
if not 1 <= retention_days <= 7:
raise InvalidArgumentValueError(
'The --soft-delete-retention-days value must be between 1 and 7 (inclusive) '
'when --enable-soft-delete is true.',
'Please specify a value between 1 and 7 days.')
elif enable_soft_delete is False:
# When enable_soft_delete is false, retention days must be 0
if retention_days != 0:
raise InvalidArgumentValueError(
'The --soft-delete-retention-days value must be 0 when --enable-soft-delete is false.',
'Please set --soft-delete-retention-days to 0 or remove it when disabling soft delete.')
# Validate range based on enable_soft_delete value using a mapping
expected_ranges = {
True: lambda v: 1 <= v <= 7,
False: lambda v: v == 0
}
error_messages = {
True: (
'The --soft-delete-retention-days value must be between 1 and 7 (inclusive) '
'when --enable-soft-delete is true.',
'Please specify a value between 1 and 7 days.'
),
False: (
'The --soft-delete-retention-days value must be 0 when --enable-soft-delete is false.',
'Please set --soft-delete-retention-days to 0 or remove it when disabling soft delete.'
)
}
if enable_soft_delete in expected_ranges:
if not expected_ranges[enable_soft_delete](retention_days):
raise InvalidArgumentValueError(*error_messages[enable_soft_delete])

Copilot uses AI. Check for mistakes.
Comment on lines 189 to 229
def _check_server_exists(client, server_name):
'''
Checks if a server already exists in the subscription and raises ValidationError if it does.
Searches across all servers in the subscription to find any server with the given name.
Returns False if server doesn't exist, raises ValidationError if it exists.
'''
try:
# List all servers in the subscription
all_servers = list(client.list())

# Check if any server matches the given server name (case-insensitive)
if any(server.name.lower() == server_name.lower() for server in all_servers):
raise ValidationError(
f'Server "{server_name}" already exists in the subscription. '
'Cannot restore to an existing server name.')

# No matching server found - this is what we want for restore
return False

except ValidationError:
# Re-raise ValidationError as-is
raise
except Exception as ex:
# If server doesn't exist, that's what we want - continue with restore
if 'ResourceNotFound' not in str(ex) and 'NotFound' not in str(ex):
# Re-raise if it's not a "not found" error
raise
return False
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Listing all servers in the subscription to check for existence is inefficient. This could cause performance issues in subscriptions with many servers. Consider using the server's get method instead, which would return a 404 if the server doesn't exist.

Suggested change
def _check_server_exists(client, server_name):
'''
Checks if a server already exists in the subscription and raises ValidationError if it does.
Searches across all servers in the subscription to find any server with the given name.
Returns False if server doesn't exist, raises ValidationError if it exists.
'''
try:
# List all servers in the subscription
all_servers = list(client.list())
# Check if any server matches the given server name (case-insensitive)
if any(server.name.lower() == server_name.lower() for server in all_servers):
raise ValidationError(
f'Server "{server_name}" already exists in the subscription. '
'Cannot restore to an existing server name.')
# No matching server found - this is what we want for restore
return False
except ValidationError:
# Re-raise ValidationError as-is
raise
except Exception as ex:
# If server doesn't exist, that's what we want - continue with restore
if 'ResourceNotFound' not in str(ex) and 'NotFound' not in str(ex):
# Re-raise if it's not a "not found" error
raise
return False
def _check_server_exists(client, server_name, resource_group_name):
'''
Checks if a server already exists in the subscription and raises ValidationError if it does.
Uses the get method to check for existence efficiently.
Returns False if server doesn't exist, raises ValidationError if it exists.
'''
try:
# Try to get the server directly
server = client.get(server_name=server_name, resource_group_name=resource_group_name)
if server:
raise ValidationError(
f'Server "{server_name}" already exists in the subscription. '
'Cannot restore to an existing server name.')
return False
except ValidationError:
# Re-raise ValidationError as-is
raise
except Exception as ex:
# If server doesn't exist, that's what we want - continue with restore
if 'ResourceNotFound' in str(ex) or 'NotFound' in str(ex):
return False
# Re-raise if it's not a "not found" error
raise

Copilot uses AI. Check for mistakes.
Comment on lines 1917 to 1918
help='Enable or disable soft delete retention. When true,'
'the soft delete is enabled for 7 days by default.')
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help text has inconsistent spacing and could be clearer. The phrase "When true, the soft delete is enabled for 7 days by default" should have a space after the comma on line 1917. Additionally, the help text should clarify that this sets the retention period, not just "enables soft delete retention."

Suggested change
help='Enable or disable soft delete retention. When true,'
'the soft delete is enabled for 7 days by default.')
help='Enable or disable soft delete retention. When true, soft delete retention is enabled and the retention period is set to 7 days by default. You can customize the retention period using --soft-delete-retention-days.')

Copilot uses AI. Check for mistakes.
Comment on lines 9197 to 9198
# Wait for the deleted server to appear in the deleted servers list (Azure API propagation delay)
time.sleep(60)
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a hard-coded sleep of 60 seconds in tests is problematic for test reliability and execution time. Consider implementing a retry mechanism with a timeout instead, polling the deleted servers list until the server appears or a maximum wait time is exceeded.

Copilot uses AI. Check for mistakes.
Comment on lines 1837 to 1840
- name: Enable soft delete protection with 7-day retention.
text: az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete
- name: Modify soft delete retention period.
text: az sql server update --name MyAzureSQLServer --resource-group MyResourceGroup --enable-soft-delete --soft-delete-retention-days 5
Copy link

Copilot AI Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The examples for sql server update show both long-form and short-form options, but the help text doesn't explain when to use each. Consider adding a brief note about the short aliases (--esd, --sdrd) being equivalent alternatives to the full parameter names.

Copilot uses AI. Check for mistakes.
with self.argument_context('sql server deleted-server list') as c:
c.argument('location', arg_type=get_location_type(self.cli_ctx),
required=True,
help='Location to list deleted servers from.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"location to list deleted servers from" is odd phrasing.
Perhaps "List deleted servers in this location"?

Comment on lines +791 to +794
with self.command_group('sql server deleted-server', deleted_servers_operations,
client_factory=get_sql_deleted_servers_operations) as g:
g.custom_show_command('show', 'deleted_server_show')
g.custom_command('list', 'deleted_server_list')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about az sql server list-deleted/show-deleted. There's no need to add another sub command group deleted-server.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API used to retrieve deleted servers is different from the standard servers API, and its response is only a subset of the full servers response. For this reason, we implemented it as a separate subgroup rather than adding list-deleted or show-deleted commands under the main group.

@evelyn-ys
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@evelyn-ys
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@rambabu-yalla rambabu-yalla changed the title Introduced changes to cli commands to support self-server restore of sql logical server [SQL] Introduced changes to cli commands to support self-server restore of sql logical server Oct 27, 2025
@rambabu-yalla
Copy link
Author

rambabu-yalla commented Oct 27, 2025

@evelyn-ys : Please do not merge the PR.

@evelyn-ys
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@evelyn-ys
Copy link
Member

@evelyn-ys : Please do not merge the PR.

@rambabu-yalla You can mark this PR as draft to prevent it from reviewing/merging

@yonzhan
Copy link
Collaborator

yonzhan commented Oct 30, 2025

Please fix CI issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot customer-reported Issues that are reported by GitHub users external to the Azure organization. SQL az sql

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants