From f4a12e9944e9c928ecfdb1dd0bdf3771c09ace29 Mon Sep 17 00:00:00 2001 From: Dingmeng Xue Date: Fri, 21 May 2021 15:38:33 +0800 Subject: [PATCH] Adding Cross Tenant DS move updated help text added new CRR regions --- .../AzureWorkloadProviderHelper.cs | 12 -- .../BMSAPIs/VaultAPIs.cs | 51 ++++++ .../Vault/CopyAzureRmRecoveryServicesVault.cs | 135 +++++++--------- ...InitializeAzureRMRecoveryServicesDSMove.cs | 98 ++++++++++++ .../TestAzureRMRecoveryServicesDSMove.cs | 117 ++++++++++++++ .../Helpers/BackupUtils.cs | 12 ++ .../RecoveryServices.Backup/ParamHelpMsgs.cs | 1 + .../RecoveryServices/Az.RecoveryServices.psd1 | 7 +- .../RecoveryServices/ChangeLog.md | 3 + .../help/Az.RecoveryServices.md | 6 + .../help/Copy-AzRecoveryServicesVault.md | 31 +++- .../Initialize-AzRecoveryServicesDSMove.md | 149 +++++++++++++++++ .../help/Test-AzRecoveryServicesDSMove.md | 150 ++++++++++++++++++ 13 files changed, 675 insertions(+), 97 deletions(-) create mode 100644 src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/InitializeAzureRMRecoveryServicesDSMove.cs create mode 100644 src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/TestAzureRMRecoveryServicesDSMove.cs create mode 100644 src/RecoveryServices/RecoveryServices/help/Initialize-AzRecoveryServicesDSMove.md create mode 100644 src/RecoveryServices/RecoveryServices/help/Test-AzRecoveryServicesDSMove.md diff --git a/src/RecoveryServices/RecoveryServices.Backup.Providers/AzureWorkloadProviderHelper.cs b/src/RecoveryServices/RecoveryServices.Backup.Providers/AzureWorkloadProviderHelper.cs index ca0756b8645a..9cce0299991c 100644 --- a/src/RecoveryServices/RecoveryServices.Backup.Providers/AzureWorkloadProviderHelper.cs +++ b/src/RecoveryServices/RecoveryServices.Backup.Providers/AzureWorkloadProviderHelper.cs @@ -379,12 +379,6 @@ public List ListRecoveryPoints(Dictionary Provi string containerUri = HelperUtils.GetContainerUri(uriDict, item.Id); string protectedItemName = HelperUtils.GetProtectedItemUri(uriDict, item.Id); - TimeSpan duration = endDate - startDate; - if (duration.TotalDays > 30) - { - throw new Exception(Resources.RestoreDiskTimeRangeError); - } - //we need to fetch the list of RPs var queryFilterString = "null"; if (string.Compare(restorePointQueryType, "All") == 0) @@ -448,12 +442,6 @@ public List ListLogChains(Dictionary ProviderData string containerUri = HelperUtils.GetContainerUri(uriDict, item.Id); string protectedItemName = HelperUtils.GetProtectedItemUri(uriDict, item.Id); - TimeSpan duration = endDate - startDate; - if (duration.TotalDays > 30) - { - throw new Exception(Resources.RestoreDiskTimeRangeError); - } - //we need to fetch the list of RPs var queryFilterString = QueryBuilder.Instance.GetQueryString(new BMSRPQueryObject() { diff --git a/src/RecoveryServices/RecoveryServices.Backup.ServiceClientAdapter/BMSAPIs/VaultAPIs.cs b/src/RecoveryServices/RecoveryServices.Backup.ServiceClientAdapter/BMSAPIs/VaultAPIs.cs index 966c60df8bd6..dbd7a348a864 100644 --- a/src/RecoveryServices/RecoveryServices.Backup.ServiceClientAdapter/BMSAPIs/VaultAPIs.cs +++ b/src/RecoveryServices/RecoveryServices.Backup.ServiceClientAdapter/BMSAPIs/VaultAPIs.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.Azure.Commands.RecoveryServices.Backup.Helpers; using Microsoft.Azure.Management.RecoveryServices.Backup.Models; using Microsoft.Azure.Management.RecoveryServices.Models; using Microsoft.Rest.Azure.OData; @@ -114,5 +115,55 @@ public AADPropertiesResource GetAADProperties(string azureRegion, string backupM AADPropertiesResource aadProperties = BmsAdapter.Client.AadProperties.GetWithHttpMessagesAsync(azureRegion, queryParams).Result.Body; return aadProperties; } + + /// + /// This method prepares the source vault for Data Move operation. + /// + /// + /// + /// + public string PrepareDataMove(string vaultName, string resourceGroupName, PrepareDataMoveRequest prepareMoveRequest) + { + // prepare move + var prepareMoveOperationResponse = BmsAdapter.Client.BeginBMSPrepareDataMoveWithHttpMessagesAsync( + vaultName, resourceGroupName, prepareMoveRequest).Result; + + // track prepare-move operation to success + var operationStatus = TrackingHelpers.GetOperationStatusDataMove( + prepareMoveOperationResponse, + operationId => GetDataMoveOperationStatus(operationId, vaultName, resourceGroupName)); + + Logger.Instance.WriteDebug("Prepare move operation: " + operationStatus.Body.Status); + + // get the correlation Id and return it for trigger data move + var operationResult = TrackingHelpers.GetCorrelationId( + prepareMoveOperationResponse, + operationId => GetPrepareDataMoveOperationResult(operationId, vaultName, resourceGroupName)); + + Logger.Instance.WriteDebug("Prepare move - correlationId:" + operationResult.CorrelationId); + + return operationResult.CorrelationId; + } + + /// + /// This method triggers the Data Move operation on Target vault. + /// + /// + /// + /// + public void TriggerDataMove(string vaultName, string resourceGroupName, TriggerDataMoveRequest triggerMoveRequest) + { + //trigger move + var triggerMoveOperationResponse = BmsAdapter.Client.BeginBMSTriggerDataMoveWithHttpMessagesAsync( + vaultName, resourceGroupName, triggerMoveRequest).Result; + + // track trigger-move operation to success + var operationStatus = TrackingHelpers.GetOperationStatusDataMove( + triggerMoveOperationResponse, + operationId => GetDataMoveOperationStatus(operationId, vaultName, resourceGroupName)); + + Logger.Instance.WriteDebug("Trigger move operation: " + operationStatus.Body.Status); + + } } } diff --git a/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/CopyAzureRmRecoveryServicesVault.cs b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/CopyAzureRmRecoveryServicesVault.cs index ea1c4cd07e2f..30d03e4857ef 100644 --- a/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/CopyAzureRmRecoveryServicesVault.cs +++ b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/CopyAzureRmRecoveryServicesVault.cs @@ -26,15 +26,21 @@ namespace Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets /// /// Used for Data Source Move operation. Currently we only support vault level data move from one region to another. /// - [Cmdlet("Copy", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "RecoveryServicesVault", SupportsShouldProcess = true), OutputType(typeof(String))] + [Cmdlet("Copy", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "RecoveryServicesVault", + DefaultParameterSetName = AzureRSVaultDataMoveParameterSet, SupportsShouldProcess = true), OutputType(typeof(String))] public class CopyAzureRmRecoveryServicesVault : RecoveryServicesBackupCmdletBase { #region Parameters + internal const string AzureRSVaultDataMoveParameterSet = "AzureRSVaultDataMoveParameterSet"; + internal const string AzureRSVaultTriggerMoveParameterSet = "AzureRSVaultTriggerMoveParameterSet"; + /// /// Source Vault for Data Move Operation /// - [Parameter(Position = 1, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.SourceVault, + [Parameter(Position = 1, Mandatory = true, ParameterSetName = AzureRSVaultDataMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.SourceVault, + ValueFromPipeline = true)] + [Parameter(Position = 1, Mandatory = true, ParameterSetName = AzureRSVaultTriggerMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.SourceVault, ValueFromPipeline = true)] [ValidateNotNullOrEmpty] public ARSVault SourceVault; @@ -42,7 +48,9 @@ public class CopyAzureRmRecoveryServicesVault : RecoveryServicesBackupCmdletBase /// /// Target Vault for Data Move Operation /// - [Parameter(Position = 2, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.TargetVault, + [Parameter(Position = 2, Mandatory = true, ParameterSetName = AzureRSVaultDataMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.TargetVault, + ValueFromPipeline = true)] + [Parameter(Position = 2, Mandatory = true, ParameterSetName = AzureRSVaultTriggerMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.TargetVault, ValueFromPipeline = true)] [ValidateNotNullOrEmpty] public ARSVault TargetVault; @@ -50,15 +58,22 @@ public class CopyAzureRmRecoveryServicesVault : RecoveryServicesBackupCmdletBase /// /// Retries data move only with unmoved containers in the source vault /// - [Parameter(Mandatory = false, HelpMessage = ParamHelpMsgs.DSMove.RetryOnlyFailed)] + [Parameter(Mandatory = false, ParameterSetName = AzureRSVaultDataMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.RetryOnlyFailed)] public SwitchParameter RetryOnlyFailed; /// /// Prevents the confirmation dialog when specified. /// - [Parameter(Mandatory = false, HelpMessage = ParamHelpMsgs.DSMove.ForceOption)] + [Parameter(Mandatory = false, ParameterSetName = AzureRSVaultDataMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.ForceOption)] + [Parameter(Mandatory = false, ParameterSetName = AzureRSVaultTriggerMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.ForceOption)] public SwitchParameter Force { get; set; } + /// + /// Prevents the confirmation dialog when specified. + /// + [Parameter(Mandatory = true, ParameterSetName = AzureRSVaultTriggerMoveParameterSet, HelpMessage = ParamHelpMsgs.DSMove.CorrelationId)] + public String CorrelationIdForDataMove { get; set; } + #endregion Parameters public override void ExecuteCmdlet() @@ -99,13 +114,14 @@ public override void ExecuteCmdlet() { throw new ArgumentException(string.Format(Resources.TargetVaultNotEmptyException)); } - + // Confirm the target vault storage type BackupResourceConfigResource getStorageResponse = ServiceClientAdapter.GetVaultStorageType( TargetVault.ResourceGroupName, TargetVault.Name); - Logger.Instance.WriteDebug("Storage Type: " + getStorageResponse.Properties.StorageType); - + Logger.Instance.WriteDebug("Storage Type: " + getStorageResponse.Properties.StorageType); + + string correlationId = ""; ConfirmAction( Force.IsPresent, string.Format(Resources.TargetVaultStorageRedundancy, TargetVault.Name, getStorageResponse.Properties.StorageType), @@ -114,31 +130,38 @@ public override void ExecuteCmdlet() { base.ExecuteCmdlet(); - // Prepare Data Move - ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = sourceSub; // set source subscription - PrepareDataMoveRequest prepareMoveRequest = new PrepareDataMoveRequest(); - prepareMoveRequest.TargetResourceId = TargetVault.ID; - prepareMoveRequest.TargetRegion = TargetVault.Location; - - /// currently only allowing vault level data move - prepareMoveRequest.DataMoveLevel = "Vault"; - - if (RetryOnlyFailed.IsPresent) + if (string.Compare(ParameterSetName, AzureRSVaultDataMoveParameterSet) == 0) { - prepareMoveRequest.IgnoreMoved = true; + // Prepare Data Move + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = sourceSub; + PrepareDataMoveRequest prepareMoveRequest = new PrepareDataMoveRequest(); + prepareMoveRequest.TargetResourceId = TargetVault.ID; + prepareMoveRequest.TargetRegion = TargetVault.Location; + + /// currently only allowing vault level data move + prepareMoveRequest.DataMoveLevel = "Vault"; + + if (RetryOnlyFailed.IsPresent) + { + prepareMoveRequest.IgnoreMoved = true; + } + else + { + prepareMoveRequest.IgnoreMoved = false; + } + + Logger.Instance.WriteDebug("Retry only with failed items : " + prepareMoveRequest.IgnoreMoved); + Logger.Instance.WriteDebug("Location of Target vault: " + TargetVault.Location); + + correlationId = ServiceClientAdapter.PrepareDataMove(SourceVault.Name, SourceVault.ResourceGroupName, prepareMoveRequest); } else { - prepareMoveRequest.IgnoreMoved = false; + correlationId = CorrelationIdForDataMove; } - Logger.Instance.WriteDebug("Retry only with failed items : " + prepareMoveRequest.IgnoreMoved); - Logger.Instance.WriteDebug("Location of Target vault: " + TargetVault.Location); - - string correlationId = PrepareDataMove(SourceVault.Name, SourceVault.ResourceGroupName, prepareMoveRequest); - // Trigger Data Move - ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = targetSub; // set target subscription + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = targetSub; TriggerDataMoveRequest triggerMoveRequest = new TriggerDataMoveRequest(); triggerMoveRequest.SourceResourceId = SourceVault.ID; triggerMoveRequest.SourceRegion = SourceVault.Location; @@ -149,63 +172,15 @@ public override void ExecuteCmdlet() triggerMoveRequest.PauseGC = false; Logger.Instance.WriteDebug("Location of Source vault: " + SourceVault.Location); - TriggerDataMove(TargetVault.Name, TargetVault.ResourceGroupName, triggerMoveRequest); + ServiceClientAdapter.TriggerDataMove(TargetVault.Name, TargetVault.ResourceGroupName, triggerMoveRequest); + + // set subscription to original + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = subscriptionContext; - ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = subscriptionContext; // set subscription to original - WriteObject(ParamHelpMsgs.DSMove.CmdletOutput); + WriteObject(ParamHelpMsgs.DSMove.CmdletOutput); } - ); + ); }, ShouldProcess(TargetVault.Name, VerbsCommon.Set)); } - - /// - /// This method prepares the source vault for Data Move operation. - /// - /// - /// - /// - public string PrepareDataMove(string vaultName, string resourceGroupName, PrepareDataMoveRequest prepareMoveRequest) - { - // prepare move - var prepareMoveOperationResponse = ServiceClientAdapter.BmsAdapter.Client.BeginBMSPrepareDataMoveWithHttpMessagesAsync( - vaultName, resourceGroupName, prepareMoveRequest).Result; - - // track prepare-move operation to success - var operationStatus = TrackingHelpers.GetOperationStatusDataMove( - prepareMoveOperationResponse, - operationId => ServiceClientAdapter.GetDataMoveOperationStatus(operationId, vaultName, resourceGroupName)); - - Logger.Instance.WriteDebug("Prepare move operation: " + operationStatus.Body.Status); - - // get the correlation Id and return it for trigger data move - var operationResult = TrackingHelpers.GetCorrelationId( - prepareMoveOperationResponse, - operationId => ServiceClientAdapter.GetPrepareDataMoveOperationResult(operationId, vaultName, resourceGroupName)); - - Logger.Instance.WriteDebug("Prepare move - correlationId:" + operationResult.CorrelationId); - - return operationResult.CorrelationId; - } - - /// - /// This method triggers the Data Move operation on Target vault. - /// - /// - /// - /// - public void TriggerDataMove(string vaultName, string resourceGroupName, TriggerDataMoveRequest triggerMoveRequest) - { - //trigger move - var triggerMoveOperationResponse = ServiceClientAdapter.BmsAdapter.Client.BeginBMSTriggerDataMoveWithHttpMessagesAsync( - vaultName, resourceGroupName, triggerMoveRequest).Result; - - // track trigger-move operation to success - var operationStatus = TrackingHelpers.GetOperationStatusDataMove( - triggerMoveOperationResponse, - operationId => ServiceClientAdapter.GetDataMoveOperationStatus(operationId, vaultName, resourceGroupName)); - - Logger.Instance.WriteDebug("Trigger move operation: " + operationStatus.Body.Status); - - } } } diff --git a/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/InitializeAzureRMRecoveryServicesDSMove.cs b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/InitializeAzureRMRecoveryServicesDSMove.cs new file mode 100644 index 000000000000..1ae23a320b88 --- /dev/null +++ b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/InitializeAzureRMRecoveryServicesDSMove.cs @@ -0,0 +1,98 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Management.Automation; +using Microsoft.Azure.Management.RecoveryServices.Backup.Models; +using Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets.ServiceClientAdapterNS; +using Microsoft.Azure.Commands.RecoveryServices.Backup.Helpers; +using System.Collections.Generic; +using CmdletModel = Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets.Models; + +namespace Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets +{ + /// + /// Used for Data Source Move operation. Currently we only support vault level data move from one region to another. + /// + [Cmdlet("Initialize", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "RecoveryServicesDSMove", SupportsShouldProcess = true), OutputType(typeof(String))] + public class InitializeAzureRMRecoveryServicesDSMove : RecoveryServicesBackupCmdletBase + { + #region Parameters + /// + /// Source Vault for Data Move Operation + /// + [Parameter(Position = 1, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.SourceVault, + ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public ARSVault SourceVault; + + /// + /// Target Vault for Data Move Operation + /// + [Parameter(Position = 2, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.TargetVault, + ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public ARSVault TargetVault; + + /// + /// Retries data move only with unmoved containers in the source vault + /// + [Parameter(Mandatory = false, HelpMessage = ParamHelpMsgs.DSMove.RetryOnlyFailed)] + public SwitchParameter RetryOnlyFailed; + + #endregion Parameters + + public override void ExecuteCmdlet() + { + ExecutionBlock(() => + { + base.ExecuteCmdlet(); + + // fetch source vault and target vault subscription + Dictionary SourceVaultDict = HelperUtils.ParseUri(SourceVault.ID); + string sourceSub = SourceVaultDict[CmdletModel.UriEnums.Subscriptions]; + + // change subscription for HTTP requests + string subscriptionContext = ServiceClientAdapter.BmsAdapter.Client.SubscriptionId; + + // Prepare Data Move + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = sourceSub; + PrepareDataMoveRequest prepareMoveRequest = new PrepareDataMoveRequest(); + prepareMoveRequest.TargetResourceId = TargetVault.ID; + prepareMoveRequest.TargetRegion = TargetVault.Location; + + /// currently only allowing vault level data move + prepareMoveRequest.DataMoveLevel = "Vault"; + + if (RetryOnlyFailed.IsPresent) + { + prepareMoveRequest.IgnoreMoved = true; + } + else + { + prepareMoveRequest.IgnoreMoved = false; + } + + Logger.Instance.WriteDebug("Retry only with failed items : " + prepareMoveRequest.IgnoreMoved); + Logger.Instance.WriteDebug("Location of Target vault: " + TargetVault.Location); + + /* move Prepare move function to vault APIs */ + string correlationId = ServiceClientAdapter.PrepareDataMove(SourceVault.Name, SourceVault.ResourceGroupName, prepareMoveRequest); + + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = subscriptionContext; // set subscription to original + WriteObject(correlationId); + }, ShouldProcess(TargetVault.Name, VerbsCommon.Set)); + } + } +} diff --git a/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/TestAzureRMRecoveryServicesDSMove.cs b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/TestAzureRMRecoveryServicesDSMove.cs new file mode 100644 index 000000000000..ffbdb70d5c22 --- /dev/null +++ b/src/RecoveryServices/RecoveryServices.Backup/Cmdlets/Vault/TestAzureRMRecoveryServicesDSMove.cs @@ -0,0 +1,117 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Management.Automation; +using Microsoft.Azure.Management.RecoveryServices.Backup.Models; +using Microsoft.Azure.Commands.RecoveryServices.Backup.Properties; +using System.Collections.Generic; +using CmdletModel = Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets.Models; +using Microsoft.Azure.Commands.RecoveryServices.Backup.Helpers; + +namespace Microsoft.Azure.Commands.RecoveryServices.Backup.Cmdlets +{ + /// + /// Used for validtaing Data Source Move operation. The command runs successfully if the DS move is feasible. + /// + [Cmdlet("Test", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "RecoveryServicesDSMove", SupportsShouldProcess = true), OutputType(typeof(Boolean))] + public class TestAzureRMRecoveryServicesDSMove : RecoveryServicesBackupCmdletBase + { + #region Parameters + /// + /// Source Vault for Data Move Operation + /// + [Parameter(Position = 1, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.SourceVault, + ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public ARSVault SourceVault; + + /// + /// Target Vault for Data Move Operation + /// + [Parameter(Position = 2, Mandatory = true, HelpMessage = ParamHelpMsgs.DSMove.TargetVault, + ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public ARSVault TargetVault; + + /// + /// Prevents the confirmation dialog when specified. + /// + [Parameter(Mandatory = false, HelpMessage = ParamHelpMsgs.DSMove.ForceOption)] + public SwitchParameter Force { get; set; } + + #endregion Parameters + + public override void ExecuteCmdlet() + { + ExecutionBlock(() => + { + // fetch source vault and target vault subscription + Dictionary TargetVaultDict = HelperUtils.ParseUri(TargetVault.ID); + string targetSub = TargetVaultDict[CmdletModel.UriEnums.Subscriptions]; + + // set target subscription + string subscriptionContext = ServiceClientAdapter.BmsAdapter.Client.SubscriptionId; + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = targetSub; + + + // Check if the Target vault is empty + /// Check the containers count in target vault + var protectionContainersCount = BackupUtils.GetProtectionContainersCount(TargetVault.Name, TargetVault.ResourceGroupName, ServiceClientAdapter); + + Logger.Instance.WriteDebug("Protection Containers within vault: " + TargetVault.Name + " and resource Group: " + + TargetVault.ResourceGroupName + " are " + protectionContainersCount); + + if (protectionContainersCount > 0) + { + throw new ArgumentException(string.Format(Resources.TargetVaultNotEmptyException)); + } + + /// check the count for VM backupItems + + int vmItemsCount = BackupUtils.GetProtectedItems(TargetVault.Name, TargetVault.ResourceGroupName, + BackupManagementType.AzureIaasVM, WorkloadType.VM, ServiceClientAdapter).Count; + + Logger.Instance.WriteDebug("Protected VMs within vault: " + TargetVault.Name + " and resource Group: " + + TargetVault.ResourceGroupName + " are " + vmItemsCount); + + if (vmItemsCount > 0) + { + throw new ArgumentException(string.Format(Resources.TargetVaultNotEmptyException)); + } + + // Confirm the target vault storage type + BackupResourceConfigResource getStorageResponse = ServiceClientAdapter.GetVaultStorageType( + TargetVault.ResourceGroupName, TargetVault.Name); + + Logger.Instance.WriteDebug("Target vault storage type: " + getStorageResponse.Properties.StorageType); + + // set subscription to original + ServiceClientAdapter.BmsAdapter.Client.SubscriptionId = subscriptionContext; + + ConfirmAction( + Force.IsPresent, + string.Format(Resources.TargetVaultStorageRedundancy, TargetVault.Name, getStorageResponse.Properties.StorageType), + Resources.TargetVaultStorageRedundancy, + getStorageResponse.Properties.StorageType, () => + { + base.ExecuteCmdlet(); + + WriteObject(true); + } + ); + }, ShouldProcess(TargetVault.Name, VerbsCommon.Set)); + } + } +} diff --git a/src/RecoveryServices/RecoveryServices.Backup/Helpers/BackupUtils.cs b/src/RecoveryServices/RecoveryServices.Backup/Helpers/BackupUtils.cs index d752dee399c4..429528c30e5d 100644 --- a/src/RecoveryServices/RecoveryServices.Backup/Helpers/BackupUtils.cs +++ b/src/RecoveryServices/RecoveryServices.Backup/Helpers/BackupUtils.cs @@ -26,6 +26,17 @@ public class BackupUtils /// secondary region mapping /// public static Dictionary regionMap = new Dictionary(){ + {"ussecwest", "usseceast"}, + {"usseceast", "ussecwest"}, + {"usnateast", "usnatwest"}, + {"usnatwest", "usnateast"}, + {"swedencentral", "swedensouth"}, + {"swedensouth", "swedencentral"}, + {"norwaywest", "norwayeast"}, + {"norwayeast", "norwaywest"}, + {"germanynorth", "germanywestcentral"}, + {"germanywestcentral", "germanynorth"}, + {"westus3", "eastus"}, {"eastasia", "southeastasia"}, {"southeastasia", "eastasia"}, {"australiaeast", "australiasoutheast"}, @@ -33,6 +44,7 @@ public class BackupUtils {"australiacentral", "australiacentral2"}, {"australiacentral2", "australiacentral"}, {"brazilsouth", "southcentralus"}, + {"brazilsoutheast", "brazilsouth"}, {"canadacentral", "canadaeast"}, {"canadaeast", "canadacentral"}, {"chinanorth", "chinaeast"}, diff --git a/src/RecoveryServices/RecoveryServices.Backup/ParamHelpMsgs.cs b/src/RecoveryServices/RecoveryServices.Backup/ParamHelpMsgs.cs index dd5bbfdd03de..e22aea0ac10f 100644 --- a/src/RecoveryServices/RecoveryServices.Backup/ParamHelpMsgs.cs +++ b/src/RecoveryServices/RecoveryServices.Backup/ParamHelpMsgs.cs @@ -197,6 +197,7 @@ internal static class DSMove public const string ForceOption = "Forces the data move operation (prevents confirmation dialog). This parameter is optional."; public const string CmdletOutput = "Please monitor the operation using Get-AzRecoveryServicesBackupJob cmdlet"; public const string RetryOnlyFailed = "Switch parameter to try data move only for containers in the source vault which are not yet moved."; + public const string CorrelationId = "Correlation Id for triggering DS Move"; } internal static class Encryption diff --git a/src/RecoveryServices/RecoveryServices/Az.RecoveryServices.psd1 b/src/RecoveryServices/RecoveryServices/Az.RecoveryServices.psd1 index 00287534d246..0e46a3f12277 100644 --- a/src/RecoveryServices/RecoveryServices/Az.RecoveryServices.psd1 +++ b/src/RecoveryServices/RecoveryServices/Az.RecoveryServices.psd1 @@ -192,9 +192,12 @@ CmdletsToExport = 'Get-AzRecoveryServicesBackupProperty', 'Undo-AzRecoveryServicesBackupItemDeletion', 'Set-AzRecoveryServicesVaultProperty', 'Get-AzRecoveryServicesVaultProperty', - 'Copy-AzRecoveryServicesVault', 'Update-AzRecoveryServicesVault', + 'Copy-AzRecoveryServicesVault', + 'Update-AzRecoveryServicesVault', 'New-AzRecoveryServicesAsrInMageRcmDiskInput', - 'Start-AzRecoveryServicesAsrCancelFailoverJob' + 'Start-AzRecoveryServicesAsrCancelFailoverJob', + 'Test-AzRecoveryServicesDSMove', + 'Initialize-AzRecoveryServicesDSMove' # Variables to export from this module # VariablesToExport = @() diff --git a/src/RecoveryServices/RecoveryServices/ChangeLog.md b/src/RecoveryServices/RecoveryServices/ChangeLog.md index 737e2b0244df..0f69edded60b 100644 --- a/src/RecoveryServices/RecoveryServices/ChangeLog.md +++ b/src/RecoveryServices/RecoveryServices/ChangeLog.md @@ -18,6 +18,9 @@ - Additional information about change #1 --> ## Upcoming Release +* Added cross tenant DS Move. +* Removed restrition to fetch recovery points only for a 30 days time range. +* Enabled CRR for new regions. ## Version 4.1.0 * Fixed security issue with SQL restore, this is a necessary breaking change. TargetContainer becomes mandatory for Alternate Location Restore. diff --git a/src/RecoveryServices/RecoveryServices/help/Az.RecoveryServices.md b/src/RecoveryServices/RecoveryServices/help/Az.RecoveryServices.md index 03bca3162116..4e232b488894 100644 --- a/src/RecoveryServices/RecoveryServices/help/Az.RecoveryServices.md +++ b/src/RecoveryServices/RecoveryServices/help/Az.RecoveryServices.md @@ -152,6 +152,9 @@ Imports the specified ASR vault settings file to set the vault context(PowerShel ### [Initialize-AzRecoveryServicesBackupProtectableItem](Initialize-AzRecoveryServicesBackupProtectableItem.md) This command triggers the discovery of any unprotected items of the given workload type in the given container. If the DB application is not auto-protected use this command to discover new DBs whenever they are added and proceed to protect them. +### [Initialize-AzRecoveryServicesDSMove](Initialize-AzRecoveryServicesDSMove.md) +Initializes DS move for Copy-AzRecoveryServicesVault. + ### [New-AzRecoveryServicesAsrAzureToAzureDiskReplicationConfig](New-AzRecoveryServicesAsrAzureToAzureDiskReplicationConfig.md) Creates a disk mapping object for Azure virtual machine disks to be replicated. @@ -300,6 +303,9 @@ Stops an Azure Site Recovery job. ### [Stop-AzRecoveryServicesBackupJob](Stop-AzRecoveryServicesBackupJob.md) Cancels a running job. +### [Test-AzRecoveryServicesDSMove](Test-AzRecoveryServicesDSMove.md) +This cmdlet performs necessary validations for DS Move. + ### [Undo-AzRecoveryServicesBackupItemDeletion](Undo-AzRecoveryServicesBackupItemDeletion.md) If a backup item is deleted and present in a soft-deleted state, this command brings the item back to a state where the data is retained forever diff --git a/src/RecoveryServices/RecoveryServices/help/Copy-AzRecoveryServicesVault.md b/src/RecoveryServices/RecoveryServices/help/Copy-AzRecoveryServicesVault.md index 72035512eebb..29ca83683700 100644 --- a/src/RecoveryServices/RecoveryServices/help/Copy-AzRecoveryServicesVault.md +++ b/src/RecoveryServices/RecoveryServices/help/Copy-AzRecoveryServicesVault.md @@ -12,18 +12,27 @@ Copies data from a vault in one region to a vault in another region. ## SYNTAX +### AzureRSVaultDataMoveParameterSet ``` Copy-AzRecoveryServicesVault [-Force] [-DefaultProfile ] [-SourceVault] [-TargetVault] [-RetryOnlyFailed] [-WhatIf] [-Confirm] [] ``` +### AzureRSVaultTriggerMoveParameterSet +``` +Copy-AzRecoveryServicesVault [-Force] -CorrelationIdForDataMove + [-DefaultProfile ] [-SourceVault] [-TargetVault] [-WhatIf] + [-Confirm] [] +``` + ## DESCRIPTION The **Copy-AzRecoveryServicesVault** cmdlet copies data from a vault in one region to a vault in another region. Currently we only support vault level data move. ## EXAMPLES ### Example 1: Copy data from vault1 to vault2 -``` + +```powershell PS C:\> $sourceVault = Get-AzRecoveryServicesVault -ResourceGroupName "rgName1" -Name "vault1" PS C:\> $targetVault = Get-AzRecoveryServicesVault -ResourceGroupName "rgName2" -Name "vault2" PS C:\> Copy-AzRecoveryServicesVault -SourceVault $sourceVault -TargetVault $targetVault @@ -33,7 +42,8 @@ The first two cmdlets fetch Recovery Services Vault - vault1 and vault2 respecti $sourceVault and $targetVault can also belong to different subscription within same tanent, can be fetched by setting different subscription contexts. ### Example 2: Copy data from vault1 to vault2 with only failed items -``` + +```powershell PS C:\> $sourceVault = Get-AzRecoveryServicesVault -ResourceGroupName "rgName1" -Name "vault1" PS C:\> $targetVault = Get-AzRecoveryServicesVault -ResourceGroupName "rgName2" -Name "vault2" PS C:\> Copy-AzRecoveryServicesVault -SourceVault $sourceVault -TargetVault $targetVault -RetryOnlyFailed @@ -45,6 +55,21 @@ $sourceVault and $targetVault can also belong to different subscription within s ## PARAMETERS +### -CorrelationIdForDataMove +Correlation Id for triggering DS Move. + +```yaml +Type: System.String +Parameter Sets: AzureRSVaultTriggerMoveParameterSet +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DefaultProfile The credentials, account, tenant, and subscription used for communication with Azure. @@ -80,7 +105,7 @@ Switch parameter to try data move only for containers in the source vault which ```yaml Type: System.Management.Automation.SwitchParameter -Parameter Sets: (All) +Parameter Sets: AzureRSVaultDataMoveParameterSet Aliases: Required: False diff --git a/src/RecoveryServices/RecoveryServices/help/Initialize-AzRecoveryServicesDSMove.md b/src/RecoveryServices/RecoveryServices/help/Initialize-AzRecoveryServicesDSMove.md new file mode 100644 index 000000000000..165fb180cddd --- /dev/null +++ b/src/RecoveryServices/RecoveryServices/help/Initialize-AzRecoveryServicesDSMove.md @@ -0,0 +1,149 @@ +--- +external help file: Microsoft.Azure.PowerShell.Cmdlets.RecoveryServices.Backup.dll-Help.xml +Module Name: Az.RecoveryServices +online version: https://docs.microsoft.com/powershell/module/az.recoveryservices/initialize-azrecoveryservicesdsmove +schema: 2.0.0 +--- + +# Initialize-AzRecoveryServicesDSMove + +## SYNOPSIS +Initializes DS move for Copy-AzRecoveryServicesVault. + +## SYNTAX + +``` +Initialize-AzRecoveryServicesDSMove [-DefaultProfile ] [-SourceVault] + [-TargetVault] [-RetryOnlyFailed] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Initializes DS move for Copy-AzRecoveryServicesVault. It is mandatory to run Test-AzRecoveryServicesDSMove +cmdlet before this cmdlet. This cmdlet generates a Correlation Id which can be used as Input to +Copy-AzRecoveryServicesVault cmdlet. This cmdlet is useful for cross tenant DS move scenario. + +## EXAMPLES + +### Example 1: Initialize DS Move for cross subscription copy +```powershell +PS C:\> Set-AzContext -SubscriptionName $targetSubscription +PS C:\> $validated = Test-AzRecoveryServicesDSMove -SourceVault $srcVault -TargetVault $trgVault -Force +PS C:\> Set-AzContext -SubscriptionName $sourceSubscription +PS C:\> if($validated) { +>> $corr = Initialize-AzRecoveryServicesDSMove -SourceVault $srcVault -TargetVault $trgVault +>> } +``` + +First cmdlet sets target subscription context. +Second cmdlet triggers some mandatory validations on target vault. +Third cmdlet sets source subscription context. +Then based on Test-AzRecoveryServicesDSMove cmdlet state, we fetch CorrelationId using +Initialize-AzRecoveryServicesDSMove cmdlet. $corr can be input to the Copy cmdlet. + +## PARAMETERS + +### -DefaultProfile +The credentials, account, tenant, and subscription used for communication with Azure. + +```yaml +Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer +Parameter Sets: (All) +Aliases: AzContext, AzureRmContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -RetryOnlyFailed +Switch parameter to try data move only for containers in the source vault which are not yet moved. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SourceVault +The source vault object to trigger data move. + +```yaml +Type: Microsoft.Azure.Commands.RecoveryServices.ARSVault +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -TargetVault +The target vault object where the data has to be moved. + +```yaml +Type: Microsoft.Azure.Commands.RecoveryServices.ARSVault +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### Microsoft.Azure.Commands.RecoveryServices.ARSVault + +## OUTPUTS + +### System.String + +## NOTES + +## RELATED LINKS diff --git a/src/RecoveryServices/RecoveryServices/help/Test-AzRecoveryServicesDSMove.md b/src/RecoveryServices/RecoveryServices/help/Test-AzRecoveryServicesDSMove.md new file mode 100644 index 000000000000..bca3caa35783 --- /dev/null +++ b/src/RecoveryServices/RecoveryServices/help/Test-AzRecoveryServicesDSMove.md @@ -0,0 +1,150 @@ +--- +external help file: Microsoft.Azure.PowerShell.Cmdlets.RecoveryServices.Backup.dll-Help.xml +Module Name: Az.RecoveryServices +online version: https://docs.microsoft.com/powershell/module/az.recoveryservices/test-azrecoveryservicesdsmove +schema: 2.0.0 +--- + +# Test-AzRecoveryServicesDSMove + +## SYNOPSIS +This cmdlet performs necessary validations for DS Move. + +## SYNTAX + +``` +Test-AzRecoveryServicesDSMove [-Force] [-DefaultProfile ] [-SourceVault] + [-TargetVault] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +This cmdlet performs necessary validations for DS Move. This cmdlet generates a boolean true if +all validations pass successfully. It is mandatory to run this cmdlet before Initialize-AzRecoveryServicesDSMove +cmdlet. This cmdlet is useful for cross tenant DS move scenario. + +## EXAMPLES + +### Example 1: Initialize DS Move for cross subscription copy +```powershell +PS C:\> Set-AzContext -SubscriptionName $targetSubscription +PS C:\> $validated = Test-AzRecoveryServicesDSMove -SourceVault $srcVault -TargetVault $trgVault -Force +PS C:\> Set-AzContext -SubscriptionName $sourceSubscription +PS C:\> if($validated) { +>> $corr = Initialize-AzRecoveryServicesDSMove -SourceVault $srcVault -TargetVault $trgVault +>> } +``` + +First cmdlet sets target subscription context. +Second cmdlet triggers some mandatory validations on target vault. +Third cmdlet sets source subscription context. +Then based on Test-AzRecoveryServicesDSMove cmdlet state, we fetch CorrelationId using +Initialize-AzRecoveryServicesDSMove cmdlet. $corr can be input to the Copy cmdlet. + +## PARAMETERS + +### -DefaultProfile +The credentials, account, tenant, and subscription used for communication with Azure. + +```yaml +Type: Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core.IAzureContextContainer +Parameter Sets: (All) +Aliases: AzContext, AzureRmContext, AzureCredential + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Forces the data move operation (prevents confirmation dialog). +This parameter is optional. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SourceVault +The source vault object to trigger data move. + +```yaml +Type: Microsoft.Azure.Commands.RecoveryServices.ARSVault +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -TargetVault +The target vault object where the data has to be moved. + +```yaml +Type: Microsoft.Azure.Commands.RecoveryServices.ARSVault +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: System.Management.Automation.SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### Microsoft.Azure.Commands.RecoveryServices.ARSVault + +## OUTPUTS + +### System.Boolean + +## NOTES + +## RELATED LINKS