-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Add AFS Cross Region Restore support for RSV PowerShell cmdlets #29836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0ddd5d5
60ba083
1c14280
6586be2
504afff
5c2eb65
5716663
6d5b076
910c0fc
70f2ecd
9c2436a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,12 +20,14 @@ | |
| using Microsoft.Azure.Management.Internal.Resources.Models; | ||
| using Microsoft.Azure.Management.RecoveryServices.Backup.Models; | ||
| using Microsoft.Rest.Azure.OData; | ||
| using Newtonsoft.Json; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Management.Automation; | ||
| using BackupManagementType = Microsoft.Azure.Management.RecoveryServices.Backup.Models.BackupManagementType; | ||
| using CmdletModel = Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets.Models; | ||
| using CrrModel = Microsoft.Azure.Management.RecoveryServices.Backup.CrossRegionRestore.Models; | ||
| using RestAzureNS = Microsoft.Rest.Azure; | ||
| using ServiceClientModel = Microsoft.Azure.Management.RecoveryServices.Backup.Models; | ||
|
|
||
|
|
@@ -253,10 +255,24 @@ public RestAzureNS.AzureOperationResponse TriggerRestore() | |
| (string[])ProviderData[RestoreFSBackupItemParams.MultipleSourceFilePath] : null; | ||
| string auxiliaryAccessToken = ProviderData.ContainsKey(ResourceGuardParams.Token) ? (string)ProviderData[ResourceGuardParams.Token] : null; | ||
| bool isMUAOperation = ProviderData.ContainsKey(ResourceGuardParams.IsMUAOperation) ? (bool)ProviderData[ResourceGuardParams.IsMUAOperation] : false; | ||
| bool useSecondaryRegion = (bool)ProviderData[CRRParams.UseSecondaryRegion]; | ||
| string secondaryRegion = useSecondaryRegion ? (string)ProviderData[CRRParams.SecondaryRegion] : null; | ||
|
|
||
| //validate file recovery request | ||
| ValidateFileRestoreRequest(sourceFilePath, sourceFileType, multipleSourceFilePaths); | ||
|
|
||
| // CRR supports only Full Share Restore for AFS; item-level restore is not supported cross-region | ||
| if (useSecondaryRegion && (sourceFilePath != null || multipleSourceFilePaths != null)) | ||
| { | ||
| throw new ArgumentException(Resources.AzureFileShareCrossRegionRestoreItemLevelNotSupported); | ||
| } | ||
|
|
||
| // CRR supports only Alternate Location Restore for AFS; both target storage account and target file share are required | ||
| if (useSecondaryRegion && (targetStorageAccountName == null || targetFileShareName == null)) | ||
| { | ||
| throw new ArgumentException(Resources.AzureFileShareCrossRegionRestoreAlrOnly); | ||
| } | ||
|
|
||
| //validate alternate location restore request | ||
| ValidateLocationRestoreRequest(targetFileShareName, targetStorageAccountName, targetFolder); | ||
|
|
||
|
|
@@ -368,6 +384,29 @@ public RestAzureNS.AzureOperationResponse TriggerRestore() | |
| // check for MUA | ||
| bool isMUAProtected = isMUAOperation; | ||
|
|
||
| // CRR path: serialize restoreRequest to CRR model and trigger cross-region restore | ||
| if (useSecondaryRegion) | ||
| { | ||
| var restoreRequestSerialized = JsonConvert.SerializeObject(restoreRequest); | ||
| CrrModel.AzureFileShareRestoreRequest restoreRequestCrr = | ||
| JsonConvert.DeserializeObject<CrrModel.AzureFileShareRestoreRequest>(restoreRequestSerialized); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This JSON serialize/deserialize bridge silently drops fields if the two models diverge. Please null-check the result and validate key fields (TargetDetails, SourceResourceId) survived. |
||
|
|
||
| CrrModel.CrrAccessToken accessToken = ServiceClientAdapter.GetCRRAccessToken( | ||
| recoveryPoint, secondaryRegion, | ||
| vaultName: vaultName, resourceGroupName: resourceGroupName, | ||
| backupManagementType: ServiceClientModel.BackupManagementType.AzureStorage); | ||
|
|
||
| CrrModel.CrossRegionRestoreRequest crrRestoreRequest = new CrrModel.CrossRegionRestoreRequest(); | ||
| crrRestoreRequest.CrossRegionRestoreAccessDetails = accessToken; | ||
| crrRestoreRequest.RestoreRequest = restoreRequestCrr; | ||
|
|
||
| return ServiceClientAdapter.RestoreDiskSecondryRegion( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two gaps: (1) target-SA region is not validated (the adapter skips the check for AzureStorage), and (2) the MUA auxiliaryAccessToken is dropped here. Please add a region check and either forward the token or fail explicitly. |
||
| recoveryPoint, | ||
| crrRestoreRequest, | ||
| targetStorageAccountLocation, | ||
| secondaryRegion: secondaryRegion); | ||
|
bharatpurwar marked this conversation as resolved.
|
||
| } | ||
|
|
||
| var response = ServiceClientAdapter.RestoreDisk( | ||
| recoveryPoint, | ||
| targetStorageAccountLocation = targetStorageAccountLocation ?? storageAccountResource.Location, | ||
|
|
@@ -955,44 +994,79 @@ public List<ItemBase> ListProtectedItems() | |
| (ItemDeleteState)ProviderData[ItemParams.DeleteState]; | ||
| PolicyBase policy = (PolicyBase)ProviderData[PolicyParams.ProtectionPolicy]; | ||
| string friendlyName = (string)ProviderData[ItemParams.FriendlyName]; | ||
| bool useSecondaryRegion = (bool)ProviderData[CRRParams.UseSecondaryRegion]; | ||
|
|
||
| if( itemName != null && isFriendlyName(itemName) ) | ||
| { | ||
| Logger.Instance.WriteWarning(Resources.FriendlyNamePassedWarning); | ||
| } | ||
|
|
||
| // 1. Filter by container | ||
| List<ProtectedItemResource> protectedItems = AzureWorkloadProviderHelper.ListProtectedItemsByContainer( | ||
| vaultName, | ||
| resourceGroupName, | ||
| container, | ||
| policy, | ||
| ServiceClientModel.BackupManagementType.AzureStorage, | ||
| DataSourceType.AzureFileShare); | ||
|
|
||
| List<ProtectedItemResource> protectedItemGetResponses = | ||
| new List<ProtectedItemResource>(); | ||
|
|
||
| // 2. Filter by item name | ||
| List<ItemBase> itemModels = AzureWorkloadProviderHelper.ListProtectedItemsByItemName( | ||
| protectedItems, | ||
| itemName, | ||
| vaultName, | ||
| resourceGroupName, | ||
| (itemModel, protectedItemGetResponse) => | ||
| { | ||
| AzureFileShareItemExtendedInfo extendedInfo = new AzureFileShareItemExtendedInfo(); | ||
| var serviceClientExtendedInfo = ((AzureFileshareProtectedItem)protectedItemGetResponse.Properties).ExtendedInfo; | ||
| if (serviceClientExtendedInfo.OldestRecoveryPoint.HasValue) | ||
| List<ItemBase> itemModels = null; | ||
|
|
||
| if (useSecondaryRegion) | ||
| { | ||
| // 1. Filter by container from secondary region | ||
| List<CrrModel.ProtectedItemResource> protectedItemsCrr = AzureWorkloadProviderHelper.ListProtectedItemsByContainerCrr( | ||
| vaultName, | ||
| resourceGroupName, | ||
| container, | ||
| policy, | ||
| ServiceClientModel.BackupManagementType.AzureStorage, | ||
| DataSourceType.AzureFileShare); | ||
|
bharatpurwar marked this conversation as resolved.
|
||
|
|
||
| // 2. Filter by item name from secondary region | ||
| itemModels = AzureWorkloadProviderHelper.ListProtectedItemsByItemNameCrr( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ListProtectedItemsByItemNameCrr reads ExtendedInfo from the primary region (helper's own // bug). This PR first activates that path for AFS, so -UseSecondaryRegion will show primary data or fail in a real DR outage — please fix or track explicitly. |
||
| protectedItemsCrr, | ||
| itemName, | ||
| vaultName, | ||
| resourceGroupName, | ||
| (itemModel, protectedItemGetResponse) => | ||
| { | ||
| extendedInfo.OldestRecoveryPoint = serviceClientExtendedInfo.OldestRecoveryPoint; | ||
| } | ||
| extendedInfo.PolicyState = serviceClientExtendedInfo.PolicyState.ToString(); | ||
| extendedInfo.RecoveryPointCount = | ||
| (int)(serviceClientExtendedInfo.RecoveryPointCount.HasValue ? | ||
| serviceClientExtendedInfo.RecoveryPointCount : 0); | ||
| ((AzureFileShareItem)itemModel).ExtendedInfo = extendedInfo; | ||
| }, friendlyName); | ||
| AzureFileShareItemExtendedInfo extendedInfo = new AzureFileShareItemExtendedInfo(); | ||
| var serviceClientExtendedInfo = ((AzureFileshareProtectedItem)protectedItemGetResponse.Properties).ExtendedInfo; | ||
| if (serviceClientExtendedInfo.OldestRecoveryPoint.HasValue) | ||
| { | ||
| extendedInfo.OldestRecoveryPoint = serviceClientExtendedInfo.OldestRecoveryPoint; | ||
| } | ||
| extendedInfo.PolicyState = serviceClientExtendedInfo.PolicyState.ToString(); | ||
| extendedInfo.RecoveryPointCount = | ||
| (int)(serviceClientExtendedInfo.RecoveryPointCount.HasValue ? | ||
| serviceClientExtendedInfo.RecoveryPointCount : 0); | ||
| ((AzureFileShareItem)itemModel).ExtendedInfo = extendedInfo; | ||
| }, friendlyName); | ||
| } | ||
| else | ||
| { | ||
| // 1. Filter by container | ||
| List<ProtectedItemResource> protectedItems = AzureWorkloadProviderHelper.ListProtectedItemsByContainer( | ||
| vaultName, | ||
| resourceGroupName, | ||
| container, | ||
| policy, | ||
| ServiceClientModel.BackupManagementType.AzureStorage, | ||
| DataSourceType.AzureFileShare); | ||
|
|
||
| // 2. Filter by item name | ||
| itemModels = AzureWorkloadProviderHelper.ListProtectedItemsByItemName( | ||
| protectedItems, | ||
| itemName, | ||
| vaultName, | ||
| resourceGroupName, | ||
| (itemModel, protectedItemGetResponse) => | ||
| { | ||
| AzureFileShareItemExtendedInfo extendedInfo = new AzureFileShareItemExtendedInfo(); | ||
| var serviceClientExtendedInfo = ((AzureFileshareProtectedItem)protectedItemGetResponse.Properties).ExtendedInfo; | ||
| if (serviceClientExtendedInfo.OldestRecoveryPoint.HasValue) | ||
| { | ||
| extendedInfo.OldestRecoveryPoint = serviceClientExtendedInfo.OldestRecoveryPoint; | ||
| } | ||
| extendedInfo.PolicyState = serviceClientExtendedInfo.PolicyState.ToString(); | ||
| extendedInfo.RecoveryPointCount = | ||
| (int)(serviceClientExtendedInfo.RecoveryPointCount.HasValue ? | ||
| serviceClientExtendedInfo.RecoveryPointCount : 0); | ||
| ((AzureFileShareItem)itemModel).ExtendedInfo = extendedInfo; | ||
| }, friendlyName); | ||
| } | ||
|
|
||
| // 3. Filter by item's Protection Status | ||
| if (protectionStatus != 0) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Soft-delete parity: primary ctor sets DateOfPurge/SoftDeleteRetentionPeriodInDays, but the CRR ctor leaves them null. Please compute DateOfPurge here so secondary-region items match.