Azure 中部署槽的 Web 配置更改

Web Config changes for deployment slots in Azure

我在我的 Azure 网站上创建了 3 个部署槽。

  1. 开发测试
  2. 质量检查
  3. 分期

对于每个插槽,我需要自定义 web.config 文件。这些更改贯穿整个 web.config 文件,而不仅仅是 AppSettings 和 ConnectionString 部分,例如 Web 服务端点、smtp 详细信息、日志记录详细信息、调试详细信息等...

Azure 似乎只支持更改 ConnectionString 和 AppSettings。

如何配置这些不同的插槽来更新 web.config 的其余部分?

最终结果是,我想不断地集成到开发测试中,然后将其交换到 QA、暂存中,最后进入生产。我尝试过发布管理,但这似乎只支持 Azure VM 部署。我是 powershell 的菜鸟,我相信这可能是答案。

如有任何帮助,我们将不胜感激。

====== 决议 ======

所以,我想我会为正在寻找方法的其他任何人提供反馈。基本上我创建了一个 WebConfig.cs class 如下:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(WebConfig), "PopulateFromAppSettings")]
namespace Application.Presentation.Web
{
    using System;
    using System.Configuration;
    using System.Xml;

    public static class WebConfig
    {
        private static XmlDocument webConfig;

        private static bool docChanged = false;

        public static bool isConfigured = false;

        static WebConfig()
        {
            if (isConfigured)
            {
                return;
            }

            webConfig = new XmlDocument();
            webConfig.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
        }

        public static void PopulateFromAppSettings()
        {
            if (isConfigured)
            {
                return;
            }

            // system.web
            SetAttributeValue("/configuration/system.web/compilation", "debug", ConfigurationManager.AppSettings["compilationDebug"]);
            SetAttributeValue("/configuration/system.web/customErrors", "mode", ConfigurationManager.AppSettings["customErrorsMode"]);

            // log4net
            SetAttributeValue("/configuration/log4net/appender/param[@name='ConnectionString']", "value", ConfigurationManager.AppSettings["azureAppenderConnectionString"]);
            SetAttributeValue("/configuration/log4net/appender/param[@name='ContainerName']", "value", ConfigurationManager.AppSettings["azureAppenderConnectionString"]);
            SetAttributeValue("/configuration/log4net/appender/param[@name='DirectoryName']", "value", ConfigurationManager.AppSettings["azureAppenderConnectionString"]);

            // system.identityModel
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/issuerNameRegistry/trustedIssuers/add", "name", ConfigurationManager.AppSettings["issuerNameRegistryTrustedIssuers"]);
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/issuerNameRegistry/trustedIssuers/add", "thumbprint", ConfigurationManager.AppSettings["trustedIssuersThumbprint"]);
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/audienceUris/add", "value", ConfigurationManager.AppSettings["audienceUriValue"]);
            SetAttributeValue("/configuration/system.identityModel.services/federationConfiguration/wsFederation", "issuer", ConfigurationManager.AppSettings["wsFederationIssuer"]);
            SetAttributeValue("/configuration/system.identityModel.services/federationConfiguration/wsFederation", "realm", ConfigurationManager.AppSettings["wsFederationRealm"]);
            SetAttributeValue("/configuration/system.identityModel.services/federationConfiguration/wsFederation", "reply", ConfigurationManager.AppSettings["wsFederationReply"]);
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/certificateValidation", "certificateValidationMode", ConfigurationManager.AppSettings["certificateValidationMode"]);
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/certificateValidation", "revocationMode", ConfigurationManager.AppSettings["certificateRevocationMode"]);
            SetAttributeValue("/configuration/system.identityModel/identityConfiguration/certificateValidation", "trustedStoreLocation", ConfigurationManager.AppSettings["certificateTrustedStoreLocation"]);

            // system.serviceModel
            SetAttributeValue("/configuration/system.serviceModel/client/endpoint", "address", ConfigurationManager.AppSettings["endpointAddress"]);
            SetAttributeValue("/configuration/system.serviceModel/client/endpoint/identity/servicePrincipalName", "value", ConfigurationManager.AppSettings["servicePrincipalName"]);

            if (docChanged)
            {
                webConfig.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
            }

            isConfigured = true;
        }

        private static void SetAttributeValue(string xpath, string attributeName, string value)
        {
            var selectSingleNode = webConfig.SelectSingleNode(xpath);
            if (selectSingleNode != null && selectSingleNode.Attributes != null)
            {
                if (selectSingleNode.Attributes[attributeName].Value != value)
                {
                    selectSingleNode.Attributes[attributeName].Value = value;
                    docChanged = true;
                }
            }
            else
            {
                throw new Exception(string.Format("Config attribute '{0}' was not found in config at path '{1}'", attributeName, xpath));
            }
        }
    }
}

这段代码是导致该方法在启动时 运行 的原因。

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(WebConfig), "PopulateFromAppSettings")]

我在 Web.Config->AppSettings 中创建了所有变量,然后在 Azure -> Application Settings 下创建它们。

除了一个问题外,它似乎运行良好。更新代码仅在网站首次 运行 时执行。这意味着第一次执行时,设置都是错误的。当我重新加载网站时,新设置生效。我认为这是一个很小的代价。如果我想出更好的方法,我会 post 回来。

PowerShell 并不是这里的答案,PowerShell 只是显示 Azure 应用服务平台功能的一个客户端。 web.config 从部署槽的角度来看被认为是内容,因此它不是交换不变的。要考虑的一种选择是将需要自定义的设置显示为应用程序设置(环境变量)并将它们填充到所有插槽中。在运行时,您可以检测代码是哪个插槽 运行 in/which 设置应该通过读取 Azure 应用服务填充 WEBSITE_SLOT_NAME 应用设置来使用。

Visual Studio 有一个名为 slow cheetah 的扩展,它允许您使用 XDT 对 Web.config 进行转换,这将允许您转换 [=15] 的任何部分=] 文件.

唯一的问题是此解决方案最适合与 Web Deploy 一起使用,因为转换是在构建时完成的,而 Azure Web App 上不提供用于慢速猎豹的位。