Skip to content

Commit 785e523

Browse files
Webapp: Support for appservice ImportKeyVault Certificate (Azure#14037)
* #Commit1 * #Commit2 * #Commit3
1 parent e90f34d commit 785e523

File tree

11 files changed

+974
-3
lines changed

11 files changed

+974
-3
lines changed

src/Websites/Websites.Test/ScenarioTests/CertificatesTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,11 @@ public void TestRemoveAzWebAppCertificate()
5353
{
5454
WebsitesController.NewInstance.RunPsTest(_logger, "Test-RemoveAzWebAppCertificate");
5555
}
56+
[Fact]
57+
[Trait(Category.AcceptanceType, Category.CheckIn)]
58+
public void TestImportAzWebAppKeyVaultCertificate()
59+
{
60+
WebsitesController.NewInstance.RunPsTest(_logger, "Test-ImportAzWebAppKeyVaultCertificate");
61+
}
5662
}
5763
}

src/Websites/Websites.Test/ScenarioTests/CertificatesTests.ps1

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,26 @@ function Test-RemoveAzWebAppCertificate
138138
}
139139
finally{
140140

141+
}
142+
}
143+
144+
<#
145+
.SYNOPSIS
146+
Tests creating a new Web Hosting Plan.
147+
#>
148+
function Test-ImportAzWebAppKeyVaultCertificate
149+
{
150+
$rgname = "testkv1611"
151+
$wname = "testasewebapp"
152+
$keyvaultname = "testkv1611"
153+
$keyvaultcertname = "testcertname1611"
154+
try
155+
{
156+
#Setup
157+
$kvcert = Import-AzWebAppKeyVaultCertificate -ResourceGroupName $rgname -WebAppName $wname -KeyVaultName $keyvaultname -CertName $keyvaultcertname
158+
159+
}
160+
finally{
161+
141162
}
142163
}

src/Websites/Websites.Test/ScenarioTests/WebsitesController.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
using System.IO;
2828
using System.Linq;
2929
using Microsoft.Azure.Management.Internal.Resources;
30+
using Microsoft.Azure.Commands.Common.KeyVault.Version2016_10_1;
3031

3132
namespace Microsoft.Azure.Commands.Websites.Test.ScenarioTests
3233
{
@@ -40,6 +41,8 @@ public class WebsitesController
4041

4142
public AuthorizationManagementClient AuthorizationManagementClient { get; private set; }
4243

44+
public KeyVaultManagementClient KeyVaultManagementClient { get; private set; }
45+
4346
public string UserDomain { get; private set; }
4447

4548
public static WebsitesController NewInstance => new WebsitesController();
@@ -57,12 +60,14 @@ public void RunPsTest(XunitTracingInterceptor logger, params string[] scripts)
5760
var mockName = sf.GetMethod().Name;
5861
_helper.TracingInterceptor = logger;
5962

63+
logger.Information(string.Format("Test method entered: {0}.{1}", callingClassType, mockName));
6064
RunPsTestWorkflow(
6165
() => scripts,
6266
// no custom cleanup
6367
null,
6468
callingClassType,
6569
mockName);
70+
logger.Information(string.Format("Test method finished: {0}.{1}", callingClassType, mockName));
6671
}
6772

6873
public void RunPsTestWorkflow(
@@ -120,12 +125,13 @@ private void SetupManagementClients(MockContext context)
120125
NewResourceManagementClient = GetResourceManagementClient(context);
121126
WebsitesManagementClient = GetWebsitesManagementClient(context);
122127
AuthorizationManagementClient = GetAuthorizationManagementClient(context);
123-
128+
KeyVaultManagementClient = GetKeyVaultManagementClient(context);
124129
var armStorageManagementClient = GetArmStorageManagementClient(context);
125130
_helper.SetupManagementClients(
126131
NewResourceManagementClient,
127132
WebsitesManagementClient,
128133
AuthorizationManagementClient,
134+
KeyVaultManagementClient,
129135
armStorageManagementClient
130136
);
131137
}
@@ -149,5 +155,10 @@ private static WebSiteManagementClient GetWebsitesManagementClient(MockContext c
149155
{
150156
return context.GetServiceClient<WebSiteManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
151157
}
158+
159+
private static KeyVaultManagementClient GetKeyVaultManagementClient(MockContext context)
160+
{
161+
return context.GetServiceClient<KeyVaultManagementClient>(TestEnvironmentFactory.GetTestEnvironment());
162+
}
152163
}
153164
}

src/Websites/Websites.Test/SessionRecords/Microsoft.Azure.Commands.Websites.Test.ScenarioTests.CertificatesTests/TestImportAzWebAppKeyVaultCertificate.json

Lines changed: 563 additions & 0 deletions
Large diffs are not rendered by default.

src/Websites/Websites/Az.Websites.psd1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ CmdletsToExport = 'Get-AzAppServicePlan', 'Set-AzAppServicePlan',
101101
'Update-AzWebAppAccessRestrictionConfig',
102102
'Add-AzWebAppTrafficRouting', 'Remove-AzWebAppTrafficRouting',
103103
'Get-AzWebAppTrafficRouting', 'Update-AzWebAppTrafficRouting',
104-
'New-AzWebAppCertificate', 'Remove-AzWebAppCertificate'
104+
'New-AzWebAppCertificate', 'Remove-AzWebAppCertificate',
105+
'Import-AzWebAppKeyVaultCertificate'
105106

106107
# Variables to export from this module
107108
# VariablesToExport = @()

src/Websites/Websites/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* Added support for Importing a keyvault certificate to WebApp.
2122

2223
## Version 2.2.0
2324
* Added support for App Service Managed certificates
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
2+
using Microsoft.Azure.Commands.WebApps.Models;
3+
using Microsoft.Azure.Commands.WebApps.Models.WebApp;
4+
using Microsoft.Azure.Commands.WebApps.Utilities;
5+
using Microsoft.Azure.Management.Internal.Resources.Utilities;
6+
using Microsoft.Azure.Management.Internal.Resources.Utilities.Models;
7+
using Microsoft.Azure.Management.WebSites.Models;
8+
using System.Management.Automation;
9+
using System.Net;
10+
11+
namespace Microsoft.Azure.Commands.WebApps.Cmdlets.WebApps
12+
{
13+
/// <summary>
14+
/// this commandlet will let you import a keyvault to Webapp
15+
/// </summary>
16+
[Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "WebAppKeyVaultCertificate", SupportsShouldProcess = true)]
17+
[OutputType(typeof(PSCertificate))]
18+
public class ImportAzWebAppKeyVaultCertificate : WebAppBaseClientCmdLet
19+
{
20+
const string ParameterSet1Name = "S1";
21+
22+
[Parameter(ParameterSetName = ParameterSet1Name, Position = 0, Mandatory = true, HelpMessage = "The name of the keyvault.")]
23+
[ValidateNotNullOrEmpty]
24+
public string KeyVaultName { get; set; }
25+
26+
[Parameter(ParameterSetName = ParameterSet1Name, Position = 1, Mandatory = true, HelpMessage = "KeyVaultCertName of the certificate created in keyvault")]
27+
[ValidateNotNullOrEmpty]
28+
public string CertName { get; set; }
29+
30+
[Parameter(ParameterSetName = ParameterSet1Name, Position = 2, Mandatory = true, HelpMessage = "The name of the webapp resource group.")]
31+
[ResourceGroupCompleter]
32+
[ValidateNotNullOrEmpty]
33+
public string ResourceGroupName { get; set; }
34+
35+
[Parameter(ParameterSetName = ParameterSet1Name, Position = 3, Mandatory = true, HelpMessage = "The name of the webapp.")]
36+
[ValidateNotNullOrEmpty]
37+
public string WebAppName { get; set; }
38+
39+
[Parameter(ParameterSetName = ParameterSet1Name, Mandatory = false, HelpMessage = "The name of the webapp slot.")]
40+
[ValidateNotNullOrEmpty]
41+
public string Slot { get; set; }
42+
43+
public override void ExecuteCmdlet()
44+
{
45+
if (!string.IsNullOrWhiteSpace(ResourceGroupName) && !string.IsNullOrWhiteSpace(WebAppName))
46+
{
47+
var webApp = new PSSite(WebsitesClient.GetWebApp(ResourceGroupName, WebAppName, Slot));
48+
var location = webApp.Location;
49+
var serverFarmId = webApp.ServerFarmId;
50+
string kvid = string.Empty;
51+
string kvresourcegrpname = string.Empty;
52+
var keyvaultResources = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions
53+
{
54+
ResourceType = "Microsoft.KeyVault/Vaults"
55+
}).ToArray();
56+
57+
foreach (var kv in keyvaultResources)
58+
{
59+
if (kv.Name == KeyVaultName)
60+
{
61+
kvid = kv.Id;
62+
kvresourcegrpname = kv.ResourceGroupName;
63+
break;
64+
}
65+
}
66+
if (string.IsNullOrEmpty(kvid))
67+
{
68+
kvid = KeyVaultName;
69+
}
70+
string keyvaultperm;
71+
keyvaultperm = CmdletHelpers.CheckServicePrincipalPermissions(this.ResourcesClient, this.KeyvaultClient,kvresourcegrpname, KeyVaultName);
72+
var lnk = "https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html";
73+
if ((keyvaultperm != "Get") & (keyvaultperm != "get"))
74+
{
75+
WriteWarning("Unable to verify Key Vault permissions.");
76+
WriteWarning("You may need to grant Microsoft.Azure.WebSites service principal the Secret:Get permission");
77+
WriteWarning(string.Format("Find more details here: '{0}'", lnk));
78+
}
79+
80+
Certificate kvc = null;
81+
var certificate = new Certificate(
82+
location: location,
83+
keyVaultId: kvid,
84+
password: "",
85+
keyVaultSecretName: CertName,
86+
serverFarmId: serverFarmId
87+
);
88+
89+
if (this.ShouldProcess(this.WebAppName, string.Format($"Importing keyvault certificate for Web App '{WebAppName}'")))
90+
{
91+
try
92+
{
93+
kvc = WebsitesClient.CreateCertificate(ResourceGroupName, CertName, certificate);
94+
}
95+
catch (DefaultErrorResponseException e)
96+
{
97+
if (e.Response.StatusCode != HttpStatusCode.Conflict)
98+
{
99+
throw e;
100+
}
101+
}
102+
}
103+
WriteObject(kvc);
104+
}
105+
106+
}
107+
}
108+
}

src/Websites/Websites/Models.WebApp/WebAppBaseClient.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
using Microsoft.Azure.Commands.ResourceManager.Common;
1717
using Microsoft.Azure.Commands.WebApps.Utilities;
18-
using Microsoft.Azure.Commands.WebApps.Models;
1918

2019
namespace Microsoft.Azure.Commands.WebApps.Models
2120
{
@@ -57,5 +56,24 @@ public WebsitesClient WebsitesClient
5756
}
5857
set { _websitesClient = value; }
5958
}
59+
60+
private KeyVaultClient _keyVaultClient { get; set; }
61+
public KeyVaultClient KeyvaultClient
62+
{
63+
get
64+
{
65+
if (_keyVaultClient == null)
66+
{
67+
_keyVaultClient = new KeyVaultClient(DefaultProfile.DefaultContext)
68+
{
69+
VerboseLogger = WriteVerboseWithTimestamp,
70+
ErrorLogger = WriteErrorWithTimestamp,
71+
WarningLogger = WriteWarningWithTimestamp
72+
};
73+
}
74+
return _keyVaultClient;
75+
}
76+
set { _keyVaultClient = value; }
77+
}
6078
}
6179
}

src/Websites/Websites/Utilities/CmdletHelpers.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.Azure.Commands.Common.Authentication;
22
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
33
using Microsoft.Azure.Commands.WebApps.Models;
4+
using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory;
45
using Microsoft.Azure.Management.Internal.Network.Version2017_10_01;
56
using Microsoft.Azure.Management.Internal.Network.Version2017_10_01.Models;
67
using Microsoft.Azure.Management.Internal.Resources.Utilities;
@@ -434,6 +435,28 @@ internal static Certificate[] GetCertificates(ResourceClient resourceClient, Web
434435
return certificates.ToArray();
435436
}
436437

438+
internal static string CheckServicePrincipalPermissions(ResourceClient resourceClient, KeyVaultClient keyVaultClient, string resourceGroupName, string keyVault)
439+
{
440+
var perm1 = " ";
441+
var kv2 = keyVaultClient.GetKeyVault(resourceGroupName, keyVault);
442+
foreach (var policy in kv2.Properties.AccessPolicies)
443+
{
444+
if (policy.ObjectId == ("f8daea97-62e7-4026-becf-13c2ea98e8b4"))
445+
{
446+
foreach (var perm in policy.Permissions.Secrets)
447+
{
448+
if ((perm == "Get") || (perm == "get"))
449+
{
450+
perm1 = perm;
451+
Console.WriteLine("Success");
452+
break;
453+
}
454+
}
455+
}
456+
}
457+
return perm1.ToString();
458+
}
459+
437460
internal static SiteConfigResource ConvertToSiteConfigResource(this SiteConfig config)
438461
{
439462
return new SiteConfigResource
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Microsoft.Azure.Commands.Common.Authentication;
2+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
3+
using Microsoft.Azure.Commands.Common.KeyVault.Version2016_10_1;
4+
using Microsoft.Azure.Commands.Common.KeyVault.Version2016_10_1.Models;
5+
using System;
6+
7+
8+
namespace Microsoft.Azure.Commands.WebApps.Utilities
9+
{
10+
public class KeyVaultClient
11+
{
12+
public Action<string> VerboseLogger { get; set; }
13+
14+
public Action<string> ErrorLogger { get; set; }
15+
16+
public Action<string> WarningLogger { get; set; }
17+
public KeyVaultClient(IAzureContext context)
18+
{
19+
this.WrappedKeyVaultClient = AzureSession.Instance.ClientFactory.CreateArmClient<KeyVaultManagementClient>(context, AzureEnvironment.Endpoint.ResourceManager);
20+
}
21+
22+
public KeyVaultManagementClient WrappedKeyVaultClient
23+
{
24+
get;
25+
private set;
26+
}
27+
28+
public Vault GetKeyVault(string resourceGroupName, string vaultName)
29+
{
30+
try
31+
{
32+
return this.WrappedKeyVaultClient.Vaults.Get(resourceGroupName, vaultName);
33+
}
34+
catch (Exception ex)
35+
{
36+
throw ex;
37+
}
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)