private static LetsencryptService GetDnsRenewalService(RenewalParameters renewalParams, IAzureDnsEnvironment azureDnsEnvironment, AzureWebAppEnvironment webAppEnvironment)
 {
     return(new LetsencryptService(
                new AcmeClient(
                    new AzureDnsProvider(
                        new AzureDnsSettings(
                            azureDnsEnvironment.ResourceGroupName,
                            azureDnsEnvironment.ZoneName,
                            GetAzureServicePrincipal(azureDnsEnvironment),
                            GetAzureSubscription(azureDnsEnvironment),
                            azureDnsEnvironment.RelativeRecordSetName)),
                    new DnsLookupService(new Logger <DnsLookupService>(s_loggerFactory)),
                    new NullCertificateStore(),
                    new Logger <AcmeClient>(s_loggerFactory)),
                new NullCertificateStore(),
                new AzureWebAppService(
                    new[]
     {
         new AzureWebAppSettings(
             webAppEnvironment.WebAppName,
             webAppEnvironment.ResourceGroupName,
             GetAzureServicePrincipal(webAppEnvironment),
             GetAzureSubscription(webAppEnvironment),
             webAppEnvironment.SiteSlotName,
             webAppEnvironment.ServicePlanResourceGroupName,
             renewalParams.UseIpBasedSsl),
     },
                    new Logger <AzureWebAppService>(s_loggerFactory)),
                new Logger <LetsencryptService>(s_loggerFactory)));
 }
        private static async Task <bool> CheckCertAddition(
            RenewalParameters renewalParams,
            AzureWebAppEnvironment webAppEnvironment,
            AcmeConfig acmeConfig,
            bool staging)
        {
            if (renewalParams.RenewXNumberOfDaysBeforeExpiration <= 0)
            {
                return(true);
            }

            var letsEncryptHostNames = await CertificateHelper.GetLetsEncryptHostNames(webAppEnvironment, staging).ConfigureAwait(false);

            Trace.TraceInformation("Let's Encrypt host names (staging: {0}): {1}", staging, String.Join(", ", letsEncryptHostNames));

            ICollection <string> missingHostNames = acmeConfig.Hostnames.Except(letsEncryptHostNames, StringComparer.OrdinalIgnoreCase).ToArray();

            if (missingHostNames.Count > 0)
            {
                Trace.TraceInformation(
                    "Detected host name(s) with no associated Let's Encrypt certificates, will add a new certificate: {0}",
                    String.Join(", ", missingHostNames));
                return(true);
            }

            Trace.TraceInformation("All host names associated with Let's Encrypt certificates, will perform cert renewal");
            return(false);
        }
Exemple #3
0
        private static IDnsProvider GetDnsProvider(RenewalParameters renewalParams)
        {
            var azureDnsEnvironment = GetAzureDnsEnvironment(renewalParams);

            if (azureDnsEnvironment != null)
            {
                return(new AzureDnsProvider(
                           new AzureDnsSettings(
                               azureDnsEnvironment.ResourceGroupName,
                               azureDnsEnvironment.ZoneName,
                               GetAzureServicePrincipal(azureDnsEnvironment),
                               GetAzureSubscription(azureDnsEnvironment),
                               azureDnsEnvironment.RelativeRecordSetName)));
            }

            if (!string.IsNullOrEmpty(renewalParams.GoDaddyDnsEnvironmentParams?.ApiKey) &&
                !string.IsNullOrEmpty(renewalParams.GoDaddyDnsEnvironmentParams?.ApiSecret) &&
                !string.IsNullOrEmpty(renewalParams.GoDaddyDnsEnvironmentParams?.Domain) &&
                !string.IsNullOrEmpty(renewalParams.GoDaddyDnsEnvironmentParams?.ShopperId))
            {
                return(new CustomGoDaddyDnsProvider(
                           new CustomGoDaddyDnsProvider.GoDaddyDnsSettings()
                {
                    ApiKey = renewalParams.GoDaddyDnsEnvironmentParams.ApiKey,
                    ApiSecret = renewalParams.GoDaddyDnsEnvironmentParams.ApiSecret,
                    Domain = renewalParams.GoDaddyDnsEnvironmentParams.Domain,
                    ShopperId = renewalParams.GoDaddyDnsEnvironmentParams.ShopperId,
                }));
            }

            Trace.TraceInformation($"Either {nameof(renewalParams.GoDaddyDnsEnvironmentParams.ApiKey)} or {nameof(renewalParams.GoDaddyDnsEnvironmentParams.ApiSecret)} or {nameof(renewalParams.GoDaddyDnsEnvironmentParams.Domain)} or {nameof(renewalParams.GoDaddyDnsEnvironmentParams.ShopperId)} are null for {GetWebAppFullName(renewalParams)}, will not use GoDaddy DNS challenge");
            return(null);
        }
Exemple #4
0
        private static readonly RNGCryptoServiceProvider s_randomGenerator = new RNGCryptoServiceProvider(); // thread-safe

        public Task Renew(RenewalParameters renewalParams)
        {
            if (renewalParams == null)
            {
                throw new ArgumentNullException(nameof(renewalParams));
            }

            return(RenewCore(renewalParams));
        }
Exemple #5
0
        private static readonly RNGCryptoServiceProvider s_randomGenerator = new RNGCryptoServiceProvider(); // thread-safe

        public async Task Renew(RenewalParameters renewalParams)
        {
            if (renewalParams == null)
            {
                throw new ArgumentNullException(nameof(renewalParams));
            }

            Trace.TraceInformation("Generating SSL certificate with parameters: {0}", renewalParams);

            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewalParams.WebApp);
            var pfxPassData = new byte[32];

            s_randomGenerator.GetBytes(pfxPassData);

            Trace.TraceInformation("Adding SSL cert for '{0}'...", renewalParams.WebApp);

            var manager = CertificateManager.CreateKuduWebAppCertificateManager(
                new AzureWebAppEnvironment(
                    renewalParams.TenantId,
                    renewalParams.SubscriptionId,
                    renewalParams.ClientId,
                    renewalParams.ClientSecret,
                    renewalParams.ResourceGroup,
                    renewalParams.WebApp,
                    renewalParams.ServicePlanResourceGroup,
                    renewalParams.SiteSlotName)
            {
                AzureWebSitesDefaultDomainName = renewalParams.AzureDefaultWebsiteDomainName ?? DefaultWebsiteDomainName,
                AuthenticationEndpoint         = renewalParams.AuthenticationUri ?? new Uri(DefaultAuthenticationUri),
                ManagementEndpoint             = renewalParams.AzureManagementEndpoint ?? new Uri(DefaultManagementEndpoint),
                TokenAudience = renewalParams.AzureTokenAudience ?? new Uri(DefaultAzureTokenAudienceService)
            },
                new AcmeConfig
            {
                Host              = renewalParams.Hosts[0],
                AlternateNames    = renewalParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewalParams.Email,
                RSAKeyLength      = renewalParams.RsaKeyLength,
                PFXPassword       = Convert.ToBase64String(pfxPassData),
                BaseUri           = (renewalParams.AcmeBaseUri ?? new Uri(DefaultAcmeBaseUri)).ToString()
            },
                new CertificateServiceSettings {
                UseIPBasedSSL = renewalParams.UseIpBasedSsl
            },
                new AuthProviderConfig());

            if (renewalParams.RenewXNumberOfDaysBeforeExpiration > 0)
            {
                await manager.RenewCertificate(false, renewalParams.RenewXNumberOfDaysBeforeExpiration);
            }
            else
            {
                await manager.AddCertificate();
            }

            Trace.TraceInformation("SSL cert added successfully to '{0}'", renewalParams.WebApp);
        }
Exemple #6
0
        private static async Task RenewCore(RenewalParameters renewalParams)
        {
            Trace.TraceInformation("Generating SSL certificate with parameters: {0}", renewalParams);

            var acmeConfig                 = GetAcmeConfig(renewalParams);
            var webAppEnvironment          = GetWebAppEnvironment(renewalParams);
            var certificateServiceSettings = new CertificateServiceSettings {
                UseIPBasedSSL = renewalParams.UseIpBasedSsl
            };
            var azureDnsEnvironment = GetAzureDnsEnvironment(renewalParams);

            if (azureDnsEnvironment != null)
            {
                throw new ConfigurationErrorsException("Azure DNS challenge currently not supported");
            }

            var manager = CertificateManager.CreateKuduWebAppCertificateManager(webAppEnvironment, acmeConfig, certificateServiceSettings, new AuthProviderConfig());

            Trace.TraceInformation("Adding SSL cert for '{0}'...", GetWebAppFullName(renewalParams));

            bool addNewCert = true;

            if (renewalParams.RenewXNumberOfDaysBeforeExpiration > 0)
            {
                var staging = acmeConfig.BaseUri.Contains("staging", StringComparison.OrdinalIgnoreCase);
                var letsEncryptHostNames = await CertificateHelper.GetLetsEncryptHostNames(webAppEnvironment, staging);

                Trace.TraceInformation("Let's Encrypt host names (staging: {0}): {1}", staging, String.Join(", ", letsEncryptHostNames));

                ICollection <string> missingHostNames = acmeConfig.Hostnames.Except(letsEncryptHostNames, StringComparer.OrdinalIgnoreCase).ToArray();
                if (missingHostNames.Count > 0)
                {
                    Trace.TraceInformation(
                        "Detected host name(s) with no associated Let's Encrypt certificates, will add a new certificate: {0}",
                        String.Join(", ", missingHostNames));
                }
                else
                {
                    Trace.TraceInformation("All host names associated with Let's Encrypt certificates, will perform cert renewal");
                    addNewCert = false;
                }
            }

            if (addNewCert)
            {
                await manager.AddCertificate();
            }
            else
            {
                await manager.RenewCertificate(false, renewalParams.RenewXNumberOfDaysBeforeExpiration);
            }

            Trace.TraceInformation("Let's Encrypt SSL certs & bindings renewed for '{0}'", renewalParams.WebApp);
        }
        private static AcmeConfig GetAcmeConfig(RenewalParameters renewalParams, string pfxPassData)
        {
            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewalParams.WebApp);

            return(new AcmeConfig
            {
                Host = renewalParams.Hosts[0],
                AlternateNames = renewalParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewalParams.Email,
                RSAKeyLength = renewalParams.RsaKeyLength,
                PFXPassword = pfxPassData,
                BaseUri = (renewalParams.AcmeBaseUri ?? new Uri(DefaultAcmeBaseUri)).ToString(),
            });
        }
Exemple #8
0
        private static AcmeConfig GetAcmeConfig(RenewalParameters renewalParams)
        {
            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewalParams.WebApp);
            var pfxPassData = new byte[32];

            s_randomGenerator.GetBytes(pfxPassData);

            return(new AcmeConfig
            {
                Host = renewalParams.Hosts[0],
                AlternateNames = renewalParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewalParams.Email,
                RSAKeyLength = renewalParams.RsaKeyLength,
                PFXPassword = Convert.ToBase64String(pfxPassData),
                BaseUri = (renewalParams.AcmeBaseUri ?? new Uri(DefaultAcmeBaseUri)).ToString(),
            });
        }
        private static readonly RNGCryptoServiceProvider s_randomGenerator = new RNGCryptoServiceProvider(); // thread-safe

        public void Renew(RenewalParameters renewParams)
        {
            if (renewParams == null)
            {
                throw new ArgumentNullException(nameof(renewParams));
            }

            Trace.TraceInformation("Generating SSL certificate with parameters: {0}", renewParams);

            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewParams.WebApp);
            byte[] pfxPassData = new byte[32];
            s_randomGenerator.GetBytes(pfxPassData);

            Trace.TraceInformation("Adding SSL cert for '{0}'...", renewParams.WebApp);
            var manager = new CertificateManager(
                new AzureEnvironment(
                    renewParams.TenantId,
                    renewParams.SubscriptionId,
                    renewParams.ClientId,
                    renewParams.ClientSecret,
                    renewParams.ResourceGroup,
                    renewParams.WebApp,
                    renewParams.ServicePlanResourceGroup,
                    renewParams.SiteSlotName),
                new AcmeConfig
            {
                Host              = renewParams.Hosts[0],
                AlternateNames    = renewParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewParams.Email,
                RSAKeyLength      = renewParams.RsaKeyLength,
                PFXPassword       = Convert.ToBase64String(pfxPassData),
#pragma warning disable S1075
                BaseUri = (renewParams.AcmeBaseUri ?? new Uri("https://acme-v01.api.letsencrypt.org/")).ToString()
#pragma warning restore S1075
            },
                new CertificateServiceSettings {
                UseIPBasedSSL = renewParams.UseIpBasedSsl
            },
                new AuthProviderConfig());

            manager.AddCertificate();

            Trace.TraceInformation("SSL cert added successfully to '{0}'", renewParams.WebApp);
        }
        private static async Task RenewCore(RenewalParameters renewalParams)
        {
            Trace.TraceInformation("Adding / renewing SSL cert for '{0}' with parameters: {1}", GetWebAppFullName(renewalParams), renewalParams);

            var acmeConfig                 = GetAcmeConfig(renewalParams, CertificateHelper.GenerateSecurePassword());
            var webAppEnvironment          = GetWebAppEnvironment(renewalParams);
            var certificateServiceSettings = new CertificateServiceSettings {
                UseIPBasedSSL = renewalParams.UseIpBasedSsl
            };
            var azureDnsEnvironment = GetAzureDnsEnvironment(renewalParams);

            bool staging = acmeConfig.BaseUri.Contains("staging", StringComparison.OrdinalIgnoreCase);

            if (azureDnsEnvironment != null)
            {
                await GetDnsRenewalService(renewalParams, azureDnsEnvironment, webAppEnvironment).Run(
                    new AcmeDnsRequest
                {
                    AcmeEnvironment   = staging ? (AcmeEnvironment) new LetsEncryptStagingV2() : new LetsEncryptV2(),
                    RegistrationEmail = acmeConfig.RegistrationEmail,
                    Host        = acmeConfig.Host,
                    PFXPassword = CertificateHelper.GenerateSecurePassword(),
                    CsrInfo     = new CsrInfo(),
                }, renewalParams.RenewXNumberOfDaysBeforeExpiration).ConfigureAwait(false);
            }
            else
            {
                var manager    = CertificateManager.CreateKuduWebAppCertificateManager(webAppEnvironment, acmeConfig, certificateServiceSettings, new AuthProviderConfig());
                var addNewCert = await CheckCertAddition(renewalParams, webAppEnvironment, acmeConfig, staging).ConfigureAwait(false);

                if (addNewCert)
                {
                    await manager.AddCertificate().ConfigureAwait(false);
                }
                else
                {
                    await manager.RenewCertificate(false, renewalParams.RenewXNumberOfDaysBeforeExpiration).ConfigureAwait(false);
                }
            }

            Trace.TraceInformation("Let's Encrypt SSL certs & bindings renewed for '{0}'", renewalParams.WebApp);
        }
Exemple #11
0
        private static AzureWebAppEnvironment GetWebAppEnvironment(RenewalParameters renewalParams)
        {
            Debug.Assert(renewalParams.WebAppEnvironmentParams?.SubscriptionId != null, "renewalParams.WebAppEnvironmentParams?.SubscriptionId != null");
            Debug.Assert(renewalParams.WebAppEnvironmentParams?.ClientId != null, "renewalParams.WebAppEnvironmentParams?.ClientId != null");

            return(new AzureWebAppEnvironment(
                       renewalParams.WebAppEnvironmentParams.TenantId,
                       renewalParams.WebAppEnvironmentParams.SubscriptionId.Value,
                       renewalParams.WebAppEnvironmentParams.ClientId.Value,
                       renewalParams.WebAppEnvironmentParams.ClientSecret,
                       renewalParams.WebAppEnvironmentParams.ResourceGroup,
                       renewalParams.WebApp,
                       renewalParams.ServicePlanResourceGroup,
                       renewalParams.SiteSlotName)
            {
                WebRootPath = renewalParams.WebRootPath,
                AzureWebSitesDefaultDomainName = renewalParams.AzureDefaultWebsiteDomainName ?? DefaultWebsiteDomainName,
                AuthenticationEndpoint = renewalParams.AuthenticationUri ?? new Uri(DefaultAuthenticationUri),
                ManagementEndpoint = renewalParams.AzureManagementEndpoint ?? new Uri(DefaultManagementEndpoint),
                TokenAudience = renewalParams.AzureTokenAudience ?? new Uri(DefaultAzureTokenAudienceService),
            });
        }
Exemple #12
0
        private static IAzureDnsEnvironment GetAzureDnsEnvironment(RenewalParameters renewalParams)
        {
            var zoneName = renewalParams.AzureDnsZoneName;
            var relativeRecordSetName = renewalParams.AzureDnsRelativeRecordSetName;

            if (zoneName == null || relativeRecordSetName == null)
            {
                Trace.TraceInformation($"Either {nameof(zoneName)} or {nameof(relativeRecordSetName)} are null for {GetWebAppFullName(renewalParams)}, will not use Azure DNS challenge");
                return(null);
            }

            Debug.Assert(renewalParams.AzureDnsEnvironmentParams?.SubscriptionId != null, "renewalParams.AzureDnsEnvironmentParams?.SubscriptionId != null");
            Debug.Assert(renewalParams.AzureDnsEnvironmentParams?.ClientId != null, "renewalParams.AzureDnsEnvironmentParams?.ClientId != null");

            return(new AzureDnsEnvironment(
                       renewalParams.AzureDnsEnvironmentParams.TenantId,
                       renewalParams.AzureDnsEnvironmentParams.SubscriptionId.Value,
                       renewalParams.AzureDnsEnvironmentParams.ClientId.Value,
                       renewalParams.AzureDnsEnvironmentParams.ClientSecret,
                       renewalParams.AzureDnsEnvironmentParams.ResourceGroup,
                       zoneName,
                       relativeRecordSetName));
        }
Exemple #13
0
 private static string GetWebAppFullName(RenewalParameters renewalParams)
 {
     return(String.Format(CultureInfo.InvariantCulture, "{0}{1}", renewalParams.WebApp, renewalParams.GroupName == null ? String.Empty : $"[{renewalParams.GroupName}]"));
 }
Exemple #14
0
        private async Task <int> RenewCore(RenewalParameters renewalParams)
        {
            Trace.TraceInformation("Generating SSL certificate with parameters: {0}", renewalParams);

            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewalParams.WebApp);
            var pfxPassData = new byte[32];

            s_randomGenerator.GetBytes(pfxPassData);

            Trace.TraceInformation(
                "Adding SSL cert for '{0}{1}'...",
                renewalParams.WebApp,
                renewalParams.GroupName == null ? String.Empty : $"[{renewalParams.GroupName}]");

            azureEnvironment = new AzureWebAppEnvironment(
                renewalParams.TenantId,
                renewalParams.SubscriptionId,
                renewalParams.ClientId,
                renewalParams.ClientSecret,
                renewalParams.ResourceGroup,
                renewalParams.WebApp,
                renewalParams.ServicePlanResourceGroup,
                renewalParams.SiteSlotName)
            {
                WebRootPath = renewalParams.WebRootPath,
                AzureWebSitesDefaultDomainName = renewalParams.AzureDefaultWebsiteDomainName ?? DefaultWebsiteDomainName,
                AuthenticationEndpoint         = renewalParams.AuthenticationUri ?? new Uri(DefaultAuthenticationUri),
                ManagementEndpoint             = renewalParams.AzureManagementEndpoint ?? new Uri(DefaultManagementEndpoint),
                TokenAudience = renewalParams.AzureTokenAudience ?? new Uri(DefaultAzureTokenAudienceService),
            };

            var manager = CertificateManager.CreateKuduWebAppCertificateManager(
                azureEnvironment,
                new AcmeConfig
            {
                Host              = renewalParams.Hosts[0],
                AlternateNames    = renewalParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewalParams.Email,
                RSAKeyLength      = renewalParams.RsaKeyLength,
                PFXPassword       = Convert.ToBase64String(pfxPassData),
                BaseUri           = (renewalParams.AcmeBaseUri ?? new Uri(DefaultAcmeBaseUri)).ToString(),
            },
                new CertificateServiceSettings {
                UseIPBasedSSL = renewalParams.UseIpBasedSsl
            },
                new AuthProviderConfig());

            var certificatesRenewed = 0;

            using (webSiteClient = await ArmHelper.GetWebSiteManagementClient(azureEnvironment))
            {
                var isWebAppRunning = IsWebAppRunning();

                if (!isWebAppRunning && !await StartWebApp())
                {
                    string errorMessage = string.Format("Could not start WebApp '{0}' to renew certificate", renewalParams.WebApp);
                    Trace.TraceError(errorMessage);
                    throw new WebAppException(renewalParams.WebApp, "Could not start WebApp");
                }

                if (await HasCertificate())
                {
                    var result = await manager.RenewCertificate(false, renewalParams.RenewXNumberOfDaysBeforeExpiration);

                    certificatesRenewed = result.Count;
                }
                else
                {
                    var result = await manager.AddCertificate();

                    if (result != null)
                    {
                        certificatesRenewed = 1;
                    }
                }

                if (!isWebAppRunning && !await StopWebApp())
                {
                    Trace.TraceWarning("Could not stop WebApp '{0}' after renewing certificate", renewalParams.WebApp);
                }
            }

            Trace.TraceInformation("SSL cert added successfully to '{0}'", renewalParams.WebApp);

            return(certificatesRenewed);
        }
        private static async Task RenewCore(RenewalParameters renewalParams)
        {
            Trace.TraceInformation("Generating SSL certificate with parameters: {0}", renewalParams);

            Trace.TraceInformation("Generating secure PFX password for '{0}'...", renewalParams.WebApp);
            var pfxPassData = new byte[32];

            s_randomGenerator.GetBytes(pfxPassData);

            Trace.TraceInformation(
                "Adding SSL cert for '{0}{1}'...",
                renewalParams.WebApp,
                renewalParams.GroupName == null ? string.Empty : $"[{renewalParams.GroupName}]");

            var certServiceSettings = new CertificateServiceSettings {
                UseIPBasedSSL = renewalParams.UseIpBasedSsl
            };

            var azureWebAppEnvironment = new AzureWebAppEnvironment(
                renewalParams.TenantId,
                renewalParams.SubscriptionId,
                renewalParams.ClientId,
                renewalParams.ClientSecret,
                renewalParams.ResourceGroup,
                renewalParams.WebApp,
                renewalParams.ServicePlanResourceGroup,
                renewalParams.SiteSlotName)
            {
                AzureWebSitesDefaultDomainName = renewalParams.AzureDefaultWebsiteDomainName ?? DefaultWebsiteDomainName,
                AuthenticationEndpoint         = renewalParams.AuthenticationUri ?? new Uri(DefaultAuthenticationUri),
                ManagementEndpoint             = renewalParams.AzureManagementEndpoint ?? new Uri(DefaultManagementEndpoint),
                TokenAudience = renewalParams.AzureTokenAudience ?? new Uri(DefaultAzureTokenAudienceService)
            };

            AzureWebAppEnvironment   otherAzureWebAppEnvironment   = null;
            WebAppCertificateService otherWebAppCertificateService = null;

            if (!string.IsNullOrEmpty(renewalParams.OtherWebAppResourceGroup) && !string.IsNullOrEmpty(renewalParams.OtherWebApp))
            {
                otherAzureWebAppEnvironment = new AzureWebAppEnvironment(
                    renewalParams.TenantId,
                    renewalParams.SubscriptionId,
                    renewalParams.ClientId,
                    renewalParams.ClientSecret,
                    renewalParams.OtherWebAppResourceGroup,
                    renewalParams.OtherWebApp,
                    renewalParams.ServicePlanResourceGroup,
                    renewalParams.OtherSlotName)
                {
                    AzureWebSitesDefaultDomainName = renewalParams.AzureDefaultWebsiteDomainName ?? DefaultWebsiteDomainName,
                    AuthenticationEndpoint         = renewalParams.AuthenticationUri ?? new Uri(DefaultAuthenticationUri),
                    ManagementEndpoint             = renewalParams.AzureManagementEndpoint ?? new Uri(DefaultManagementEndpoint),
                    TokenAudience = renewalParams.AzureTokenAudience ?? new Uri(DefaultAzureTokenAudienceService)
                };

                otherWebAppCertificateService = new WebAppCertificateService(otherAzureWebAppEnvironment, certServiceSettings);
            }

            var azureStorageEnvironment = new AzureStorageEnvironment(
                renewalParams.TenantId,
                renewalParams.SubscriptionId,
                renewalParams.ClientId,
                renewalParams.ClientSecret,
                renewalParams.ResourceGroup,
                renewalParams.StorageConnectionString,
                renewalParams.StorageContainer);

            var webAppCertificateService = new WebAppCertificateService(azureWebAppEnvironment, certServiceSettings);

            var manager = new CertificateManager(
                azureWebAppEnvironment,
                new AcmeConfig
            {
                Host              = renewalParams.Hosts[0],
                AlternateNames    = renewalParams.Hosts.Skip(1).ToList(),
                RegistrationEmail = renewalParams.Email,
                RSAKeyLength      = renewalParams.RsaKeyLength,
                PFXPassword       = Convert.ToBase64String(pfxPassData),
                BaseUri           = (renewalParams.AcmeBaseUri ?? new Uri(DefaultAcmeBaseUri)).ToString()
            },
                webAppCertificateService,
                new AzureStorageFileSystemAuthorizationChallengeProvider(azureStorageEnvironment));

            if (renewalParams.RenewXNumberOfDaysBeforeExpiration > 0)
            {
                await manager.RenewCertificate(false, renewalParams.RenewXNumberOfDaysBeforeExpiration);
            }
            else
            {
                var res = await manager.AddCertificate();

                webAppCertificateService.RemoveExpired();
                otherWebAppCertificateService?.Install(res);
                otherWebAppCertificateService?.RemoveExpired();
            }

            Trace.TraceInformation("SSL cert added successfully to '{0}'", renewalParams.WebApp);
        }