Azure 中部署槽的 Web 配置更改
Web Config changes for deployment slots in Azure
我在我的 Azure 网站上创建了 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 上不提供用于慢速猎豹的位。
我在我的 Azure 网站上创建了 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 上不提供用于慢速猎豹的位。