Beispiel #1
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(ManagedSite managedSite, IProgress <RequestProgressState> progress = null)
        {
            // FIXME: refactor into different concerns, there's way to much being done here

            return(await Task.Run(async() =>
            {
                // start with a failure result, set to success when succeeding
                var result = new CertificateRequestResult {
                    ManagedItem = managedSite, IsSuccess = false, Message = ""
                };

                var config = managedSite.RequestConfig;
                try
                {
                    // run pre-request script, if set
                    if (!string.IsNullOrEmpty(config.PreRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PreRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Pre-Request Script output: \n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Pre-Request Script error:\n{ex.Message}");
                        }
                    }

                    // if the script has requested the certificate request to be aborted, skip the request
                    if (result.Abort)
                    {
                        LogMessage(managedSite.Id, $"Certificate Request Aborted: {managedSite.Name}");
                        result.Message = "Certificate Request was aborted by PS script";
                        goto CertRequestAborted;
                    }

                    LogMessage(managedSite.Id, $"Beginning Certificate Request Process: {managedSite.Name}");

                    //enable or disable EFS flag on private key certs based on preference
                    if (Properties.Settings.Default.EnableEFS)
                    {
                        _vaultProvider.EnableSensitiveFileEncryption();
                    }

                    //primary domain and each subject alternative name must now be registered as an identifier with LE and validated
                    ReportProgress(progress, new RequestProgressState {
                        IsRunning = true, CurrentState = RequestState.Running, Message = "Registering Domain Identifiers"
                    });

                    await Task.Delay(200); //allow UI update, we should we using async calls instead

                    List <string> allDomains = new List <string> {
                        config.PrimaryDomain
                    };

                    if (config.SubjectAlternativeNames != null)
                    {
                        allDomains.AddRange(config.SubjectAlternativeNames);
                    }

                    // begin by assuming all identifiers are valid
                    bool allIdentifiersValidated = true;

                    if (config.ChallengeType == null)
                    {
                        config.ChallengeType = "http-01";
                    }

                    List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();
                    var distinctDomains = allDomains.Distinct();

                    // perform validation process for each domain
                    foreach (var domain in distinctDomains)
                    {
                        //begin authorization process (register identifier, request authorization if not already given)
                        var domainIdentifierId = _vaultProvider.ComputeDomainIdentifierId(domain);

                        LogMessage(managedSite.Id, $"Attempting Domain Validation: {domain}", LogItemType.CertificateRequestStarted);
                        ReportProgress(progress, $"Registering and Validating {domain} ");

                        //TODO: make operations async and yield IO of vault

                        /*var authorization = await Task.Run(() =>
                         * {
                         *  return vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);
                         * });*/

                        // begin authorization by registering the domain identifier. This may return
                        // an already validated authorization or we may still have to complete the
                        // authorization challenge
                        var authorization = _vaultProvider.BeginRegistrationAndValidation(config, domainIdentifierId, challengeType: config.ChallengeType, domain: domain);

                        if (authorization != null && authorization.Identifier != null)
                        {
                            // check if authorization is pending, it may already be valid if an
                            // existing authorization was reused
                            if (authorization.Identifier.IsAuthorizationPending)
                            {
                                if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                                {
                                    ReportProgress(progress, $"Performing Challenge Response via IIS: {domain} ");

                                    //ask LE to check our answer to their authorization challenge (http), LE will then attempt to fetch our answer, if all accessible and correct (authorized) LE will then allow us to request a certificate
                                    //prepare IIS with answer for the LE challenege
                                    authorization = _vaultProvider.PerformIISAutomatedChallengeResponse(_iisManager, managedSite, authorization);

                                    //if we attempted extensionless config checks, report any errors
                                    if (config.PerformAutoConfig && !authorization.ExtensionlessConfigCheckedOK)
                                    {
                                        LogMessage(managedSite.Id, $"Failed prerequisite configuration checks ({ managedSite.ItemType })", LogItemType.CertficateRequestFailed);

                                        _siteManager.StoreSettings();

                                        result.Message = "Automated configuration checks failed. Authorizations will not be able to complete.\nCheck you have http bindings for your site and ensure you can browse to http://" + domain + "/.well-known/acme-challenge/configcheck before proceeding.";
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Error, Message = result.Message, Result = result
                                        });

                                        break;
                                    }
                                    else
                                    {
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Running, Message = $"Requesting Validation from Let's Encrypt: {domain}"
                                        });

                                        //ask LE to validate our challenge response
                                        _vaultProvider.SubmitChallenge(domainIdentifierId, config.ChallengeType);

                                        bool identifierValidated = _vaultProvider.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                        if (!identifierValidated)
                                        {
                                            ReportProgress(progress, new RequestProgressState {
                                                CurrentState = RequestState.Error, Message = "Domain validation failed: " + domain
                                            }, managedSite.Id);

                                            allIdentifiersValidated = false;
                                        }
                                        else
                                        {
                                            ReportProgress(progress, new RequestProgressState {
                                                CurrentState = RequestState.Running, Message = "Domain validation completed: " + domain
                                            }, managedSite.Id);

                                            identifierAuthorizations.Add(authorization);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // we already have a completed authorization, check it's valid
                                if (authorization.Identifier.Status == "valid")
                                {
                                    LogMessage(managedSite.Id, $"Domain already has current authorization, skipping verification: { domain }");

                                    identifierAuthorizations.Add(new PendingAuthorization {
                                        Identifier = authorization.Identifier
                                    });
                                }
                                else
                                {
                                    LogMessage(managedSite.Id, $"Domain authorization failed : { domain } ");

                                    allIdentifiersValidated = false;
                                }
                            }
                        }
                        else
                        {
                            // could not begin authorization
                            LogMessage(managedSite.Id, $"Could not begin authorization for domain with Let's Encrypt: { domain } ");
                            allIdentifiersValidated = false;
                        }

                        // abandon authorization attempts if one of our domains has failed verification
                        if (!allIdentifiersValidated)
                        {
                            break;
                        }
                    }

                    //check if all identifiers have a valid authorization
                    if (identifierAuthorizations.Count != distinctDomains.Count())
                    {
                        allIdentifiersValidated = false;
                    }

                    if (allIdentifiersValidated)
                    {
                        string primaryDnsIdentifier = identifierAuthorizations.First().Identifier.Alias;
                        string[] alternativeDnsIdentifiers = identifierAuthorizations.Select(i => i.Identifier.Alias).ToArray();

                        ReportProgress(progress, new RequestProgressState {
                            CurrentState = RequestState.Running, Message = "Requesting Certificate via Lets Encrypt"
                        }, managedSite.Id);

                        // Perform CSR request
                        // FIXME: make call async
                        var certRequestResult = _vaultProvider.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);

                        if (certRequestResult.IsSuccess)
                        {
                            ReportProgress(progress, new RequestProgressState {
                                CurrentState = RequestState.Success, Message = "Completed Certificate Request."
                            }, managedSite.Id);

                            string pfxPath = certRequestResult.Result.ToString();

                            // update managed site summary
                            try
                            {
                                var certInfo = new CertificateManager().GetCertificate(pfxPath);
                                managedSite.DateStart = certInfo.NotBefore;
                                managedSite.DateExpiry = certInfo.NotAfter;
                                managedSite.DateRenewed = DateTime.Now;

                                managedSite.CertificatePath = pfxPath;

                                //ensure certificate contains all the requested domains
                                var subjectNames = certInfo.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.UpnName, false);

                                LogMessage(managedSite.Id, "New certificate contains following domains: " + subjectNames, LogItemType.GeneralInfo);
                            }
                            catch (Exception)
                            {
                                LogMessage(managedSite.Id, "Failed to parse certificate dates", LogItemType.GeneralError);
                            }

                            if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                            {
                                ReportProgress(progress, new RequestProgressState {
                                    CurrentState = RequestState.Running, Message = "Performing Automated Certificate Binding"
                                });

                                // Install certificate into certificate store and bind to IIS site
                                if (_iisManager.InstallCertForRequest(managedSite, pfxPath, cleanupCertStore: true))
                                {
                                    //all done
                                    LogMessage(managedSite.Id, "Completed certificate request and automated bindings update (IIS)", LogItemType.CertificateRequestSuccessful);

                                    _siteManager.UpdatedManagedSite(managedSite);

                                    result.IsSuccess = true;
                                    result.Message = $"Certificate installed and SSL bindings updated for {config.PrimaryDomain }";
                                    ReportProgress(progress, new RequestProgressState {
                                        IsRunning = false, CurrentState = RequestState.Success, Message = result.Message
                                    });
                                }
                                else
                                {
                                    result.Message = $"An error occurred installing the certificate. Certificate file may not be valid: {pfxPath}";
                                    LogMessage(managedSite.Id, result.Message, LogItemType.GeneralError);
                                }
                            }
                            else
                            {
                                //user has opted for manual binding of certificate

                                _siteManager.UpdatedManagedSite(managedSite);

                                result.IsSuccess = true;
                                result.Message = $"Certificate created ready for manual binding: {pfxPath}";
                                LogMessage(managedSite.Id, result.Message, LogItemType.CertificateRequestSuccessful);
                            }
                        }
                        else
                        {
                            result.Message = $"The Let's Encrypt service did not issue a valid certificate in the time allowed. {(certRequestResult.ErrorMessage ?? "")}";
                            LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                        }
                    }
                    else
                    {
                        result.Message = "Validation of the required challenges did not complete successfully. Please ensure all domains to be referenced in the Certificate can be used to access this site without redirection. ";
                        LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    }

                    // Goto label for aborted certificate request
                    CertRequestAborted : { }
                }
                catch (Exception exp)
                {
                    result.IsSuccess = false;
                    result.Message = managedSite.Name + ": Request failed - " + exp.Message + " " + exp.ToString();
                    LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);

                    System.Diagnostics.Debug.WriteLine(exp.ToString());
                }
                finally
                {
                    // if the request was not aborted, run post-request script, if set
                    if (!result.Abort && !string.IsNullOrEmpty(config.PostRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PostRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Post-Request Script output:\n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Post-Request Script error:\n{ex.Message}");
                        }
                    }
                }
                return result;
            }));
        }
Beispiel #2
0
        public async Task <CertificateRequestResult> PerformCertificateRequest(ManagedSite managedSite, IProgress <RequestProgressState> progress = null)
        {
            // FIXME: refactor into different concerns, there's way too much being done here
            if (managedSite.RequestConfig.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP && managedSite.RequestConfig.PerformExtensionlessConfigChecks)
            {
                ReportProgress(progress, new RequestProgressState {
                    IsRunning = true, CurrentState = RequestState.Running, Message = Certify.CoreSR.CertifyManager_PerformingConfigTests
                });

                var testResult = await TestChallenge(managedSite, isPreviewMode : false);

                if (!testResult.IsOK)
                {
                    return(new CertificateRequestResult {
                        ManagedItem = managedSite, IsSuccess = false, Message = String.Join("; ", testResult.FailedItemSummary), Result = testResult.Result
                    });
                }
            }

            return(await Task.Run(async() =>
            {
                // start with a failure result, set to success when succeeding
                var result = new CertificateRequestResult {
                    ManagedItem = managedSite, IsSuccess = false, Message = ""
                };

                var config = managedSite.RequestConfig;
                try
                {
                    // run pre-request script, if set
                    if (!string.IsNullOrEmpty(config.PreRequestPowerShellScript))
                    {
                        try
                        {
                            string scriptOutput = await PowerShellManager.RunScript(result, config.PreRequestPowerShellScript);
                            LogMessage(managedSite.Id, $"Pre-Request Script output: \n{scriptOutput}");
                        }
                        catch (Exception ex)
                        {
                            LogMessage(managedSite.Id, $"Pre-Request Script error:\n{ex.Message}");
                        }
                    }

                    // if the script has requested the certificate request to be aborted, skip the request
                    if (result.Abort)
                    {
                        LogMessage(managedSite.Id, $"Certificate Request Aborted: {managedSite.Name}");
                        result.Message = Certify.CoreSR.CertificateRequestWasAbortedByPSScript;
                        goto CertRequestAborted;
                    }

                    LogMessage(managedSite.Id, $"Beginning Certificate Request Process: {managedSite.Name}");

                    //enable or disable EFS flag on private key certs based on preference
                    if (CoreAppSettings.Current.EnableEFS)
                    {
                        _vaultProvider.EnableSensitiveFileEncryption();
                    }

                    //primary domain and each subject alternative name must now be registered as an identifier with LE and validated
                    ReportProgress(progress, new RequestProgressState {
                        IsRunning = true, CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_RegisterDomainIdentity
                    });

                    await Task.Delay(200); //allow UI update, we should we using async calls instead

                    List <string> allDomains = new List <string> {
                        config.PrimaryDomain
                    };

                    if (config.SubjectAlternativeNames != null)
                    {
                        allDomains.AddRange(config.SubjectAlternativeNames);
                    }

                    // begin by assuming all identifiers are valid
                    bool allIdentifiersValidated = true;

                    if (config.ChallengeType == null)
                    {
                        config.ChallengeType = ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP;
                    }

                    List <PendingAuthorization> identifierAuthorizations = new List <PendingAuthorization>();
                    var distinctDomains = allDomains.Distinct();

                    string failureSummaryMessage = null;

                    // perform validation process for each domain
                    foreach (var domain in distinctDomains)
                    {
                        //begin authorization process (register identifier, request authorization if not already given)
                        var domainIdentifierId = _vaultProvider.ComputeDomainIdentifierId(domain);

                        LogMessage(managedSite.Id, $"Attempting Domain Validation: {domain}", LogItemType.CertificateRequestStarted);
                        ReportProgress(progress, string.Format(Certify.CoreSR.CertifyManager_RegisteringAndValidatingX0, domain));

                        //TODO: make operations async and yield IO of vault

                        /*var authorization = await Task.Run(() =>
                         * {
                         *  return vaultManager.BeginRegistrationAndValidation(config, identifierAlias, challengeType: config.ChallengeType, domain: domain);
                         * });*/

                        // begin authorization by registering the domain identifier. This may return
                        // an already validated authorization or we may still have to complete the
                        // authorization challenge. When rate limits are encountered, this step may fail.
                        var authorization = _vaultProvider.BeginRegistrationAndValidation(config, domainIdentifierId, challengeType: config.ChallengeType, domain: domain);

                        if (authorization != null && authorization.Identifier != null)
                        {
                            // check if authorization is pending, it may already be valid if an
                            // existing authorization was reused
                            if (authorization.Identifier.IsAuthorizationPending)
                            {
                                if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS)
                                {
                                    ReportProgress(progress, string.Format(Certify.CoreSR.CertifyManager_PerformingChallengeResponseViaIISX0, domain));

                                    // ask LE to check our answer to their authorization challenge
                                    // (http-01 or tls-sni-01), LE will then attempt to fetch our
                                    // answer, if all accessible and correct (authorized) LE will
                                    // then allow us to request a certificate
                                    authorization = _vaultProvider.PerformIISAutomatedChallengeResponse(_iisManager, managedSite, authorization);

                                    // pass authorization log items onto main log

                                    /*authorization.LogItems?.ForEach((msg) =>
                                     * {
                                     *  if (msg != null) LogMessage(managedSite.Id, msg, LogItemType.GeneralInfo);
                                     * });*/

                                    if ((config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP && config.PerformExtensionlessConfigChecks && !authorization.ExtensionlessConfigCheckedOK) ||
                                        (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI && config.PerformTlsSniBindingConfigChecks && !authorization.TlsSniConfigCheckedOK))
                                    {
                                        //if we failed the config checks, report any errors
                                        LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_FailedPrerequisiteCheck, managedSite.ItemType), LogItemType.CertficateRequestFailed);

                                        _siteManager.StoreSettings();

                                        if (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_HTTP)
                                        {
                                            result.Message = string.Format(CoreSR.CertifyManager_AutomateConfigurationCheckFailed_HTTP, domain);
                                        }

                                        if (config.ChallengeType == ACMESharpCompat.ACMESharpUtils.CHALLENGE_TYPE_SNI)
                                        {
                                            result.Message = Certify.CoreSR.CertifyManager_AutomateConfigurationCheckFailed_SNI;
                                        }

                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Error, Message = result.Message, Result = result
                                        });

                                        break;
                                    }
                                    else
                                    {
                                        ReportProgress(progress, new RequestProgressState {
                                            CurrentState = RequestState.Running, Message = string.Format(CoreSR.CertifyManager_ReqestValidationFromLetsEncrypt, domain)
                                        });
                                        try
                                        {
                                            //ask LE to validate our challenge response
                                            _vaultProvider.SubmitChallenge(domainIdentifierId, config.ChallengeType);

                                            bool identifierValidated = _vaultProvider.CompleteIdentifierValidationProcess(authorization.Identifier.Alias);

                                            if (!identifierValidated)
                                            {
                                                var identifierInfo = _vaultProvider.GetDomainIdentifier(domain);
                                                var errorMsg = identifierInfo?.ValidationError;
                                                var errorType = identifierInfo?.ValidationErrorType;

                                                failureSummaryMessage = string.Format(CoreSR.CertifyManager_DomainValidationFailed, domain, errorMsg);
                                                ReportProgress(progress, new RequestProgressState {
                                                    CurrentState = RequestState.Error, Message = failureSummaryMessage
                                                }, managedSite.Id);

                                                allIdentifiersValidated = false;
                                            }
                                            else
                                            {
                                                ReportProgress(progress, new RequestProgressState {
                                                    CurrentState = RequestState.Running, Message = string.Format(CoreSR.CertifyManager_DomainValidationCompleted, domain)
                                                }, managedSite.Id);

                                                identifierAuthorizations.Add(authorization);
                                            }
                                        }
                                        finally
                                        {
                                            // clean up challenge answers
                                            // (.well-known/acme-challenge/* files for http-01 or iis
                                            // bindings for tls-sni-01)
                                            authorization.Cleanup();
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // we already have a completed authorization, check it's valid
                                if (authorization.Identifier.Status == "valid")
                                {
                                    LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_DomainValidationSkipVerifed, domain));

                                    identifierAuthorizations.Add(new PendingAuthorization {
                                        Identifier = authorization.Identifier
                                    });
                                }
                                else
                                {
                                    var errorMsg = "";
                                    if (authorization?.Identifier != null)
                                    {
                                        errorMsg = authorization.Identifier.ValidationError;
                                        var errorType = authorization.Identifier.ValidationErrorType;
                                    }

                                    failureSummaryMessage = $"Domain validation failed: {domain} \r\n{errorMsg}";

                                    LogMessage(managedSite.Id, string.Format(CoreSR.CertifyManager_DomainValidationFailed, domain));

                                    allIdentifiersValidated = false;
                                }
                            }
                        }
                        else
                        {
                            // could not begin authorization : TODO: pass error from authorization
                            // step to UI

                            var lastActionLogItem = _vaultProvider.GetLastActionLogItem();
                            var actionLogMsg = "";
                            if (lastActionLogItem != null)
                            {
                                actionLogMsg = lastActionLogItem.ToString();
                            }

                            LogMessage(managedSite.Id, $"Could not begin authorization for domain with Let's Encrypt: { domain } {(authorization?.AuthorizationError != null ? authorization?.AuthorizationError : "Could not register domain identifier")} - {actionLogMsg}");

                            /*if (authorization != null && authorization.LogItems != null)
                             * {
                             *  LogMessage(managedSite.Id, authorization.LogItems);
                             * }*/
                            allIdentifiersValidated = false;
                        }

                        // abandon authorization attempts if one of our domains has failed verification
                        if (!allIdentifiersValidated)
                        {
                            break;
                        }
                    }

                    //check if all identifiers have a valid authorization
                    if (identifierAuthorizations.Count != distinctDomains.Count())
                    {
                        allIdentifiersValidated = false;
                    }

                    if (allIdentifiersValidated)
                    {
                        string primaryDnsIdentifier = identifierAuthorizations.First().Identifier.Alias;
                        string[] alternativeDnsIdentifiers = identifierAuthorizations.Select(i => i.Identifier.Alias).ToArray();

                        ReportProgress(progress, new RequestProgressState {
                            CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_RequestCertificate
                        }, managedSite.Id);

                        // Perform CSR request
                        // FIXME: make call async
                        var certRequestResult = _vaultProvider.PerformCertificateRequestProcess(primaryDnsIdentifier, alternativeDnsIdentifiers);

                        if (certRequestResult.IsSuccess)
                        {
                            ReportProgress(progress, new RequestProgressState {
                                CurrentState = RequestState.Success, Message = CoreSR.CertifyManager_CompleteRequest
                            }, managedSite.Id);

                            string pfxPath = certRequestResult.Result.ToString();

                            // update managed site summary
                            try
                            {
                                var certInfo = CertificateManager.LoadCertificate(pfxPath);
                                managedSite.DateStart = certInfo.NotBefore;
                                managedSite.DateExpiry = certInfo.NotAfter;
                                managedSite.DateRenewed = DateTime.Now;

                                managedSite.CertificatePath = pfxPath;
                                managedSite.CertificateRevoked = false;

                                //ensure certificate contains all the requested domains
                                var subjectNames = certInfo.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.UpnName, false);

                                //FIXME: LogMessage(managedSite.Id, "New certificate contains following domains: " + subjectNames, LogItemType.GeneralInfo);
                            }
                            catch (Exception)
                            {
                                LogMessage(managedSite.Id, "Failed to parse certificate dates", LogItemType.GeneralError);
                            }

                            if (managedSite.ItemType == ManagedItemType.SSL_LetsEncrypt_LocalIIS && config.PerformAutomatedCertBinding)
                            {
                                ReportProgress(progress, new RequestProgressState {
                                    CurrentState = RequestState.Running, Message = CoreSR.CertifyManager_AutoBinding
                                });

                                // Install certificate into certificate store and bind to IIS site
                                if (_iisManager.InstallCertForRequest(managedSite, pfxPath, cleanupCertStore: true))
                                {
                                    //all done
                                    LogMessage(managedSite.Id, CoreSR.CertifyManager_CompleteRequestAndUpdateBinding, LogItemType.CertificateRequestSuccessful);

                                    _siteManager.UpdatedManagedSite(managedSite);

                                    result.IsSuccess = true;
                                    result.Message = string.Format(CoreSR.CertifyManager_CertificateInstalledAndBindingUpdated, config.PrimaryDomain);
                                    ReportProgress(progress, new RequestProgressState {
                                        IsRunning = false, CurrentState = RequestState.Success, Message = result.Message
                                    });
                                }
                                else
                                {
                                    result.Message = string.Format(CoreSR.CertifyManager_CertificateInstallFailed, pfxPath);
                                    LogMessage(managedSite.Id, result.Message, LogItemType.GeneralError);
                                }
                            }
                            else
                            {
                                //user has opted for manual binding of certificate

                                _siteManager.UpdatedManagedSite(managedSite);

                                result.IsSuccess = true;
                                result.Message = string.Format(CoreSR.CertifyManager_CertificateCreatedForBinding, pfxPath);
                                LogMessage(managedSite.Id, result.Message, LogItemType.CertificateRequestSuccessful);
                            }
                        }
                        else
                        {
                            result.Message = string.Format(CoreSR.CertifyManager_LetsEncryptServiceTimeout, certRequestResult.ErrorMessage ?? "");
                            LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                        }
                    }
                    else
                    {
                        result.Message = string.Format(CoreSR.CertifyManager_ValidationForChallengeNotSuccess, (failureSummaryMessage != null ? failureSummaryMessage : ""));
                        LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    }

                    // Goto label for aborted certificate request
                    CertRequestAborted : { }
                }
                catch (Exception exp)
                {
                    result.IsSuccess = false;
                    result.Message = string.Format(Certify.CoreSR.CertifyManager_RequestFailed, managedSite.Name, exp.Message, exp);
                    LogMessage(managedSite.Id, result.Message, LogItemType.CertficateRequestFailed);
                    //LogMessage(managedSite.Id, String.Join("\r\n", _vaultProvider.GetActionSummary())); FIXME: needs to be filtered in managed site
                    System.Diagnostics.Debug.WriteLine(exp.ToString());
                }
                finally
                {
                    // if the request was not aborted, perform post-request actions
                    if (!result.Abort)
                    {
                        // run post-request script, if set
                        if (!string.IsNullOrEmpty(config.PostRequestPowerShellScript))
                        {
                            try
                            {
                                string scriptOutput = await PowerShellManager.RunScript(result, config.PostRequestPowerShellScript);
                                LogMessage(managedSite.Id, $"Post-Request Script output:\n{scriptOutput}");
                            }
                            catch (Exception ex)
                            {
                                LogMessage(managedSite.Id, $"Post-Request Script error: {ex.Message}");
                            }
                        }
                        // run webhook triggers, if set
                        if ((config.WebhookTrigger == Webhook.ON_SUCCESS && result.IsSuccess) ||
                            (config.WebhookTrigger == Webhook.ON_ERROR && !result.IsSuccess) ||
                            (config.WebhookTrigger == Webhook.ON_SUCCESS_OR_ERROR))
                        {
                            try
                            {
                                var(success, code) = await Webhook.SendRequest(config, result.IsSuccess);
                                LogMessage(managedSite.Id, $"Webhook invoked: Url: {config.WebhookUrl}, Success: {success}, StatusCode: {code}");
                            }
                            catch (Exception ex)
                            {
                                LogMessage(managedSite.Id, $"Webhook error: {ex.Message}");
                            }
                        }
                    }
                }
                return result;
            }));
        }