Appsettings and Application Secrets in Azure Functions
No real world app without application-settings and -secrets. You will invariably need some settings defining stuff like feature flags or connection strings. Since function apps are so tightly coupled to Azure functions, there is really only two possible environments your function app can run in, your local computer and Azure cloud. This keeps the number of configuration options to a minimum.
Basically there is two places configuration settings are kept for Azure Functions apps:
- A file called
host.json
in the root of the project. This file is mainly responsible for keeping configuration settings that affects function behavior at runtime. This file lives in your source code. - Function App settings configured using the Azure portal. These settings are mainly responsible for configuring the environment the function app runs in, as well as application secrets like connection strings and more.
Besides this you need to be able to configure your local app environment when developing locally. This is done in a file called local.settings.json
. This file should be excluded from the git repo using .gitignore to avoid publishing application secrets to the source code repo, if you add it anyway you should know that the settings in this file will not have any effect when the function app runs in Azure.
The host.json file
The host.json
is the main configuration file in a functions app project, it’s used for configuring a lot of application settings like extension settings, health monitoring, logging, application insights and more.
Some of the more important settings, besides extension configurations, are:
Monitoring settings
Azure Application Insights (app insights) is the most convenient way to handle monitoring your function app, it gives you direct access to sophisticated analytics tools in the browser and makes it possible to set up alerts when something out of the ordinary happens.The hosts.json file is where you configure the settings that control application insights usage.
The file controls three aspects of application monitoring in app insights:
- Logging
- Execution data aggregation
- Telemetry data sampling
Of which logging and data sampling has important consequences for your setup.
Logging
Logging can be an important part of troubleshooting your application. If you attach your application to an app insights instance your logs will be picked up and available for querying and viewing here, if you do not logs will be picked up in the storage account and available for download here.
Logging levels can be segmented by Functions and runtime categories, runtime categories are Host.Results - request logs, Host.Aggregator - execution data like durations and counts and Host.Executor - runtime messages when executing functions. Logging can be specified hierarchically meaning you can set an overall logging level for Functions using "Function": "Error"
and use information level logging for a selected function by adding "Function.MyFunction": "Information"
.
A workable setting in production would look like this:
{
"logging": {
"logLevel": {
"default": "Information",
"Host.Results": "Error",
"Host.Aggregator": "Trace",
"Function": "Error"
}
}
}
This would result in the application logging only error results when a function executes, and only error messages from logging statements in code, while execution times and counts are all logged (Trace
is comparable to Verbose
in other frameworks). It is important that you user Trace
on Host.Aggregator
since setting level at Warning
or above will exclude these data, and you wont be able to monitor and alert on execution times.
In QA and development you will probably want more verbose logging levels, see the Appsettings below for information on how to set other levels in a other Azure environments, and local settings for information about how to control logging locally.
Execution Data Aggregation
Using the Azure portal you can find data for execution metrics like average response time and function execution count for a function app. In order to calculate these metrics, execution data is by default aggregated and reported in intervals of 30 seconds or 1000 executions. You can change these defaults in the aggregator
property of the host.json file.
{
"aggregator": {
"batchSize": 1000,
"flushTimeout": "00:00:30"
}
}
Telemetry Data Sampling
If your function app runs up a large number of executions, application insights will start sampling the data reported to application insights. This is totally ok - even preferred - when your application is running fine, since not sampling these data can incur significant costs. Per default Auzure Functions setups enables Adaptive sampling, a sampling method that reduces the number of actually logged data, when the number of telemetry items per second exceeds a configurable number. The default number of telemetry items to exceed is set using the maxTelemetryItemsPerSecond
setting, and the default value is 20. Adaptive sampling functions in a way so that telemetry items with the same OperationId are always logged or dropped together ensuring traceability across different layers of your application - something to keep in mind if you log custom events from your code - and Application Insights renormalize metric counts to keep effects on statistics to a minimum. In other words your statistics will reflect real numbers even though your the data is sampled.
There is no really pressing reasons to specialize your telemetry settings from the outset. If you find you need to adjust the settings they are documented here.
If you want to make sure you gather all your telemetry data, for instance to make sure you can find telemetry for that elusive periodic errors, you can disable data sampling by adding this to your hosts.json file.
{
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": false
}
}
}
}
Appsettings
App settings are global configuration options that affects all functions for a function app - documentation for all settings is found here. I’ll highlight the important ones below.
Settings can be configured using the Azure portal or your preferred command line interface for Azure (Azure CLI or Azure Powershell).
- APPINSIGHTS_INSTRUMENTATIONKEY
- Use this setting to connection you function app to the right Application Insights instance in Azure
- AZURE_FUNCTIONS_ENVIRONMENT
- Define the environment name for the function app, generally the runtime assumes a default value of `Production` if nothing is specified, and `Development` when running locally. Use this if you have a staging environment for your function app and your code needs to know about it, by setting the value to
staging
. - AzureWebJobsStorage
- If you have any functions in your function app that are not http-triggered (timer triggered), you need a storage account for the runtime to store information. You set the value of this setting to a connection string for a storage account. The storage account need to a general-purpos account that support blobs, queues and tables.
- FUNCTION_APP_EDIT_MODE
- If you've set up a CI/CD pipeline for your function app, as I showed on my post [about deploying functions app](/posts/2020/08/deploy-azure-functions-app/), you don't want somebody unaware about the code repo and CI/CD setup to be able to log in an change the function code. Set the value of this setting to
readonly
to prevent this from happening. - FUNCTIONS_EXTENSION_VERSION
- This setting specifies which version of the Functions runtime that hosts your function app. You can specify that you want to use the lates version available for any major version by prepending the version number with a ~, so for v3 use the setting value `~3` and your function app will always run in the latest minor version in the cloud. If you need to pin the app to a specific version you can do so by specifying a full version number like `3.0.12345`
- FUNCTIONS_WORKER_RUNTIME
- This setting specifies the language of your code, so the runtime knows which language worker to use. Use
dotnet
for c# based code andnode
for Javascript and Typescript. Other languages are available - WEBSITE_TIME_ZONE
- This setting specifies which time zone your app runs in, use it to avoid surprises when using CRON expressions to configure timed function runs. I live in Denmark so I would use
Romance Standard Time
for the value of this setting. You can get a list of timezone names for windows by running this command in your preferred shell:tzutil.exe /L
Local settings
App settings for local use is set in a file named local.settings.json
, which should be placed at the root of your functions project. As a minimum you should set the function app to run without encryption (no need for local certs), the language of your app, and if you’re creating timed functions a storage location:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME: "dotnet"
}
}
The “Values” section of the file, corresponds to the app settings of an online function app.
Overriding host.json settings
If you need to override settings from host.json either in a Azure function app or locally, you can do so by using a value in app settings prepended with “AzureFunctionsJobHost__”, and then converting the json path to a path with __ (double underscore) instead of period-characters. So if you want to override a setting in host.json placed like this
{
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": false
}
}
}
}
You should create a setting called AzureFunctionsJobHost__logging__applicationInsights__samplingSettings__isEnabled
in your app settings at set the value to false
. This works in both app settings in an online app, and in the local.settings.json file.
Handling application secrets
Generally all connection strings and the like that are used by built in bindings such as bindings to CosmosDB, Azure Service Bus or Storage Accounts, are stored in app settings. This is neat since it means that host.json - the file the configures your app - at the outset doesn’t store application secrets. You would then put connection strings in the app settings in Azure and in local.settings.json, which shouldn’t be added to your source code repo. This means that application secret for your app, can be configured by people that contributor access to your production environment, and this might be enough security in some organizations.
If you want to up the security further you can store your application secrets in Azure Key Vaults, and then set up your function app to read the secret from the key vault. What they describe here is basically a three step procedure:
- Create an application identity for your function app
- Create an access policy in the key vault that lets the application identity read application secrets from the key vault
- Set the value of the application setting using a special syntax referencing the key vault `@Microsoft.KeyVault({referencestring}).
If you for some reason find yourself in a situation that necessitates putting an application secret in your host.json file, I recommend you set the value in the host.json file to a dummy value and then use the method mentioned above to override the host.json value with an application setting.
This blogpost is a part of a series of posts about Azure Function Apps in production. These are the posts and planned posts in the series so far.
- Creating a .NET Core based Azure Function App and runnning it locally
- Build / Deploy pipeline for Azure Functions App
- Handling configuration and application secrets
- Binding data in Azure Function Apps
- Debugging your Function App
- Versioning your Function App API
- Documenting your Function App API