Last active
August 8, 2019 20:44
-
-
Save YannickRe/f2cf5e82cefcb370b2a2e677bffd9c87 to your computer and use it in GitHub Desktop.
Code accompanying https://blog.yannickreekmans.be/use-lets-encrypt-in-an-azure-function-for-an-azure-web-app/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<TargetFramework>netstandard2.0</TargetFramework> | |
<AzureFunctionsVersion>v2</AzureFunctionsVersion> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.2" /> | |
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.1.0-preview" /> | |
</ItemGroup> | |
</Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#r "Newtonsoft.Json" | |
using System; | |
using Newtonsoft.Json; | |
using System.Net.Http; | |
using System.Text; | |
using System.Threading; | |
using Microsoft.Azure.KeyVault; | |
using Microsoft.Azure.Services.AppAuthentication; | |
using Microsoft.Azure.KeyVault.Models; | |
public static async Task Run(TimerInfo myTimer, ILogger log) | |
{ | |
log.LogInformation($"C# Timer trigger function executed at: {myTimer.ToString()}"); | |
var functionAppName = ""; //TODO: fill in! | |
var keyVaultUrl = ""; //TODO: fill in! | |
var userName = $"${functionAppName}"; | |
var userPwd = await GetSecretFromKeyVault(keyVaultUrl, "userPwd"); | |
var pfxPassword = await GetSecretFromKeyVault(keyVaultUrl, "pfxPwd"); | |
var clientSecret = await GetSecretFromKeyVault(keyVaultUrl, "clientSecret"); | |
var configBody = new Config() { | |
AzureEnvironment = new AzureEnvironment() { | |
WebAppName = "", //TODO: fill in! | |
ClientId = "", //TODO: fill in! | |
ClientSecret = clientSecret, | |
ResourceGroupName = "root", //TODO: fill in! | |
SubscriptionId = "", //TODO: fill in! | |
Tenant = "", //TODO: fill in! | |
}, | |
AcmeConfig = new AcmeConfig() { | |
RegistrationEmail = "", //TODO: fill in! | |
Host = "", | |
AlternateNames = new string[]{}, | |
RSAKeyLength = 2048, | |
PFXPassword = pfxPassword, | |
UseProduction = true | |
}, | |
CertificateSettings = new CertificateSettings() { | |
UseIPBasedSSL = false | |
}, | |
AuthorizationChallengeProviderConfig = new AuthorizationChallengeProviderConfig() { | |
DisableWebConfigUpdate = false | |
} | |
}; | |
var client = new HttpClient(); | |
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{userName}:{userPwd}"))); | |
configBody.AcmeConfig.Host = "yannickreekmans.be"; //TODO: fill in! | |
await CreateCertificate(configBody, functionAppName, client, log); | |
configBody.AzureEnvironment.WebAppName = "blog"; //TODO: fill in! | |
configBody.AcmeConfig.Host = "blog.yannickreekmans.be"; //TODO: fill in! | |
await CreateCertificate(configBody, functionAppName, client, log); | |
} | |
private static async Task CreateCertificate(dynamic config, string functionAppName, HttpClient client, ILogger log) { | |
log.LogInformation($"Creating certificate for {config.AcmeConfig.Host}"); | |
var executionResult = await client.PostAsync($"https://{functionAppName}.scm.azurewebsites.net/letsencrypt/api/certificates/challengeprovider/http/kudu/certificateinstall/azurewebapp?api-version=2017-09-01", | |
new StringContent(JsonConvert.SerializeObject(config), Encoding.UTF8, "application/json")); | |
log.LogInformation(await executionResult.Content.ReadAsStringAsync()); | |
log.LogInformation($"Done creating certificate for {config.AcmeConfig.Host}"); | |
} | |
private static async Task<string> GetSecretFromKeyVault(string keyVaultUrl, string secretName) { | |
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(); | |
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); | |
SecretBundle secretValue = await keyVaultClient.GetSecretAsync($"{keyVaultUrl}/secrets/{secretName}"); | |
return secretValue.Value; | |
} | |
internal class Config | |
{ | |
internal AzureEnvironment AzureEnvironment { get; set; } | |
internal AcmeConfig AcmeConfig { get; set; } | |
internal CertificateSettings CertificateSettings { get; set; } | |
internal AuthorizationChallengeProviderConfig AuthorizationChallengeProviderConfig { get; set; } | |
} | |
internal class AzureEnvironment | |
{ | |
internal string WebAppName { get; set; } | |
internal string ClientId { get; set; } | |
internal string ClientSecret { get; set; } | |
internal string ResourceGroupName { get; set; } | |
internal string SubscriptionId { get; set; } | |
internal string Tenant { get; set; } | |
} | |
internal class AcmeConfig { | |
internal string RegistrationEmail { get; set; } | |
internal string Host { get; set; } | |
internal string[] AlternateNames { get; set; } | |
internal int RSAKeyLength { get; set; } | |
internal string PFXPassword { get; set; } | |
internal bool UseProduction { get; set; } | |
} | |
internal class CertificateSettings { | |
internal bool UseIPBasedSSL { get; set; } | |
} | |
internal class AuthorizationChallengeProviderConfig { | |
internal bool DisableWebConfigUpdate { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment