.NET Web API - Environment Variables

.NET Web API - Environment Variables

Creating and Reading ENVs

Introduction

The first article in this series was the default WeatherForecast model that is created by default in a .NET Web API project. We'll build off Article 1 to start reading in values from environment variables and appsettings.json files.

The code for this article can be found in the following GitHub Repo - Article 2. The repository will contain a dedicated directory for each article in the series.

Environment Variables

Environment variables are used for specifying external configurations or settings such as credentials, secrets, service configs, and any important stuff we need to remember. Not just are they important but they can change from time to time, so they need to be in a location for quick access to change and be available for any resource or application to pick up these changes.

First, let's review how we set environment variables on macOS or similar *nix environments. Read my short article on creating Environment Variables as a refresher.

Now let's review how to read those environment variables in a .NET Web API project.

Read Runtime Environment in .NET

One of the primary reasons to read an environment variable is to identify the correct environment your application/ service is running in. Visual Studio, on both PC and Mac, has a built-in Environment Variable Configuration feature built into the IDE.

We can set any value we'd like to use in our application by using the .NET Web API project's properties. Right-click the main project in the Visual Studio Solution Explorer and select Properties. In the Properties window, Go to the Run > Configurations > Default section. You'll see the Environment Variables table.

The example project we built in Article-1 is setting the ASPNETCORE_ENVIRONMENT variable to the value Development. The ASPNETCORE_ENVIRONMENT is a reserved word for .NET to automatically assign it to the application's Hosting Environment. You can read further detail about .NET Runtime Environments here.

Back to the code: the Program.cs file is using the ASPNETCORE_ENVIRONMENT variable to determine if the environment is set to Development. The if statement on line 38 will determine if the Swagger (OpenAPI) Documentation should be displayed. In the screenshot below I'm running the project and set a breakpoint on line 19. I'm hovering over the app.Environment variable. As you can see .NET read in the value and the EnvironmentName is set to Development.

Read Environment Variables in .NET

Now let's review custom environment variables, I'm going to create a new env variable VS_ENV in the Visual Studio properties window and one env variable MACOS_ENV in macOS (.zshrc file). If you are using bash, it would be created in your .bashrc file. Below are screenshots of the newly created env variables.

⚠️ WARNING! ⚠️

Visual Studio won't read in your MACOS (export env variables) by default. In order to have Visual Studio read in OS env variables you need to start Visual Studio from your terminal. You can't double click the icon. Shut down your current instance of Visual Studio and start the application from your terminal.

To start Visual Studio from your terminal use the following command in your terminal:

open -n "/Applications/Visual Studio.app"

Now back to work, add the following code to your Program.cs file to read our new env variables values.

// Taken from Program.cs
...

var app = builder.Build();

var vs_env = Environment.GetEnvironmentVariable("VS_ENV"));
var macos_env = Environment.GetEnvironmentVariable("MAC_ENV"));

var dict = Environment.GetEnvironmentVariables();
//Add Breakpoint on next line
foreach(var d in dict)
{
    Console.WriteLine(d);
}

...

Add a breakpoint stopping on the foreach statement and run the project. On start-up, hover over the dict variable and you'll see all environment variables that are read in from Visual Studio. You can hover over the vs_env and macos_env variables and see the values we created. Cool, we can read env variables now.

Starting Visual Studio on your Mac from the terminal is not something you want to do each time you start the app. For that reason, I stick to using environment variables from either the project's properties windows we used earlier or I use the project's appsettings.json files. These files can be set to use a runtime environment automatically. Based on the ASPNETCORE_ENVIRONMENT variable, .NET will load the matching appsettings.json file. Let's test it.

Read appsettings.json Variables in .NET

In .NET 6, the WebApplicationBuilder class exposes the Configuration properties to the application. The Configuration class makes it easy to read values from the appsettings.{RuntimeEnvironment}.json files.

Let's create a new file at the application root of the project named: appsettings.staging.json. Visual Studio will automatically nest app settings environment files under the main appsettings.json file.

The app setting files should have the following content:

// appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "MyENV": "ImFromDevelopment"
}



// appsettings.Staging.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "MyENV": "ImFromStaging"
}

In the Program.cs file we'll add two lines of code to read the configuration value MyENV. Below is what your Program.cs files should look like. Values from the appsettings.json files can be read in multiple ways. The first new line of code is using square bracket notation and the second new line of code is using the GetValue<Type> method to read the same value.

We will run this project two times. One time with the Visual Studio environment variable ASPNETCORE_ENVIRONMENT=Development and another run with the environment variable ASPNETCORE_ENVIRONMENT=Staging. The screenshot below shows the values read from each runtime environment.

There you have it, Visual Studio will load the correct appsettings.json file based on your ASPNETCORE_ENVIRONMENT variable automatically. This is great for local development and you can write to the app setting files via CI/CD builds and deploy builds with the correct variables based on the runtime environment.

Strongly Typed Settings - Options Pattern

Well now use the Options Pattern to provide the variable/config/setting values from the appsettings.json file into a class structure that can be utilized throughout your application.

// Updated appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "SampleClass": {
    "StringValue": "String Sample",
    "BooleanValue": true
  }
}

We'll create the SampleClass.cs as shown below.

// New file, SampleClass.cs

using System;
namespace article_2_environment_variables;

public class SampleClass
{
    public string StringValue { get; set; }
    public bool BooleanValue { get; set; }
}

Next, add a single line in the Program.cs file to bind the values from the JSON file to the class object. That's it.

// Program.cs File

...

builder.Services.Configure<SampleClass>(builder.Configuration.GetSection(nameof(SampleClass)));

...

You can now use Dependency Injection (DI) to use these values in your WeatherForecastController. Setup your class object in the controller's constructor as below. DI is a recommended approach instead of global/ static variables.

// WeatherForecastController.cs

...

private readonly SampleClass _sampleClass;

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptions<SampleClass> sampleClassOptions)
{
    _logger = logger;
    _sampleClass = sampleClassOptions.Value;
}

 [HttpGet]
 [Route("SampleClass")]
 public SampleClass GetSampleClass()
 {
    return _sampleClass;
 }

...

When running the project (set ASPNETCORE_ENVIRONMENT=Development you can now hit this new /SampleClass endpoint and see the values returned from your API.

End

The official .NET docs are great. Read further details here.