Home » C# » How to manage environment variables for a .Net Core web API in Azure

How to manage environment variables for a .Net Core web API in Azure

By Emily

When you’re building a .Net Core API, you run the code locally until it’s ready to deploy. The likelihood is you’re using a local database at this point too. However, once the API is deployed, it will need to connect to a different database – not the local one. Therefore the database connection string will need to change when it’s deployed, or in other words – moved to another environment. In other words you need to understand how to use Azure app configuration.

The problem is you can’t manually change this each time you deploy, so you need it to change automatically depending on its environment. Any value that does this is called an Environment Variable – because it will need to change to match the environment the code is in.

This post will explain how to manage these variables across different environments, with a focus on how to do this in an API hosted in Azure. So how DO you make sure that your environment variable changes as the application gets deployed in different environments, and where do you store environment variables in a Net Core Api?

Before you start – prepare environments

Before you start I’m presuming you already have a simple .Net Core 6 API. Also that you know how to read the values from the appsettings.json file. Once you’ve got to this stage you’re ready to start preparing your application for different environments.

Define & prepare the deployment environments

For the purposes of this post I’m going to presume that you’re hosting your .Net Core API in Azure. I’m also going to presume that you have 3 environments named as follows:

  • Development
  • Test
  • Production

I’m not going to cover how to configure those environments in Azure in this post, as it will vary depending on all sorts of factors. I’ll just explain how to set the environment names in Azure, manage the environment variables in your code, and how to set the variable values in each environment.

Set environment names in Azure

You may have already wondered…. how does the code know which environment it’s in once it’s been deployed? Good question. Although I am not going to cover Azure set up in this post, you do need to at least have an understanding of how and where the environments are named.

At the beginning of this post I discussed the fact that for this example we would have three environments – Development, Test, and Production.

Azure App Service Configuration

There is one App Service per environment in Azure. In each App Service, there’s a Configuration section. The name of the environment is specified in the ASPNETCORE_ENVIRONMENT property in this Configuration section – as either ‘Development‘, ‘Test‘, or ‘Production‘ for this example, but ultimately you can call your environments whatever you like.

NOTE -> Other environment variables can be stored here too, as long as they are named the same across each App Service. However, you can also use an Azure App Config instance to store your environment variables. I prefer this as you have one variable defined for each environment, so everything is in one place.


Define and store the environment variables in appsettings.json

In this example we’ll consider Net Core API which you have running locally, with an appsettings.json file which contains references to a few application variables:

  1. TestValue – a string which needs to contain a different value in each environment
  2. ShowDetails – a boolean value which will be used to control the visibility of an element in each environment
  3. AllowedHosts – a list of strings, in this case urls, which will be used for CORS configuration between the backend and the front end
  4. ConnectionStrings – this sections name is defined by Microsoft and must be named exactly this. You can store several different connection values in here. We are only going to store one.
  5. Logging – another section defined by Microsoft, used to specify the logging level in each environment

Your file will look like this:

{
  "TestValue": "General environment file", 
  "ShowDetails": true,
  "AllowedHosts": [
      "http://localhost:3000",
      "https://deployed-website-here.com"
    ],
  "ConnectionStrings": {
    	"Database": "your-connection-string-here"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

This will serve as your default file. If you deploy your application with only this file, then the values in here will be used in every environment. Which is fine if you want to connect to the same database in Local, Development, and Production, but not if you want them to change in each environment!

Understand the configuration priority in .Net Core

.Net has a list of priorities when it’s looking for your configuration values. It checks a list of different places and if it can’t find what it’s looking for in the first one, it will look for the property in the next item in the list, and will keep going until it finds a match.

As defined by Microsoft here, there are 8, yes 8, different places that environment variables can come from. In summary, the order is shown in the image below. However, as long as we aren’t going to use 1, 2 or 3, 6,7 or 8, then we can comfortably use only the appsettings.json files, highlighted here.

.Net Core configuration priority in Azure

With that in mind we’ll be using 4 & 5. Any value that is the same across environments will stay in the default appsettings.json file. Any value that needs to be different in each environment will be moved into an environment specific version of the file, as show in the next section.

Create an appsettings.json file for each environment

So any value that will be the same across every environment will stay in the default appsettings.json file. In this example there are two properties which are going to need to change in each environment – AllowedHosts, and Database from the ConnectionStrings section.

Now we need to duplicate the default file and create a version for every environment. Each of these files will only contain references to the variables which need to be specific to that environment. We’ll end up with an appsettings file structure like this:

- appsettings
-- appsettings.development.json
-- appsettings.test.json
-- appsettings.production.json

The appsettings.Development.json file

The Development environment specific file will look like this:

{
  "AllowedHosts": [
      "https://dev.deployed-website-here.com"
    ],
  "ConnectionStrings": {
    	"Database": "DEV-database-connection-string"
  }
}

The appsettings.Test.json file

And in the Test environment we’d have the same file but the values will be different:

{
  "AllowedHosts": [
      "https://test.deployed-website-here.com"
    ],
  "ConnectionStrings": {
    	"Database": "TEST-database-connection-string"
  }
}

The appsettings.Production.json file

And in the Prod environment we’d have the same file but the values will be different again:

{
  "AllowedHosts": [
      "https://deployed-website-here.com"
    ],
  "ConnectionStrings": {
    	"Database": "PROD-database-connection-string"
  }
}

Wrapping up

Now you deploy your code, and make sure your pipelines are set up correctly. That means the pipeline which deploys to the Development environment must specifiy the environment is Development, and the Test pipeline must specify the environment is Test. And the same in Prod. This will ensure the correct appsettings file is used in each environment.

Security and the database connection string

I’ve used the example of a database connection string in here, because it’s an easy to understand example of how certain values may change between environments. However, it’s not really secure to store your database connection string in an appsettings.json file, so please don’t do this in a deployed application!

But then where should you store a database connection string? In an Azure App Config instance.

Use an Azure App Config Instance to store environment variables

An Azure App Config instance is a place to centrally manage application settings. It differs slightly from using the Configuration section of an App Service. In an App Config instance you can view all of your environment variables in one place. In an App Service Configuration section you have to move from one to another to see the variables for that environment.

Using an Azure App Config instance you can store a named variable and then create several versions of it, each with a label. The label must match an environment name, then it will be used for the matching environment.

So you can store one value called DatabaseConnection, with 3 versions labelled Development, Test and Production.

Connect to an Azure AppConfig

You have to connect to this config instance, and that’s the connection string I tend to keep in my appsettings file. So the updated version of our default appSettings.json file would be:

{
  "TestValue": "General environment file", 
  "ShowDetails": true,
  "AllowedHosts": [
      "http://localhost:3000",
      "https://deployed-website-here.com"
    ],
  "ConnectionStrings": {
    	"AppConfig": "Endpoint=https://your-azure-url.azconfig.io;Id=your-value-here"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

In your C# code, you need to connect to the Azure AppConfig to get values from it:

//Extension method on IConfigurationBuilder
public static IConfigurationBuilder ConfigureAzureAppConfiguration(this IConfigurationBuilder configBuilder, HostBuilderContext context)
        {
	string connection = settings.GetConnectionString("AppConfig");

	configBuilder.AddAzureAppConfiguration(options =>
            options
                .Connect(connection)
                // Load configuration values with no label
                .Select(KeyFilter.Any, LabelFilter.Null)
                // Override with any configuration values specific to current hosting env
                .Select(KeyFilter.Any, context.HostingEnvironment.EnvironmentName)

}

                                       
//In program.cs
// CONFIGURATION - Get values from the Azure App config instance
builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    ConfigureAzureAppConfiguration(hostingContext);
});

Summary

In summary, managing environment variables isn’t complicated once you understand the principles behind it, but there are a few different areas to understand, and they all have to work together in your application.

As always there are several different ways you could do this, and I have provided you with a working example of one way of managing environment variables in a .Net Core API which is hosted in Azure.