ConnectionString is null when deploying Azure Function - azure

I've been working with functions with Azure, I've built a very simple Http Function locally by following the example linked here, the only difference is I've defined a User table instead of a Todo table
Everything works as expected locally, I'm able to post and get.
However, when deploying the function and trying to make a POST request I see the following within the logs:
Executed 'User' (Failed, Id=5df9dffe-eedf-4b11-aa10-54fda00992b0, Duration=1ms)System.ArgumentNullException : Value cannot be null. (Parameter 'connectionString')
I've checked the SQL Server to ensure it's accessible by other Azure Services just encase that was causing a problem, but I can confirm it's set to allow.
I have found this question, I've gone through the steps and checked against mine and I can confirm my Function App configuration does have the AzureWebJobsStorage connection string.
I'm not 100% sure why this would be happening due to my lack of knowledge of functions at the moment, have anyone else experience this? if so how did you resolve it?
Update
After further testing, it seems the error is coming from my Startup class,
class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
string connectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
builder.Services.AddDbContext<ApplicationDbContext>(
options => SqlServerDbContextOptionsExtensions.UseSqlServer(options, connectionString));
}
}
Upon deployment, connectionString variable is null.... not sure why though.

Yes, you can not get it because you didn't set it in the configuration settings.

If you want to use the Connection Strings section.
Add the "ConnectionStrings":{} section to your local.settings.json file then add your connection string
{
...
"ConnectionStrings": {
"MyConnectionString": ""
}
}
Then you need to set the connection string in the Settings section of the Function App in the Azure Portal.
The scroll down to the Connection Section
And add a new connection string. Make sure it has the same name as you connection in the local.settings.json file.

Your question isn't 100% clear if this is happening locally (as you refer to local.settings.json) or when deploying. If this occurs when deploying, changing your local.settings.json file will not help, unfortunately.
You will need to add the Application Setting within the Azure Portal (located under Settings -> Configuration -> Application Settings -> New application setting).
You will need to save the application setting, and then restart the Azure Function instance for the changes to reflect.
Check out https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal

Related

Access Azure App Service ConnectionString from ASP.NET Core

I've got an azure app service that I've set up like this:
But when I call IConfiguration.GetConnectionString("db") I get null back.
I've read articles like this https://mderriey.com/2018/08/21/azure-app-service-connection-strings-and-asp-net-core/ which say "it just works", but they're all several years old. I assume something's changed, but what?
Enumerating over all settings in my IConfiguration object I've got no connection strings. I do in development, where my appsettings.development.json has a connectionStrings: { db: "" } defined.
I can see and read the ENV variable: POSTGRESQLCONNSTR_db from within code, and it's value is correct (what I've set via the Azure portal).
Should I expect to be able to do IConfiguration.GetConnectionString("db")? Or am I expected to switch between reading env variables in prod vs dev.
Do I need to include some nuget package to make IConfiguration work under Azure with these ENV variables and their mad prefixes?
My startup.cs basically looks like:
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
Nothing else in there of interest to this question.
The POSTGRESQLCONNSTR_ prefix isn't supported by the environment variables configuration provider. The docs shows this, in an indirect fashion, where it states that the following prefixes are supported:
CUSTOMCONNSTR_
MYSQLCONNSTR_
SQLAZURECONNSTR_
SQLCONNSTR_
It's also apparent in the source code for the provider.
There are a couple of options for working around this:
Change the Type to Custom in the Connection strings section of the Azure portal.
Change to an Application setting of ConectionStrings:db in the Azure portal.
This is being tracked on GitHub: https://github.com/dotnet/runtime/issues/36123.
I got confused as well, so here it is:
You have two options to specify Connection String locally:
launchSettings.json (environmentVariables section)
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"SQLAZURECONNSTR_SomeConnectionString": "DefaultEndpointsProtocol=blah"
}
appSettings.json
"ConnectionStrings": {
"SomeConnectionString": "DefaultEndpointsProtocol=blah"
}
Having either way will allow you to get the connection string setting by calling:
IConfiguration.GetConnectionString("SomeConnectionString")
Function call above will also work when deployed to Azure, as it is using EnvironmentVariables configuration provider to read settings.
Instead of getting the config from the interface
IConfiguration.GetConnectionString("db")
try to get it from
Configuration.GetConnectionString("db")
And in production you have an empty string in production.appsetting.json and add the value in azure(appservice) configuration directly under connectionstrings(this will override the json setting file). And no nugets are needed for reading from appsettings

App Settings not being observed by Core WebJob

I have a Core WebJob deployed into an Azure Web App. I'm using WebJobs version 3.0.6.
I've noticed that changes to Connection Strings and App Settings (added via the Azure web UI) are not being picked up immediately by the WebJob code.
This seems to correlate with the same Connection Strings and App Settings not being displayed on the app's KUDU env page straight away (although I acknowledge this may be a red herring and could be some KUDU caching thing which I'm unaware of).
I've deployed a few non-Core WebJobs in the past and have not come across this issue so wonder if it's Core related? Although I can't see how that might affect configs showing up KUDU though.
I was having this issue the other day (where the configs were not getting picked up by the WebJob or shown in KUDU) and was getting nowhere, so left it. When I checked back the following day, the configs were now correctly showing in KUDU and being picked up by the WebJob. So I'd like to know what has happened in the meantime which means the configs are now being picked up as expected.
I've tried re-starting the WebJob and re-starting the app after making config changes but neither seem to have an effect.
It's worth also noting that I'm not loading appSettings.json during the program setup. That being said, the connection string being loaded was consistenly the connection string from that file i.e. my local machine SQL Server/DB. My understanding was always that the anything in the Azure web UI would override any equivalent settings from config files. This post from David Ebbo indicates that by calling AddEnvironmentVariables() during the setup will cause the Azure configs to be observed, but that doesn't seem to be the case here. Has this changed or is it loading the configs from this file by convention because it can't see the stuff from Azure?
Here's my WebJob Program code:
public static void Main(string[] args)
{
var host = new HostBuilder()
.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables();
})
.ConfigureWebJobs(webJobConfiguration =>
{
webJobConfiguration.AddTimers();
webJobConfiguration.AddAzureStorageCoreServices();
}
)
.ConfigureServices((context, services) =>
{
var connectionString = context.Configuration.GetConnectionString("MyConnectionStringKey");
services.AddDbContext<DatabaseContext>(options =>
options
.UseLazyLoadingProxies()
.UseSqlServer(connectionString)
);
// Add other services
})
.Build();
using(host)
{
host.Run();
}
}
So my questions are:
How quickly should configs added/updated via the Azure web UI be displayed in KUDU?
Is the fact they're not showing in KUDU related to my Core WebJob also not seeing the updated configs?
Is appSettings.json getting loaded even though I'm not calling .AddJsonFile("appSettings.json")?
What can I do to force the new configs added via Azure to be available to my WebJob immediately?
The order in which configuration sources are specified is important, as this establishes the precedence with which settings will be applied if they exist in multiple locations. In the example below, if the same setting exists in both appsettings.json and in an environment variable, the setting from the environment variable will be the one that is used. The last configuration source specified “wins” if a setting exists in more than one location. The ASP.NET team recommends specifying environment variables last, so that the environment where your app is running can override anything set in deployed configuration files.
You can refer here for more details on Azure App Services Application Settings and Connection Strings in ASP.NET Core

Configure Azure Web Sites/Jobs app settings when developing locally

As described in this article: https://azure.microsoft.com/en-us/blog/windows-azure-web-sites-how-application-strings-and-connection-strings-work/, Azure Web Apps/Web Sites/Web Jobs can take their configuration settings (appSettings, connectionString) from environment variables instead of app.config/web.config.
For example, if an environment variable named "APPSETTING_appSettingKey" exists, it will override the following setting from app.config/web.config:
<appSettings>
<add key="appSettingKey" value="defaultValue" />
</appSettings>
This works fine once the application is deployed in Azure, but I would like to use the same method when testing locally.
I tried to emulate this in a local command line:
> set APPSETTING_appSettingKey=overridedValue
> MyWebJob.exe
The web job accesses this setting using:
ConfigurationManager.AppSettings["appSettingKey"]
When running in Azure, it reads the value "overridedValue" as expected, but locally it reads the value "defaultValue" from the app.config file.
Should I expect this to work, or is this implemented only under an Azure environment?
I could obviously create an abstraction over ConfigurationManager that emulates this, but this wouldn't work when calling code that needs a connection string name instead of a connection string value. Also, I want to use the same method regardless of the environment to simplify management of settings.
There are 3 reasons why I need this:
1) I don't like the idea of deploying to production a web.config file that references connection strings, etc for a developement environment, because there's a risk of an error that would cause the development settings (in web.config) to be used in production (production web app connecting to development database, etc), for example if an environment variable is named incorrectly (after renaming the setting in web.config but forgetting to rename it in environment variables)
2) I'm trying to setup development environments where each developer has his own isolated cloud resources (storage account, databases,...). Currently, everyone has to manually edit his .config files to reference the correct resources, and be careful when checking-in or merging changes to these files.
3) A solution can have multiple projects that need to duplicate the same settings (main web app, web jobs, integration test projects,...). This causes a lot of work to ensure updated settings are replicated across all files.
This would be simplified if there was an environment-independent .config file without any actual configuration, each developer would configure a set of environment variables once and be able to use them for all parts of a solution.
Yes, this special transformation of environment variables into config values is done via a component that is specific to Azure WebApps and won't be in play locally.
Generally people are fine with the local behavior this produces - locally you are reading from config settings as usual, but in Azure you're reading from secure settings that were configured via the App Settings portal blade (so these settings aren't in your source code).
You could write an abstraction over this if you wish, E.g. the WebJobs SDK actually does this internally (code here).
When I am developing locally and want to consistantly use Environment.GetEnvironmentVariable. In my static class Main I have the following code:
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
Environment.SetEnvironmentVariable("UseDevelopmentSettings", "true");
}
Then in my static class Functions I add a static constructor and in there I call the static method below:
static void AddAppSettingsToEnvironmentVariables()
{
String useDevelopmentSettings = Environment.GetEnvironmentVariable("UseDevelopmentSettings"); ;
if (!(String.IsNullOrEmpty(useDevelopmentSettings)))
{
foreach (String key in ConfigurationManager.AppSettings.AllKeys)
{
Environment.SetEnvironmentVariable(key, ConfigurationManager.AppSettings[key]);
}
}
}
The code is small enough that I can simply comment it out before I test in Azure.
If you want to test the application with the value that will be used in Azure portal AppSettings/Connection String. I would recommend use HostingEnvironment.IsDevelopmentEnvironment. To ensure it will work, please change the <compilation debug="true" targetFramework="4.5.2" /> to <compilation debug="false" targetFramework="4.5.2" />. set the value with the same value in Azure portal if (HostingEnvironment.IsDevelopmentEnvironment == false). I have try with a simple project, hope it helps:
public ActionResult Index()
{
if (HostingEnvironment.IsDevelopmentEnvironment == true)
{
ViewBag.Message = "Is development.";
}
else
{
ViewBag.Message = "Azure environment.";
}
return View();
}
Here is the result:

Azure WebJob configurations return null

I've got a webjob always returns null values when using CloudConfigurationManager.GetSetting
I have values in my web.config file, the app.config file (which get renamed to myapp.exe.config, as expected during deployment) and also in the App Settings configuration for the website/webapp in the portal.
It's a continuous webjob that is trying to execute some logic in the static main method to read some settings etc, however it crashes as the values aren't expected to be null and then gets stuck in a loop trying to start up.
Here's some snippets of code... it's not the actual code but you get the idea.
static void Main()
{
bool.Parse(CloudConfigurationManager.GetSetting("IsValueTrue"));
host.RunAndBlock();
}
The host.RunAndBlock(); method is never reached as the call for the value IsValueTrue from the app.settings returns null.
Are configuration settings not available during starting of a WebJob?
Instead of CloudConfigurationManager.GetSetting(), use ConfigurationManager. For example,
var someSetting = ConfigurationManager.AppSettings["IsValueTrue"];
The setting in the portal will take precedence over settings in your app.config and is also the recommended place to store your setting. The code sample above should get you going.

Can I set the Diagnostic connection string for Windows Azure WebRole from code?

We are hosting 3party sites in our webrole and to limit them access to the storage container I need to set the connection string from code instead of the connection string in serviceconfiguration?
Is this possible?
Based on answer i ran into a problem.
DiagnosticMonitorConfiguration dmConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();
DiagnosticMonitor.StartWithConnectionString(conn, dmConfig);
This resets the configuration to defaults and overrides the stuff that was deployed with the cloud service. I assume when using the StartWithConnectionString, you cant use the support they added in visual studio for setting these things.
Yes, I think you can. Do take a look at DiagnosticMonitor.StartWithConnectionString method. You would do something like this in your WebRole's OnStart() method:
DiagnosticMonitorConfiguration dmConfig = DiagnosticMonitor.GetDefaultInitialConfiguration();
DiagnosticMonitor.StartWithConnectionString("DefaultEndpointsProtocol=https;AccountName=accountname;AccountKey=accountkey", dmConfig);
return base.OnStart();
However I would not recommend hard coding the connection string in the code itself. Instead take it from some database.

Resources