Skip to content

Commit 5df449e

Browse files
committed
Vault Credentials Client changes except the first time flow
1 parent a5f8146 commit 5df449e

File tree

9 files changed

+705
-3
lines changed

9 files changed

+705
-3
lines changed

src/ServiceManagement/RecoveryServices/Commands.RecoveryServices/Commands.RecoveryServices.csproj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
<Prefer32Bit>false</Prefer32Bit>
4848
</PropertyGroup>
4949
<ItemGroup>
50+
<Reference Include="Microsoft.Azure.RecoveryServices">
51+
<HintPath>..\..\..\..\..\hydra-specs-pr\SiteRecovery\SiteRecovery.Tests\bin\Debug\Microsoft.Azure.RecoveryServices.dll</HintPath>
52+
</Reference>
5053
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
5154
<SpecificVersion>False</SpecificVersion>
5255
<HintPath>..\..\..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
@@ -75,6 +78,10 @@
7578
<SpecificVersion>False</SpecificVersion>
7679
<HintPath>..\..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
7780
</Reference>
81+
<Reference Include="Security.Cryptography, Version=1.6.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
82+
<SpecificVersion>False</SpecificVersion>
83+
<HintPath>lib\Security.Cryptography.dll</HintPath>
84+
</Reference>
7885
<Reference Include="System" />
7986
<Reference Include="System.Core" />
8087
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@@ -103,10 +110,12 @@
103110
<ItemGroup>
104111
<Compile Include="lib\PSStorageObjects.cs" />
105112
<Compile Include="lib\PSNetworkObjects.cs" />
113+
<Compile Include="lib\CertUtils.cs" />
106114
<Compile Include="lib\PSContracts.cs" />
107115
<Compile Include="lib\PSObjects.cs" />
108116
<Compile Include="lib\PSParameterSets.cs" />
109117
<Compile Include="lib\PSRecoveryPlanObjects.cs" />
118+
<Compile Include="lib\Utilities.cs" />
110119
<Compile Include="Properties\Resources.Designer.cs">
111120
<AutoGen>True</AutoGen>
112121
<DesignTime>True</DesignTime>
@@ -116,6 +125,7 @@
116125
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesStorageMappingClient.cs" />
117126
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesNetworkMappingClient.cs" />
118127
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesNetworkClient.cs" />
128+
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesVaultClient.cs" />
119129
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesVMGroupClient.cs" />
120130
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesPEClient.cs" />
121131
<Compile Include="PSRecoveryServicesClient\PSRecoveryServicesClient.cs">
@@ -138,6 +148,7 @@
138148
<Compile Include="Service\GetAzureSiteRecoveryNetwork.cs" />
139149
<Compile Include="Service\CreateAzureSiteRecoveryRecoveryPlan.cs" />
140150
<Compile Include="Service\GetAzureSiteRecoveryRecoveryPlanFile.cs" />
151+
<Compile Include="Service\GetVaultCredentialsFile.cs" />
141152
<Compile Include="Service\RemoveAzureSiteRecoveryRecoveryPlan.cs" />
142153
<Compile Include="Service\GetAzureSiteRecoveryProtectionEntity.cs" />
143154
<Compile Include="Service\RestartAzureSiteRecoveryJob.cs" />
@@ -166,6 +177,7 @@
166177
</ProjectReference>
167178
</ItemGroup>
168179
<ItemGroup>
180+
<Content Include="lib\Security.Cryptography.dll" />
169181
<Content Include="Microsoft.Azure.Commands.RecoveryServices.dll-help.xml">
170182
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
171183
</Content>
@@ -181,6 +193,9 @@
181193
<None Include="MSSharedLibKey.snk" />
182194
<None Include="packages.config" />
183195
</ItemGroup>
196+
<ItemGroup>
197+
<WCFMetadata Include="Service References\" />
198+
</ItemGroup>
184199
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
185200
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
186201
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

src/ServiceManagement/RecoveryServices/Commands.RecoveryServices/PSRecoveryServicesClient/PSRecoveryServicesClient.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ public string GenerateAgentAuthenticationHeader(string clientRequestId)
184184
/// <summary>
185185
/// Gets request headers.
186186
/// </summary>
187+
/// <param name="shouldSignRequest">specifies whether to sign the request or not</param>
187188
/// <returns>Custom request headers</returns>
188-
public CustomRequestHeaders GetRequestHeaders()
189+
public CustomRequestHeaders GetRequestHeaders(bool shouldSignRequest = true)
189190
{
190191
this.ClientRequestId = Guid.NewGuid().ToString() + "-" + DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ssZ") + "-P";
191192

@@ -194,7 +195,7 @@ public CustomRequestHeaders GetRequestHeaders()
194195
// ClientRequestId is a unique ID for every request to Azure Site Recovery.
195196
// It is useful when diagnosing failures in API calls.
196197
ClientRequestId = this.ClientRequestId,
197-
AgentAuthenticationHeader = this.GenerateAgentAuthenticationHeader(this.ClientRequestId)
198+
AgentAuthenticationHeader = shouldSignRequest ? this.GenerateAgentAuthenticationHeader(this.ClientRequestId) : ""
198199
};
199200
}
200201

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
using System.Threading.Tasks;
17+
using Microsoft.WindowsAzure;
18+
using Microsoft.WindowsAzure.Management.SiteRecovery;
19+
using Microsoft.WindowsAzure.Management.SiteRecovery.Models;
20+
21+
namespace Microsoft.Azure.Commands.RecoveryServices
22+
{
23+
/// <summary>
24+
/// Recovery services convenience client.
25+
/// </summary>
26+
public partial class PSRecoveryServicesClient
27+
{
28+
/// <summary>
29+
/// Gets Vault Extended Information
30+
/// </summary>
31+
/// <returns>ResourceExtendedInformationResponse</returns>
32+
public async Task<ResourceExtendedInformation> GetExtendedInfo()
33+
{
34+
ResourceExtendedInformationResponse response = await this.GetSiteRecoveryClient().Vaults.GetExtendedInfoAsync(this.GetRequestHeaders(false));
35+
36+
return response.ResourceExtendedInformation;
37+
}
38+
39+
/// <summary>
40+
/// Creates the extended information for the vault
41+
/// </summary>
42+
/// <param name="extendedInfoArgs">extneded info to be created</param>
43+
/// <returns>ResourceExtendedInformation</returns>
44+
public async Task<ResourceExtendedInformation> CreateExtendedInfo(ResourceExtendedInformationArgs extendedInfoArgs)
45+
{
46+
ResourceExtendedInformationResponse response = await this.GetSiteRecoveryClient().Vaults.CreateExtendedInfoAsync(extendedInfoArgs, this.GetRequestHeaders(false));
47+
48+
return response.ResourceExtendedInformation;
49+
}
50+
51+
/// <summary>
52+
/// Updates the vault certificate
53+
/// </summary>
54+
/// <param name="args">the certificate update arguments</param>
55+
/// <returns>UploadCertificateResponse</returns>
56+
public async Task<UploadCertificateResponse> UpdateVaultCertificate(CertificateArgs args)
57+
{
58+
return await this.GetSiteRecoveryClient().Vaults.UploadCertificateAsync(args, this.GetRequestHeaders(false));
59+
}
60+
}
61+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
using System.Management.Automation;
17+
using System.Security.Cryptography.X509Certificates;
18+
using System.Threading.Tasks;
19+
using Microsoft.Azure.Commands.RecoveryServices.lib;
20+
using Microsoft.Azure.Commands.RecoveryServices.SiteRecovery;
21+
using Microsoft.Azure.Portal.HybridServicesCore;
22+
using Microsoft.Azure.Portal.RecoveryServices.Models.Common;
23+
using Microsoft.WindowsAzure.Management.SiteRecovery.Models;
24+
using Microsoft.WindowsAzure.Commands.Common;
25+
using Microsoft.WindowsAzure.Commands.Common.Models;
26+
27+
namespace Microsoft.Azure.Commands.RecoveryServices
28+
{
29+
/// <summary>
30+
/// Retrieves Azure Site Recovery Server.
31+
/// </summary>
32+
[Cmdlet(VerbsCommon.Get, "AzureSiteRecoveryVaultCredential", DefaultParameterSetName = ASRParameterSets.Default)]
33+
[OutputType(typeof(string))]
34+
public class GetVaultCredentialsFile : RecoveryServicesCmdletBase
35+
{
36+
private const int VaultCertificateExpiryInHoursForHRM = 120;
37+
38+
#region Parameters
39+
40+
/// <summary>
41+
/// Gets or sets the vault name
42+
/// </summary>
43+
[Parameter(ParameterSetName = ASRParameterSets.ByParam, HelpMessage = "Vault Name for which the cred file to be generated")]
44+
[ValidateNotNullOrEmpty]
45+
public string Name { get; set; }
46+
47+
/// <summary>
48+
/// Gets or sets the vault name
49+
/// </summary>
50+
[Parameter(ParameterSetName = ASRParameterSets.ByParam, HelpMessage = "Vault Name for which the cred file to be generated")]
51+
[ValidateNotNullOrEmpty]
52+
// TODO: devsri - Remove this.
53+
public string CloudServiceName { get; set; }
54+
55+
/// <summary>
56+
/// Gets or sets the location of the vault
57+
/// </summary>
58+
[Parameter(ParameterSetName = ASRParameterSets.ByParam, HelpMessage = "Geo Name to which the vault belongs")]
59+
[ValidateNotNullOrEmpty]
60+
public string Geo { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets the path where the credential file is to be generated
64+
/// </summary>
65+
[Parameter(ParameterSetName = ASRParameterSets.ByParam, Mandatory = false, HelpMessage = "The site name if the vault credentials to be downloaded for a Hyper-V sites.")]
66+
public string SiteName { get; set; }
67+
68+
/// <summary>
69+
/// Gets or sets the path where the credential file is to be generated
70+
/// </summary>
71+
[Parameter(ParameterSetName = ASRParameterSets.ByParam, Mandatory =false, HelpMessage = "The path where the vault credential file is to be created.")]
72+
// TODO:devsri - add file path validator over here.
73+
public string Path { get; set; }
74+
75+
#endregion Parameters
76+
77+
/// <summary>
78+
/// ProcessRecord of the command.
79+
/// </summary>
80+
public override async void ExecuteCmdlet()
81+
{
82+
try
83+
{
84+
AzureSubscription subscription = AzureSession.CurrentContext.Subscription;
85+
86+
// Generate certificate
87+
X509Certificate2 cert = CertUtils.CreateSelfSignedCertificate(VaultCertificateExpiryInHoursForHRM, subscription.Id.ToString(), this.Name);
88+
89+
Utilities.UpdateVaultSettings(new ASRVaultCreds()
90+
{
91+
CloudServiceName = this.CloudServiceName,
92+
ResourceName = this.Name
93+
});
94+
95+
// Upload certificate
96+
UploadCertificateResponse acsDetails = await this.UpdateVaultCertificate(cert);
97+
98+
// Get Channel Integrity key
99+
string channelIntegrityKey = await this.GetChannelIntegrityKey();
100+
101+
// Generate file.
102+
ASRVaultCreds vaultCreds = this.GenerateCredential(
103+
subscription.Id.ToString(),
104+
this.Name,
105+
cert,
106+
acsDetails,
107+
channelIntegrityKey,
108+
this.CloudServiceName);
109+
110+
string filePath = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path;
111+
string fileName = this.GenerateFileName();
112+
113+
// write the content to a file.
114+
Utilities.WriteToFile<ASRVaultCreds>(vaultCreds, filePath, fileName);
115+
}
116+
catch (Exception exception)
117+
{
118+
this.HandleException(exception);
119+
}
120+
}
121+
122+
private async Task<UploadCertificateResponse> UpdateVaultCertificate(X509Certificate2 cert)
123+
{
124+
var certificateArgs = new CertificateArgs()
125+
{
126+
Certificate = Convert.ToBase64String(cert.GetRawCertData()),
127+
ContractVersion = "V2012_12"
128+
};
129+
130+
UploadCertificateResponse response = await RecoveryServicesClient.UpdateVaultCertificate(certificateArgs);
131+
132+
return response;
133+
}
134+
135+
private async Task<string> GetChannelIntegrityKey()
136+
{
137+
ResourceExtendedInformation extendedInformation;
138+
try
139+
{
140+
extendedInformation = await RecoveryServicesClient.GetExtendedInfo();
141+
}
142+
catch (Exception)
143+
{
144+
//TODO:devsri - Handle specific error rather than generic once
145+
extendedInformation = new ResourceExtendedInformation();
146+
}
147+
148+
ResourceExtendedInfo extendedInfo = Utilities.Deserialize<ResourceExtendedInfo>(extendedInformation.ExtendedInfo);
149+
150+
if (extendedInfo == null)
151+
{
152+
ResourceExtendedInformationArgs extendedInfoArgs = extendedInfo.Translate();
153+
extendedInformation = await RecoveryServicesClient.CreateExtendedInfo(extendedInfoArgs);
154+
155+
extendedInfo = Utilities.Deserialize<ResourceExtendedInfo>(extendedInformation.ExtendedInfo);
156+
}
157+
158+
return extendedInfo.ChannelIntegrityKey;
159+
}
160+
161+
private ASRVaultCreds GenerateCredential(string subscriptionId, string resourceName, X509Certificate2 managementCert, UploadCertificateResponse acsDetails, string channelIntegrityKey, string cloudServiceName)
162+
{
163+
string serializedCertifivate = Convert.ToBase64String(managementCert.Export(X509ContentType.Pfx));
164+
165+
AcsNamespace acsNamespace = new AcsNamespace(acsDetails);
166+
167+
ASRVaultCreds vaultCreds = new ASRVaultCreds(
168+
subscriptionId,
169+
resourceName,
170+
serializedCertifivate,
171+
acsNamespace,
172+
channelIntegrityKey,
173+
cloudServiceName);
174+
175+
return vaultCreds;
176+
}
177+
178+
private string GenerateFileName()
179+
{
180+
string fileName;
181+
182+
if (string.IsNullOrEmpty(this.SiteName))
183+
{
184+
fileName = string.Format("{0}_{1}.VaultCredentials", this.Name, DateTime.UtcNow.ToLongDateString());
185+
}
186+
else
187+
{
188+
fileName = string.Format("{0}_{1}_{2}.VaultCredentials", this.SiteName, this.Name, DateTime.UtcNow.ToLongDateString());
189+
}
190+
191+
return fileName;
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)