diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Common/PSRecoveryServicesVaultExtendedInfoClient.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Common/PSRecoveryServicesVaultExtendedInfoClient.cs
index 1020440f6548..8eadcc529135 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Common/PSRecoveryServicesVaultExtendedInfoClient.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Common/PSRecoveryServicesVaultExtendedInfoClient.cs
@@ -125,6 +125,27 @@ public ASRVaultCreds GenerateVaultCredential(X509Certificate2 managementCert, AR
return asrVaultCreds;
}
+ ///
+ /// Upload cert to idmgmt
+ ///
+ /// certificate to be uploaded
+ /// vault object
+ /// Upload Certificate Response
+ public UploadCertificateResponse UploadCertificate(X509Certificate2 managementCert, ARSVault vault)
+ {
+ var certificateArgs = new CertificateArgs();
+ certificateArgs.Properties = new Dictionary();
+ certificateArgs.Properties.Add("certificate", Convert.ToBase64String(managementCert.GetRawCertData()));
+
+ var response = this.recoveryServicesClient.VaultExtendedInfo.UploadCertificateAsync(
+ vault.ResouceGroupName,
+ vault.Name,
+ certificateArgs, managementCert.FriendlyName,
+ this.GetRequestHeaders());
+ response.Wait();
+ return response.Result;
+ }
+
///
/// Changes the Vault context
///
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSContracts.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSContracts.cs
index f36831037a6b..869ebbc99a5e 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSContracts.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSContracts.cs
@@ -358,10 +358,11 @@ public VaultCreds()
/// resource name
/// management cert
/// authenticating service namespace
- public VaultCreds(string subscriptionId, string resourceName, string managementCert, AcsNamespace acsNamespace)
+ /// resource type backup vault or ASR vault
+ public VaultCreds(string subscriptionId, string resourceName, string managementCert, AcsNamespace acsNamespace, string resourceType = null)
{
this.SubscriptionId = subscriptionId;
- this.ResourceType = Constants.ASRVaultType;
+ this.ResourceType = string.IsNullOrEmpty(resourceType) ? Constants.ASRVaultType : resourceType;
this.ResourceName = resourceName;
this.ManagementCert = managementCert;
this.AcsNamespace = acsNamespace;
@@ -504,6 +505,54 @@ public ASRVaultCreds(
#endregion
}
+ ///
+ /// Class to define backup vault credentials
+ ///
+ [DataContract]
+ public class BackupVaultCreds : VaultCreds
+ {
+ ///
+ /// Gets or sets the agent links
+ ///
+ [DataMember(Order = 0)]
+ public string AgentLinks { get; set; }
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the BackupVaultCreds class
+ ///
+ public BackupVaultCreds() { }
+
+ ///
+ /// Initializes a new instance of the BackupVaultCreds class
+ ///
+ /// subscription Id
+ /// resource type
+ /// resource name
+ /// management cert
+ /// acs namespace
+ public BackupVaultCreds(string subscriptionId, string resourceName, string managementCert, AcsNamespace acsNamespace)
+ : base(subscriptionId, resourceName, managementCert, acsNamespace, Constants.BackupVaultType) { }
+
+ ///
+ /// Initializes a new instance of the BackupVaultCreds class
+ ///
+ /// subscription Id
+ /// resource type
+ /// resource name
+ /// management cert
+ /// acs namespace
+ /// agent links
+ public BackupVaultCreds(string subscriptionId, string resourceName, string managementCert, AcsNamespace acsNamespace, string agentLinks)
+ : this(subscriptionId, resourceName, managementCert, acsNamespace)
+ {
+ AgentLinks = agentLinks;
+ }
+
+ #endregion
+ }
+
///
/// Class to define ACS name space
///
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSObjects.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSObjects.cs
index e89150a3072e..1095894ac51c 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSObjects.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Models/PSObjects.cs
@@ -26,6 +26,11 @@ public class Constants
///
public const string ASRVaultType = "HyperVRecoveryManagerVault";
+ ///
+ /// Backup vault type
+ ///
+ public const string BackupVaultType = "Vaults";
+
///
/// Vault Credential version.
///
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.Designer.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.Designer.cs
index 9e9061c1ad0b..31ae6ced6777 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.Designer.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.Designer.cs
@@ -69,6 +69,15 @@ internal static string AzureVMNetworkIsNotAssociatedWithTheSubscription {
}
}
+ ///
+ /// Looks up a localized string similar to RecoveryService - Backup Vault - Successfully serialized the file content.
+ ///
+ internal static string BackupVaultSerialized {
+ get {
+ return ResourceManager.GetString("BackupVaultSerialized", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Operation Failed.
///.
@@ -106,6 +115,15 @@ internal static string DisableProtectionWhatIfMessage {
}
}
+ ///
+ /// Looks up a localized string similar to Executing cmdlet with SubscriptionId = {0}, ResourceGroupName = {1}, ResourceName = {2}, TargetLocation = {3}.
+ ///
+ internal static string ExecutingGetVaultCredCmdlet {
+ get {
+ return ResourceManager.GetString("ExecutingGetVaultCredCmdlet", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Calls using ID based parameter {0} will not be supported from next release. Please use its corresponding full object parameter instead..
///
@@ -368,6 +386,15 @@ internal static string ResourceNameNullOrEmpty {
}
}
+ ///
+ /// Looks up a localized string similar to Saving Vault Credentials to file : {0}.
+ ///
+ internal static string SavingVaultCred {
+ get {
+ return ResourceManager.GetString("SavingVaultCred", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Server {0} is not associated with the Vault {1}.
///
@@ -441,6 +468,24 @@ internal static string SubscriptionIsNotAssociatedWithTheAccount {
}
}
+ ///
+ /// Looks up a localized string similar to RecoveryService - Successfully uploaded the certificate.
+ ///
+ internal static string UploadedCertToIdmgmt {
+ get {
+ return ResourceManager.GetString("UploadedCertToIdmgmt", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to RecoveryService - Going to upload the certificate.
+ ///
+ internal static string UploadingCertToIdmgmt {
+ get {
+ return ResourceManager.GetString("UploadingCertToIdmgmt", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Could not validate the storage account and subscription given.
///Are you sure you want to continue {0}?.
@@ -469,6 +514,15 @@ internal static string VaultCreationSuccessMessage {
}
}
+ ///
+ /// Looks up a localized string similar to The target location provided is not a directory. Please provide a directory..
+ ///
+ internal static string VaultCredPathException {
+ get {
+ return ResourceManager.GetString("VaultCredPathException", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Vault has been deleted.
///
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.resx b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.resx
index feef11f65291..783bbe796ff6 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.resx
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Properties/Resources.resx
@@ -277,4 +277,22 @@ Please provide a storage account with the same location as that of the vault.
Site {0} is not associated with the Vault {1}
+
+ RecoveryService - Backup Vault - Successfully serialized the file content
+
+
+ Executing cmdlet with SubscriptionId = {0}, ResourceGroupName = {1}, ResourceName = {2}, TargetLocation = {3}
+
+
+ Saving Vault Credentials to file : {0}
+
+
+ RecoveryService - Successfully uploaded the certificate
+
+
+ RecoveryService - Going to upload the certificate
+
+
+ The target location provided is not a directory. Please provide a directory.
+
\ No newline at end of file
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Utilities/CertUtils.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Utilities/CertUtils.cs
index eb1b9e2d38bb..0845a32cfb10 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Utilities/CertUtils.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Utilities/CertUtils.cs
@@ -221,5 +221,16 @@ private static CngKey Create2048RsaKey()
return CngKey.Create(CngAlgorithm2.Rsa, null, keyCreationParameters);
}
+
+ ///
+ /// Returns serialized certificate - Base64 encoded based on the content type
+ ///
+ /// The certificate provided
+ /// Cert content type
+ /// The serialized cert value in string
+ public static string SerializeCert(X509Certificate2 cert, X509ContentType contentType)
+ {
+ return Convert.ToBase64String(cert.Export(contentType));
+ }
}
}
\ No newline at end of file
diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs
index 2c952368f686..37b3b6e701a8 100644
--- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs
+++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs
@@ -18,6 +18,13 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.Azure.Common.Authentication.Models;
using Microsoft.Azure.Portal.RecoveryServices.Models.Common;
+using System.IO;
+using System.Xml;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Globalization;
+using Microsoft.Azure.Management.RecoveryServices.Models;
+using Microsoft.Azure.Commands.RecoveryServices.Properties;
namespace Microsoft.Azure.Commands.RecoveryServices
{
@@ -31,15 +38,19 @@ public class GetAzureRmRecoveryServicesVaultSettingsFile : RecoveryServicesCmdle
///
/// Expiry in hours for generated certificate.
///
- private const int VaultCertificateExpiryInHoursForHRM = 120;
+ private const int VaultCertificateExpiryInHoursForHRM = 120;
+
+ ///
+ /// Expiry in hours for generated certificate.
+ ///
+ private const int VaultCertificateExpiryInHoursForBackup = 48;
#region Parameters
///
/// Gets or sets vault Object.
///
- [Parameter(ParameterSetName = ASRParameterSets.ByDefault, Mandatory = true, ValueFromPipeline = true)]
- [Parameter(ParameterSetName = ASRParameterSets.ForSite, Mandatory = true, ValueFromPipeline = true)]
+ [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1)]
[ValidateNotNullOrEmpty]
public ARSVault Vault { get; set; }
@@ -63,10 +74,38 @@ public class GetAzureRmRecoveryServicesVaultSettingsFile : RecoveryServicesCmdle
///
/// Gets or sets vault Object.
///
- [Parameter(ParameterSetName = ASRParameterSets.ByDefault)]
- [Parameter(ParameterSetName = ASRParameterSets.ForSite)]
+ [Parameter(Position = 2)]
public string Path { get; set; }
+ ///
+ /// Gets or sets the path where the credential file is to be generated
+ ///
+ ///
+ /// Gets or sets vault Object.
+ ///
+ [Parameter(ParameterSetName = ASRParameterSets.ByDefault, Mandatory = false)]
+ [Parameter(ParameterSetName = ASRParameterSets.ForSite, Mandatory = false)]
+ public SwitchParameter SiteRecovery
+ {
+ get { return siteRecovery; }
+ set { siteRecovery = value; }
+ }
+ private bool siteRecovery;
+
+ ///
+ /// Gets or sets the path where the credential file is to be generated
+ ///
+ ///
+ /// Gets or sets vault Object.
+ ///
+ [Parameter(ParameterSetName = "ForBackup", Mandatory = true)]
+ public SwitchParameter Backup
+ {
+ get { return backup; }
+ set { backup = value; }
+ }
+ private bool backup;
+
#endregion Parameters
///
@@ -76,7 +115,15 @@ public override void ExecuteCmdlet()
{
try
{
- this.GetVaultSettingsFile();
+ if (backup)
+ {
+ this.GetAzureRMRecoveryServicesVaultBackupCredentials();
+ }
+ else
+ {
+ this.GetVaultSettingsFile();
+ }
+
}
catch (AggregateException aggregateEx)
{
@@ -101,7 +148,7 @@ private void GetVaultSettingsFile()
if (!string.IsNullOrEmpty(this.SiteIdentifier) && !string.IsNullOrEmpty(this.SiteFriendlyName))
{
- site.ID = this.SiteIdentifier;
+ site.ID = this.SiteIdentifier;
site.Name = this.SiteFriendlyName;
}
@@ -144,5 +191,153 @@ private string GenerateFileName()
return fileName;
}
+
+ #region Backup Vault Credentials
+ ///
+ /// Get vault credentials for backup vault type.
+ ///
+ public void GetAzureRMRecoveryServicesVaultBackupCredentials()
+ {
+ string targetLocation = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path;
+ if (!Directory.Exists(targetLocation))
+ {
+ throw new ArgumentException(Resources.VaultCredPathException);
+ }
+
+ string subscriptionId = DefaultContext.Subscription.Id.ToString();
+ string displayName = this.Vault.Name;
+
+ WriteDebug(string.Format(CultureInfo.InvariantCulture,
+ Resources.ExecutingGetVaultCredCmdlet,
+ subscriptionId, this.Vault.ResouceGroupName, this.Vault.Name, targetLocation));
+
+ // Generate certificate
+ X509Certificate2 cert = CertUtils.CreateSelfSignedCertificate(VaultCertificateExpiryInHoursForBackup, subscriptionId.ToString(), this.Vault.Name);
+
+ AcsNamespace acsNamespace = null;
+ string channelIntegrityKey = string.Empty;
+ try
+ {
+ // Upload cert into ID Mgmt
+ WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadingCertToIdmgmt));
+ acsNamespace = UploadCert(cert);
+ WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadedCertToIdmgmt));
+ }
+ catch (Exception exception)
+ {
+ throw exception;
+ }
+
+ // generate vault credentials
+ string vaultCredsFileContent = GenerateVaultCreds(cert, subscriptionId, acsNamespace);
+
+ // NOTE: One of the scenarios for this cmdlet is to generate a file which will be an input to DPM servers.
+ // We found a bug in the DPM UI which is looking for a particular namespace in the input file.
+ // The below is a hack to circumvent this issue and this would be removed once the bug can be fixed.
+ vaultCredsFileContent = vaultCredsFileContent.Replace("Microsoft.Azure.Commands.AzureBackup.Models",
+ "Microsoft.Azure.Portal.RecoveryServices.Models.Common");
+
+ // prepare for download
+ string fileName = string.Format("{0}_{1:ddd MMM dd yyyy}.VaultCredentials", displayName, DateTime.UtcNow);
+ string filePath = System.IO.Path.Combine(targetLocation, fileName);
+ WriteDebug(string.Format(Resources.SavingVaultCred, filePath));
+
+ File.WriteAllBytes(filePath, Encoding.UTF8.GetBytes(vaultCredsFileContent));
+
+ VaultSettingsFilePath output = new VaultSettingsFilePath()
+ {
+ FilePath = filePath,
+ };
+
+ // Output filename back to user
+ WriteObject(output);
+ }
+
+ ///
+ /// Upload certificate
+ ///
+ /// management certificate
+ /// acs namespace of the uploaded cert
+ private AcsNamespace UploadCert(X509Certificate2 cert)
+ {
+ UploadCertificateResponse response = RecoveryServicesClient.UploadCertificate(cert, this.Vault);
+
+ return new AcsNamespace(response);
+ }
+
+ ///
+ /// Generates vault creds file
+ ///
+ /// management certificate
+ /// subscription Id
+ /// acs namespace
+ /// xml file in string format
+ private string GenerateVaultCreds(X509Certificate2 cert, string subscriptionId, AcsNamespace acsNamespace)
+ {
+ try
+ {
+ return GenerateVaultCredsForBackup(cert, subscriptionId, acsNamespace);
+ }
+ catch (Exception exception)
+ {
+ throw exception;
+ }
+ }
+
+ ///
+ /// Generates vault creds file content for backup Vault
+ ///
+ /// management certificate
+ /// subscription Id
+ /// acs namespace
+ /// xml file in string format
+ private string GenerateVaultCredsForBackup(X509Certificate2 cert, string subscriptionId, AcsNamespace acsNamespace)
+ {
+ using (var output = new MemoryStream())
+ {
+ using (var writer = XmlWriter.Create(output, GetXmlWriterSettings()))
+ {
+ BackupVaultCreds backupVaultCreds = new BackupVaultCreds(subscriptionId,
+ this.Vault.Name,
+ CertUtils.SerializeCert(cert, X509ContentType.Pfx),
+ acsNamespace,
+ GetAgentLinks());
+ DataContractSerializer serializer = new DataContractSerializer(typeof(BackupVaultCreds));
+ serializer.WriteObject(writer, backupVaultCreds);
+
+ WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.BackupVaultSerialized));
+ }
+
+ return Encoding.UTF8.GetString(output.ToArray());
+ }
+ }
+
+ ///
+ /// Get Agent Links
+ ///
+ /// Agent links in string format
+ private static string GetAgentLinks()
+ {
+ return "WABUpdateKBLink,http://go.microsoft.com/fwlink/p/?LinkId=229525;" +
+ "StorageQuotaPurchaseLink,http://go.microsoft.com/fwlink/?LinkId=205490;" +
+ "WebPortalLink,http://go.microsoft.com/fwlink/?LinkId=252913;" +
+ "WABprivacyStatement,http://go.microsoft.com/fwlink/?LinkId=221308";
+ }
+
+ ///
+ /// A set of XmlWriterSettings to use for the publishing profile
+ ///
+ /// The XmlWriterSettings set
+ private XmlWriterSettings GetXmlWriterSettings()
+ {
+ return new XmlWriterSettings
+ {
+ Encoding = new UTF8Encoding(false),
+ Indent = true,
+ NewLineOnAttributes = true
+ };
+ }
+
+ #endregion
}
}