Skip to content

[Backup] Add Azure File Share cross-region restore and soft-delete undelete support - #33809

Open
Bharat Purwar (bharatpurwar) wants to merge 6 commits into
Azure:devfrom
bharatpurwar:bharatpurwar/afs-crr-6d028121
Open

[Backup] Add Azure File Share cross-region restore and soft-delete undelete support#33809
Bharat Purwar (bharatpurwar) wants to merge 6 commits into
Azure:devfrom
bharatpurwar:bharatpurwar/afs-crr-6d028121

Conversation

@bharatpurwar

@bharatpurwar Bharat Purwar (bharatpurwar) commented Jul 29, 2026

Copy link
Copy Markdown
Member

🤖 PR Validation — ⚠️ Review suggested

Breaking Changes Tests
⚠️ None ️✔️ 130/130
⚠️AzureCLI-BreakingChangeTest
⚠️backup
rule cmd_name rule_message suggest_message
⚠️ 1006 - ParaAdd backup restore restore-azurefileshare cmd backup restore restore-azurefileshare added parameter use_secondary_region

Related commands

az backup item list --use-secondary-region
az backup item show --use-secondary-region
az backup recoverypoint list --use-secondary-region
az backup restore restore-azurefileshare --use-secondary-region
az backup job show --use-secondary-region
az backup protection undelete

Description

Adds Azure File Share cross-region restore support for full-share alternate-location restores. The implementation resolves protected items and recovery points from the secondary region, validates the target file share, builds the passive-stamp CRR request with the Azure Storage access token, triggers the restore, and tracks the resulting CRR job.

Adds Azure File Share soft-delete undelete support. Soft-deleted Azure File Share protected items remain discoverable through item listing and can be rehydrated using az backup protection undelete. The command validates the deferred-delete state and submits the protected-item rehydrate request.

Also fixes Azure Storage CRR job formatting for task models without start/end timestamps.

Testing Guide

  • azdev test test_afs_cross_region_restore test_afs_backup_protection_undelete --series --no-exitfirst
  • azdev style backup
  • azdev linter backup

Both scenarios use recordings for CI playback. The undelete scenario uses transient resources through preparers and has a dedicated test_afs_backup_protection_undelete.yaml recording.

History Notes

[Backup] az backup restore restore-azurefileshare: Add Azure File Share cross-region restore support.

[Backup] az backup protection undelete: Add Azure File Share soft-delete undelete support.


Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7a8ea165-11b7-4fc8-9919-ec589073b1fe
@bharatpurwar
Bharat Purwar (bharatpurwar) requested a review from a team as a code owner July 29, 2026 04:06
Copilot AI review requested due to automatic review settings July 29, 2026 04:06
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds Azure File Share cross-region restore (CRR) support to the backup command module, enabling --use-secondary-region for full-share alternate-location restores and validating target file shares before triggering the passive-stamp CRR workflow. Also adjusts CRR job/task formatting to tolerate task models missing timestamps and adds a record-only scenario test + recording.

Changes:

  • Enable CRR restore flow for Azure File Share restores via passive-stamp access token + CRR trigger, tracked as a CRR job.
  • Allow --use-secondary-region for AzureStorage recovery point listing and wire the flag through restore entrypoints/params.
  • Add playback-only scenario test and recording for the AFS CRR restore path; harden CRR task timestamp formatting.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/azure-cli/azure/cli/command_modules/backup/custom_afs.py Implements the AFS CRR restore path (passive stamp) and enables secondary-region recovery point listing; adds target file share validation.
src/azure-cli/azure/cli/command_modules/backup/custom_base.py Threads use_secondary_region into the restore_azurefileshare entrypoint and item resolution.
src/azure-cli/azure/cli/command_modules/backup/custom_common.py Allows AzureStorage to use --use-secondary-region by updating the “not supported” list.
src/azure-cli/azure/cli/command_modules/backup/custom_help.py Makes CRR task timestamp normalization resilient to missing start_time/end_time.
src/azure-cli/azure/cli/command_modules/backup/_params.py Adds --use-secondary-region to backup restore restore-azurefileshare.
src/azure-cli/azure/cli/command_modules/backup/_client_factory.py Introduces a Storage mgmt client factory and a file_shares_cf accessor used for target share validation.
src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py Adds a record-only scenario test covering AFS CRR restore and CRR job tracking.
src/azure-cli/azure/cli/command_modules/backup/tests/latest/recordings/test_afs_cross_region_restore.yaml Adds the test recording for the new scenario.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 18 to +25
from azure.cli.core.util import CLIError
from azure.cli.command_modules.backup._client_factory import protection_containers_cf, protectable_containers_cf, \
protection_policies_cf, backup_protection_containers_cf, backup_protectable_items_cf, \
resources_cf, backup_protected_items_cf, protected_items_cf
from azure.cli.core.azclierror import ArgumentUsageError, ValidationError
resources_cf, backup_protected_items_cf, protected_items_cf, \
recovery_points_crr_cf, recovery_points_passive_cf, aad_properties_cf, cross_region_restore_cf, vaults_cf, \
file_shares_cf
from azure.cli.core.azclierror import ArgumentUsageError, ValidationError, InvalidArgumentValueError
from azure.core.exceptions import ResourceNotFoundError
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7a8ea165-11b7-4fc8-9919-ec589073b1fe
@bharatpurwar Bharat Purwar (bharatpurwar) changed the title [Backup] Add Azure File Share cross-region restore support [Backup] Add Azure File Share cross-region restore and soft-delete undelete support Aug 3, 2026
@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7a8ea165-11b7-4fc8-9919-ec589073b1fe
@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@bharatpurwar

Copy link
Copy Markdown
Member Author

/azp run Azure.azure-cli

@azure-pipelines

Copy link
Copy Markdown
Commenter does not have sufficient privileges for PR 33809 in repo Azure/azure-cli

@yonzhan

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@bharatpurwar

Copy link
Copy Markdown
Member Author

Hi Team, please review the changes

@zubairabid Zubair Abid (zubairabid) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Look good, minor changes unrelated to function.

# CRR path: route through passive stamp (secondary region endpoint)
if use_secondary_region:
vault = vaults_cf(cmd.cli_ctx).get(resource_group_name, vault_name)
import azure.cli.command_modules.backup.custom as custom_module

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: move imports to top of file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed

recovery_points = client.list(vault_name, resource_group_name, fabric_name, container_uri, item_uri, filter_string)
paged_recovery_points = helper.get_list_from_paged_response(recovery_points)
if use_secondary_region:
crr_client = recovery_points_crr_cf(cmd.cli_ctx)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: having both this and the previous cross_region_restore_cf instance be called crr_client can get a bit confusing, useful to disambiguate the naming a bit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7a8ea165-11b7-4fc8-9919-ec589073b1fe
@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@a0x1ab

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@bharatpurwar

Bharat Purwar (bharatpurwar) commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

Hi Team, please review the pr.
Julie Zhu (@yanzhudd)

@a0x1ab Aditya Pujara (a0x1ab) added the Azure Client Tools Agent Requested Request Azure Client Tools Agent testing and review label Aug 19, 2026
@azure-client-tools-agent

Copy link
Copy Markdown
Contributor

Live test results — azdev test --live --series (changed test files only)

FAIL (exit 1)

Selectors: test_afs_commands (module)
PR head ref: bharatpurwar/afs-crr-6d028121
PR head sha: 46929978020bd21ba79cc473b85e9547a041bf7c
PR base ref: dev
New test files in PR: false

Changed test files run
src/azure-cli/azure/cli/command_modules/backup/tests/latest/test_afs_commands.py

Workflow run: https://github.com/Azure/issue-sentinel/actions/runs/32233032941

Last 80 lines of azdev output
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.venv/lib/python3.12/site-packages/azure/core/tracing/decorator.py:119: in wrapper_use_tracer
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <azure.mgmt.resource.resources.v2024_11_01.operations._operations.ResourceGroupsOperations object at 0x7f8004f35fa0>
resource_group_name = 'AzureBackupRG_clitest_5hfhlwimo5'
parameters = <azure.mgmt.resource.resources.v2024_11_01.models._models_py3.ResourceGroup object at 0x7f8004d70860>
kwargs = {}

    @distributed_trace
    def create_or_update(
        self, resource_group_name: str, parameters: Union[_models.ResourceGroup, IO[bytes]], **kwargs: Any
    ) -> _models.ResourceGroup:
        """Creates or updates a resource group.
    
        :param resource_group_name: The name of the resource group to create or update. Can include
         alphanumeric, underscore, parentheses, hyphen, period (except at end), and Unicode characters
         that match the allowed characters. Required.
        :type resource_group_name: str
        :param parameters: Parameters supplied to the create or update a resource group. Is either a
         ResourceGroup type or a IO[bytes] type. Required.
        :type parameters: ~azure.mgmt.resource.resources.v2024_11_01.models.ResourceGroup or IO[bytes]
        :return: ResourceGroup or the result of cls(response)
        :rtype: ~azure.mgmt.resource.resources.v2024_11_01.models.ResourceGroup
        :raises ~azure.core.exceptions.HttpResponseError:
        """
        error_map: MutableMapping = {
            401: ClientAuthenticationError,
            404: ResourceNotFoundError,
            409: ResourceExistsError,
            304: ResourceNotModifiedError,
        }
        error_map.update(kwargs.pop("error_map", {}) or {})
    
        _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {})
        _params = case_insensitive_dict(kwargs.pop("params", {}) or {})
    
        api_version: str = kwargs.pop("api_version", _params.pop("api-version", self._api_version or "2024-11-01"))
        content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None))
        cls: ClsType[_models.ResourceGroup] = kwargs.pop("cls", None)
    
        content_type = content_type or "application/json"
        _json = None
        _content = None
        if isinstance(parameters, (IOBase, bytes)):
            _content = parameters
        else:
            _json = self._serialize.body(parameters, "ResourceGroup")
    
        _request = build_resource_groups_create_or_update_request(
            resource_group_name=resource_group_name,
            subscription_id=self._config.subscription_id,
            api_version=api_version,
            content_type=content_type,
            json=_json,
            content=_content,
            headers=_headers,
            params=_params,
        )
        _request.url = self._client.format_url(_request.url)
    
        _stream = False
        pipeline_response: PipelineResponse = self._client._pipeline.run(  # pylint: disable=protected-access
            _request, stream=_stream, **kwargs
        )
    
        response = pipeline_response.http_response
    
        if response.status_code not in [200, 201]:
            map_error(status_code=response.status_code, response=response, error_map=error_map)
>           raise HttpResponseError(response=response, error_format=ARMErrorFormat)
E           azure.core.exceptions.HttpResponseError: (LocationNotAvailableForResourceGroup) The provided location 'eastus2euap' is not available for resource group. List of available regions is 'eastasia,southeastasia,austriaeast,australiaeast,australiacentral,australiacentral2,australiasoutheast,belgiumcentral,brazilsouth,canadacentral,canadaeast,switzerlandnorth,chilecentral,denmarkeast,germanywestcentral,eastus2,eastus,centralus,northcentralus,francecentral,uksouth,ukwest,indonesiacentral,israelcentral,centralindia,southindia,jioindiawest,italynorth,japaneast,japanwest,koreacentral,koreasouth,malaysiawest,mexicocentral,northeurope,norwayeast,newzealandnorth,polandcentral,qatarcentral,spaincentral,swedencentral,uaenorth,westcentralus,westeurope,westus2,westus,southcentralus,westus3,southafricanorth,westindia,jioindiacentral,indiasouthcentral'.
E           Code: LocationNotAvailableForResourceGroup
E           Message: The provided location 'eastus2euap' is not available for resource group. List of available regions is 'eastasia,southeastasia,austriaeast,australiaeast,australiacentral,australiacentral2,australiasoutheast,belgiumcentral,brazilsouth,canadacentral,canadaeast,switzerlandnorth,chilecentral,denmarkeast,germanywestcentral,eastus2,eastus,centralus,northcentralus,francecentral,uksouth,ukwest,indonesiacentral,israelcentral,centralindia,southindia,jioindiawest,italynorth,japaneast,japanwest,koreacentral,koreasouth,malaysiawest,mexicocentral,northeurope,norwayeast,newzealandnorth,polandcentral,qatarcentral,spaincentral,swedencentral,uaenorth,westcentralus,westeurope,westus2,westus,southcentralus,westus3,southafricanorth,westindia,jioindiacentral,indiasouthcentral'.

.venv/lib/python3.12/site-packages/azure/mgmt/resource/resources/v2024_11_01/operations/_operations.py:5922: HttpResponseError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
============================== 1 failed in 2.38s ===============================

Posted by agent-assist live-test workflow.

@azure-client-tools-agent azure-client-tools-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Live-Test & CI Review Summary — FAILING

CI checks: all 56 checks passed. ✅

Live test (azdev test --live): ❌ FAILED

Please check the linked live-test run output for the specific assertion/error, and update the tests or implementation so azdev test --live passes for the changed Backup test files.

@azure-client-tools-agent azure-client-tools-agent Bot added azure-client-tools-agent Pull request reviewed by Azure Client Tools Agent and removed Azure Client Tools Agent Requested Request Azure Client Tools Agent testing and review labels Aug 19, 2026
@microsoft-github-policy-service

Copy link
Copy Markdown
Contributor

🔔 Routing this PR to @Azure/act-observability-squad.

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

Labels

act-observability-squad Auto-Assign Auto assign by bot azure-client-tools-agent Pull request reviewed by Azure Client Tools Agent Backup needs-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants