5

I'm trying to follow Best practices for private config data and connection strings in configuration in ASP.NET and Azure and Best practices for deploying passwords and other sensitive data to ASP.NET and Azure App Service.

Steps I took:

I have an ASP.NET 4.6 Web App with a regular web.config file. I created two files for my secrets: Web.Secrets.AppSettings.config and Web.Secrets.ConnectionString.config, put corresponding secrets into them according to the tutorial and modified the root web.config so it looks like that:

<configuration>
  <appSettings file="Web.Secrets.AppSettings.config">
  </appSettings>
  <connectionStrings configSource="Web.Secrets.ConnectionStrings.config">
  </connectionStrings>
  <!--...-->
</configuration>

Then I created a new Azure App Service in Azure Portal, opened it's Application Settings and added the secrets into the corresponding sections (App Settings and Connection Strings).

After that I deployed my Web App to this Azure App Service and right after that at the startup got yellow screen of death with the following message:

Server Error in '/' Application.

Configuration Error

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'.

Source Error:

An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine.

Source File: D:\home\site\wwwroot\web.config Line: 8

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1590.0

Of course Web.Secrets.ConnectionStrings.config and Web.Secrets.AppSettings.config are not copied and it's exactly what I need. The corresponding secrets should be taken from the environment variables.

There is a stack trace in HTML Source of the error page:

[ConfigurationErrorsException]: Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'. (D:\home\site\wwwroot\web.config line 8)
   at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)
   at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
   at System.Web.Configuration.HttpConfigurationSystem.GetApplicationSection(String sectionName)
   at System.Web.Configuration.HttpConfigurationSystem.GetSection(String sectionName)
   at System.Web.Configuration.HttpConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String configKey)
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_ConnectionStrings()
   at EnvSettings.SettingsProcessor.SetConnectionString(String name, String connString, String providerName)
   at EnvSettings.SettingsProcessor.Start()
[InvalidOperationException]: The pre-application start initialization method Start on type EnvSettings.SettingsProcessor threw an exception with the following error message: Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'. (D:\home\site\wwwroot\web.config line 8).
   at System.Web.Compilation.BuildManager.InvokePreStartInitMethodsCore(ICollection`1 methods, Func`1 setHostingEnvironmentCultures)
   at System.Web.Compilation.BuildManager.InvokePreStartInitMethods(ICollection`1 methods)
   at System.Web.Compilation.BuildManager.CallPreStartInitMethods(String preStartInitListPath, Boolean& isRefAssemblyLoaded)
   at System.Web.Compilation.BuildManager.ExecutePreAppStart()
   at System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException)
[HttpException]: The pre-application start initialization method Start on type EnvSettings.SettingsProc

What am I doing wrong? Or it's just a bug in Azure?

Important notes

  • The app fails right on the startup. I don't even touch anything related to config files anywhere.
  • If I delete the connection string from connection strings section in Application Settings of Azure App Service, the app starts fine. If I get it back, the app starts failing at the startup again. This is very odd! Just think about it! There IS connection string - fail, there is NO connection string - fine.
  • When I run the app locally and there is no Web.Secrets.ConnectionStrings.config file, the app runs just fine. The app fails only when being deployed at Azure App Service. Hence, the issue is Azure App Service specific.
  • It is being reproduced even with a plain empty ASP.NET Web Application project.
  • The part about appSettings works just fine, only the part with connectionStrings fails.

Default Azure App Service Environment Variables:

WEBSITE_NODE_DEFAULT_VERSION: 4.4.7

Possible workaround:

Config transformation could be used for removing the appropriate attribute in Web.config. For example, that's how Web.Release.config might look like:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings xdt:Transform="RemoveAttributes(configSource)"/>
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
  </system.web>
</configuration>
Deilan
  • 4,740
  • 3
  • 39
  • 52
  • Does it fail at the time you try to look up the connection string, or is it on startup before you execute any of your own code? – David Ebbo Nov 23 '16 at 15:15
  • Also, if you look at the HTML source of the error page, use there some kind of stack trace in there? – David Ebbo Nov 23 '16 at 15:18
  • @DavidEbbo, It fails right on the startup. I don't touch anything related to config files anywhere. I've added stack trace to the question from the error page HTML source. – Deilan Nov 24 '16 at 10:32
  • @DavidEbbo, when I run the app locally and there is no `Web.Secrets.ConnectionStrings.config` file, the app runs just fine. The app fails only when being deployed at Azure App Service. I've updated **important notes** section regarding to your questions. I think it's worth to take a look at it. – Deilan Nov 24 '16 at 10:54
  • see my Answer below. The issue is not specific to Azure. Please try the [trivial console repro](https://github.com/davidebbo-test/ConsoleAppWithMissingConfigSourceFile) I added to my Answer which demonstrates that. – David Ebbo Nov 24 '16 at 17:19
  • @DavidEbbo, thank you! Unfortunately, This example is not correct because it focuses different things, that out of scope for my case. 1) `ConfigurationManager` explicitly used in the demo. I don't have any invokations of `ConfigurationManager` in my demo, and the app fails nevertheless. 2) The deployed app fails if I add an Azure environment connection string variable. If I delete environment connection string variable, the app works fine. I will prepare an example app and a Azure App Service instance soon to demonstrate the issue to you. – Deilan Nov 24 '16 at 21:49
  • You don't need to prepare an example, as I know why that's happening. What I don't understand is why you'd have a connection string if you were never planning to access it. If you did, it would break at that time. So yes, you could see Azure makes it worse by breaking on start, but in the end, the app will be busted either way. – David Ebbo Nov 24 '16 at 21:58
  • @DavidEbbo, it fails, when there IS a connection string defined in Azure Application Settings Connection Strings! Like [that](https://learn.microsoft.com/en-us/azure/app-service-web/media/web-sites-hybrid-connection-get-started/set-sql-server-database-connection.png)! And I am accessing it in my app, but NOT at the startup. But the app fails at the startup and nothing ever happens after that. That's the point. – Deilan Nov 24 '16 at 22:01
  • Sure but how does that matter? It's going to fail either way when you later access it. So why Azure breaks it earlier, you'd still be busted either way. Bottom line: you can't have a config pointing to missing file, regardless of Azure. – David Ebbo Nov 24 '16 at 22:27
  • @DavidEbbo, I'm sorry, but I'm pretty sure you are leaving some details of the issue out of account. I've prepared **important section** to gather together notable points. The app should fail at the moment when configuration is being fetched and there is no appropriate setting in it. But the app should not fail if there is connection string defined in Azure App Service's application settings section. – Deilan Nov 28 '16 at 16:12
  • @DavidEbbo, I have prepared the demo for illustrating the issue. Take a look at the **[source code](https://github.com/Deilan/AzureAppFailsWhenConfigSourceAttributePresents)**; I threw away all the unneeded stuff. I published that app to two different azure app services: **[The first with connection string defined in app setting](http://azureappfailsdemo-withconnectionstringdefinedinappsettings.azurewebsites.net/)**, and **[the second without connection string](http://azureappfailsdemo-withoutconnectionstringdefinedinappsetting.azurewebsites.net/)**. The first should not ever fail. – Deilan Nov 28 '16 at 16:14
  • @DavidEbbo, The deployed code is the same on both App Services. The only difference between them is a connection string defined at Application Settings section of Azure App Service. Here are a couple of screenshots to illustrate: **[the first](http://imgh.us/WithConnectionString.png)**, **[the second](http://imgh.us/WithoutConnectionString.png)**. The first app's home page should never fail. But it fails. – Deilan Nov 28 '16 at 16:36
  • I think we are misunderstanding each other here. **The fact that it fails right on startup on Azure when you have an Azure connection string is understood** The question for you is what I asked above: "Sure but how does that matter? It's going to fail either way when you later access it. So why Azure breaks it earlier, you'd still be busted either way. Bottom line: you can't have a config pointing to missing file, regardless of Azure". Can you please respond to this point? Or if you're never planning to ask for this connection string at all, why have it in the first place? – David Ebbo Nov 28 '16 at 18:10
  • @DavidEbbo, 1) There is no sense failing the app when the setting being requested is explicitly defined. Lazy eval makes sense: look up nearly (env settings), and if nothing is found, try to look in files - and fail if file not found. 2) None of the referred articles mentions this issue in sections about publishing to Azure. 3) I have done all this two years before and it worked without cutting off the attributes via transformations. 4) The behavior is inconsistent: it fails if there is no external `connectionString` file, but does not fail if there is no external `appSettings` file? – Deilan Nov 28 '16 at 18:30
  • @DavidEbbo, of course there are usages of connection string in the app. I added it even in the demo app to make it clear. I am not gettting why are you asking that? Have I mentioned the opposite somewhere? – Deilan Nov 28 '16 at 18:34
  • And when you do end up using the connection string in your own code, does it not fail in the same way? – David Ebbo Nov 28 '16 at 19:53
  • @DavidEbbo, after putting this all together, some experiments with config files and further research I've encountered the [appropriate thread on SO, which discusses the topic about differences between AppSettings and ConnectionStrings "externalization" attributes](https://stackoverflow.com/questions/6940004/asp-net-web-config-configsource-vs-file-attributes). That was the last straw that convinced me that you are right. **Connection strings' `configSource` uses all-or-nothing approach**. Thanks for your hints and patience and sorry for some insistence here. :) – Deilan Nov 29 '16 at 11:59
  • It's a shame that 'file' can't be used on connecting strings. – David Ebbo Nov 30 '16 at 04:38
  • we work in a small team to share code. each user has different connection string in their local machine. when we commit changes it messes up connection string in other users web.config. we decided to put the connection string in a config file and added the file in ignore list. Everything was working fine until we decided to add connection string in azure portal page. it broke web application. But thanks for the workaround. now it works fine. – akd Jun 23 '17 at 09:35

1 Answers1

3

I looked into this, and my conclusion is that this is not an Azure issue, but is the way the config system behaves with a connection string configSource. Specifically, the behavior is that when you specify such directive, that file must be present, or any attempt to access connection strings blows up. e.g. outside of Azure, set up your web.config pointing to a missing configSource and run:

ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["foo"];

And it will blow up in the same way (Unable to open configSource file 'Web.Secrets.ConnectionStrings.config'.).

It's interesting, because with App Settings, it's able to simply ignore a file directive when the file is missing.

But none of that is Azure specific. It's just .NET framework config system behavior. I create a trivial Console app which demonstrates that: https://github.com/davidebbo-test/ConsoleAppWithMissingConfigSourceFile

You'll need to either yank the attribute (as you're showing), or deploy a dummy file to keep it happy.

David Ebbo
  • 42,443
  • 8
  • 103
  • 117