Configuration in ASP.NET Core

By Rick Anderson, Mark Michaelis, Steve Smith, Daniel Roth, and Luke Latham

The Configuration API provides a way to configure an ASP.NET Core web app based on a list of name-value pairs. Configuration is read at runtime from multiple sources. Name-value pairs can be grouped into a multi-level hierarchy.

There are configuration providers for:

  • File formats (INI, JSON, and XML).
  • Command-line arguments.
  • Environment variables.
  • In-memory .NET objects.
  • The unencrypted Secret Manager storage.
  • An encrypted user store, such as Azure Key Vault.
  • Custom providers (installed or created).

Each configuration value maps to a string key. There's built-in binding support to deserialize settings into a custom POCO object (a simple .NET class with properties).

The options pattern uses options classes to represent groups of related settings. For more information on using the options pattern, see the Options topic.

View or download sample code (how to download)

Examples provided in this topic rely upon:

These packages are included in the Microsoft.AspNetCore.App metapackage.

Examples provided in this topic rely upon:

These packages are included in the Microsoft.AspNetCore.All metapackage.

Examples provided in this topic rely upon:

JSON configuration

The following console app uses the JSON configuration provider:

using System;
using System.IO;
// Requires NuGet package
// Microsoft.Extensions.Configuration.Json
using Microsoft.Extensions.Configuration;

public class Program
{
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args = null)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        Configuration = builder.Build();

        Console.WriteLine($"option1 = {Configuration["Option1"]}");
        Console.WriteLine($"option2 = {Configuration["option2"]}");
        Console.WriteLine(
            $"suboption1 = {Configuration["subsection:suboption1"]}");
        Console.WriteLine();

        Console.WriteLine("Wizards:");
        Console.Write($"{Configuration["wizards:0:Name"]}, ");
        Console.WriteLine($"age {Configuration["wizards:0:Age"]}");
        Console.Write($"{Configuration["wizards:1:Name"]}, ");
        Console.WriteLine($"age {Configuration["wizards:1:Age"]}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

The app reads and displays the following configuration settings:

{
  "option1": "value1_from_json",
  "option2": 2,

  "subsection": {
    "suboption1": "subvalue1_from_json"
  },
  "wizards": [
    {
      "Name": "Gandalf",
      "Age": "1000"
    },
    {
      "Name": "Harry",
      "Age": "17"
    }
  ]
}

Configuration consists of a hierarchical list of name-value pairs in which the nodes are separated by a colon (:). To retrieve a value, access the Configuration indexer with the corresponding item's key:

Console.WriteLine(
    $"suboption1 = {Configuration["subsection:suboption1"]}");

To work with arrays in JSON-formatted configuration sources, use an array index as part of the colon-separated string. The following example gets the name of the first item in the preceding wizards array:

Console.Write($"{Configuration["wizards:0:Name"]}");
// Output: Gandalf

Name-value pairs written to the built-in Configuration providers are not persisted. However, a custom provider that saves values can be created. See custom configuration provider.

The preceding sample uses the configuration indexer to read values. To access configuration outside of Startup, use the options pattern. For more information, see the Options topic.

XML configuration

To work with arrays in XML-formatted configuration sources, provide a name index to each element. Use the index to access the values:

<wizards>
  <wizard name="Gandalf">
    <age>1000</age>
  </wizard>
  <wizard name="Harry">
    <age>17</age>
  </wizard>
</wizards>
Console.Write($"{Configuration["wizard:Harry:age"]}");
// Output: 17

Configuration by environment

It's typical to have different configuration settings for different environments, for example, development, testing, and production. The CreateDefaultBuilder extension method in an ASP.NET Core 2.x app (or using AddJsonFile and AddEnvironmentVariables directly in an ASP.NET Core 1.x app) adds configuration providers for reading JSON files and system configuration sources:

  • appsettings.json
  • appsettings.<EnvironmentName>.json
  • Environment variables

ASP.NET Core 1.x apps need to call AddJsonFile and AddEnvironmentVariables.

See AddJsonFile for an explanation of the parameters. reloadOnChange is only supported in ASP.NET Core 1.1 and later.

Configuration sources are read in the order that they're specified. In the preceding code, the environment variables are read last. Any configuration values set through the environment replace those set in the two previous providers.

Consider the following appsettings.Staging.json file:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "MyConfig": "My Config Value for staging."
}

In the following code, Configure reads the value of MyConfig:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    var myConfig = Configuration["MyConfig"];
    // use myConfig
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }

    if (env.IsProduction() || env.IsStaging())
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

The environment is typically set to Development, Staging, or Production. For more information, see Use multiple environments.

Configuration considerations:

  • IOptionsSnapshot can reload configuration data when it changes.
  • Configuration keys are not case-sensitive.
  • Never store passwords or other sensitive data in configuration provider code or in plain text configuration files. Don't use production secrets in development or test environments. Specify secrets outside of the project so that they can't be accidentally committed to a source code repository. Learn more about how to use multiple environments and managing safe storage of app secrets in development.
  • For hierarchical config values specified in environment variables, a colon (:) may not work on all platforms. Double underscore (__) is supported by all platforms.
  • When interacting with the configuration API, a colon (:) works on all platforms.

In-memory provider and binding to a POCO class

The following sample shows how to use the in-memory provider and bind to a class:

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

public class Program
{   
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args = null)
    {
        var dict = new Dictionary<string, string>
            {
                {"Profile:MachineName", "Rick"},
                {"App:MainWindow:Height", "11"},
                {"App:MainWindow:Width", "11"},
                {"App:MainWindow:Top", "11"},
                {"App:MainWindow:Left", "11"}
            };

        var builder = new ConfigurationBuilder();
        builder.AddInMemoryCollection(dict);

        Configuration = builder.Build();

        Console.WriteLine($"Hello {Configuration["Profile:MachineName"]}");

        var window = new MyWindow();
        // Bind requrires NuGet package
        // Microsoft.Extensions.Configuration.Binder
        Configuration.GetSection("App:MainWindow").Bind(window);
        Console.WriteLine($"Left {window.Left}");
        Console.WriteLine();

        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

public class MyWindow
{
    public int Height { get; set; }
    public int Width { get; set; }
    public int Top { get; set; }
    public int Left { get; set; }
}

Configuration values are returned as strings, but binding enables the construction of objects. Binding allows the retrieval of POCO objects or even entire object graphs.

GetValue

The following sample demonstrates the GetValue<T> extension method:

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;

public class Program
{   
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args = null)
    {
        var dict = new Dictionary<string, string>
            {
                {"Profile:MachineName", "Rick"},
                {"App:MainWindow:Height", "11"},
                {"App:MainWindow:Width", "11"},
                {"App:MainWindow:Top", "11"},
                {"App:MainWindow:Left", "11"}
            };

        var builder = new ConfigurationBuilder();
        builder.AddInMemoryCollection(dict);

        Configuration = builder.Build();

        Console.WriteLine($"Hello {Configuration["Profile:MachineName"]}");

        // Show GetValue overload and set the default value to 80
        // Requires NuGet package "Microsoft.Extensions.Configuration.Binder"
        var left = Configuration.GetValue<int>("App:MainWindow:Left", 80);
        Console.WriteLine($"Left {left}");

        var window = new MyWindow();
        Configuration.GetSection("App:MainWindow").Bind(window);
        Console.WriteLine($"Left {window.Left}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

public class MyWindow
{
    public int Height { get; set; }
    public int Width { get; set; }
    public int Top { get; set; }
    public int Left { get; set; }
}

The ConfigurationBinder's GetValue<T> method allows the specification of a default value (80 in the sample). GetValue<T> is for simple scenarios and doesn't bind to entire sections. GetValue<T> obtains scalar values from GetSection(key).Value converted to a specific type.

Bind to an object graph

Each object in a class can be recursively bound. Consider the following AppSettings class:

public class AppSettings
{
    public Window Window { get; set; }
    public Connection Connection { get; set; }
    public Profile Profile { get; set; }
}

public class Window
{
    public int Height { get; set; }
    public int Width { get; set; }
}

public class Connection
{
    public string Value { get; set; }
}

public class Profile
{
    public string Machine { get; set; }
}

The following sample binds to the AppSettings class:

using System;
using System.IO;
using Microsoft.Extensions.Configuration;

public class Program
{
    public static void Main(string[] args = null)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        var config = builder.Build();

        var appConfig = new AppSettings();
        config.GetSection("App").Bind(appConfig);

        Console.WriteLine($"Height {appConfig.Window.Height}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

ASP.NET Core 1.1 and higher can use Get<T>, which works with entire sections. Get<T> can be more convenient than using Bind. The following code shows how to use Get<T> with the preceding sample:

var appConfig = config.GetSection("App").Get<AppSettings>();

Using the following appsettings.json file:

{
  "App": {
    "Profile": {
      "Machine": "Rick"
    },
    "Connection": {
      "Value": "connectionstring"
    },
    "Window": {
      "Height": "11",
      "Width": "11"
    }
  }
}

The program displays Height 11.

The following code can be used to unit test the configuration:

[Fact]
public void CanBindObjectTree()
{
    var dict = new Dictionary<string, string>
        {
            {"App:Profile:Machine", "Rick"},
            {"App:Connection:Value", "connectionstring"},
            {"App:Window:Height", "11"},
            {"App:Window:Width", "11"}
        };
    var builder = new ConfigurationBuilder();
    builder.AddInMemoryCollection(dict);
    var config = builder.Build();

    var settings = new AppSettings();
    config.GetSection("App").Bind(settings);

    Assert.Equal("Rick", settings.Profile.Machine);
    Assert.Equal(11, settings.Window.Height);
    Assert.Equal(11, settings.Window.Width);
    Assert.Equal("connectionstring", settings.Connection.Value);
}

Create an Entity Framework custom provider

In this section, a basic configuration provider that reads name-value pairs from a database using EF is created.

Define a ConfigurationValue entity for storing configuration values in the database:

public class ConfigurationValue
{
    public string Id { get; set; }
    public string Value { get; set; }
}

Add a ConfigurationContext to store and access the configured values:

public class ConfigurationContext : DbContext
{
    public ConfigurationContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<ConfigurationValue> Values { get; set; }
}

Create a class that implements IConfigurationSource:

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace CustomConfigurationProvider
{
    public class EFConfigSource : IConfigurationSource
    {
        private readonly Action<DbContextOptionsBuilder> _optionsAction;

        public EFConfigSource(Action<DbContextOptionsBuilder> optionsAction)
        {
            _optionsAction = optionsAction;
        }

        public IConfigurationProvider Build(IConfigurationBuilder builder)
        {
            return new EFConfigProvider(_optionsAction);
        }
    }
}

Create the custom configuration provider by inheriting from ConfigurationProvider. The configuration provider initializes the database when it's empty:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace CustomConfigurationProvider
{
    public class EFConfigProvider : ConfigurationProvider
    {
        public EFConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
        {
            OptionsAction = optionsAction;
        }

        Action<DbContextOptionsBuilder> OptionsAction { get; }

        // Load config data from EF DB.
        public override void Load()
        {
            var builder = new DbContextOptionsBuilder<ConfigurationContext>();
            OptionsAction(builder);

            using (var dbContext = new ConfigurationContext(builder.Options))
            {
                dbContext.Database.EnsureCreated();
                Data = !dbContext.Values.Any()
                    ? CreateAndSaveDefaultValues(dbContext)
                    : dbContext.Values.ToDictionary(c => c.Id, c => c.Value);
            }
        }

        private static IDictionary<string, string> CreateAndSaveDefaultValues(
            ConfigurationContext dbContext)
        {
            var configValues = new Dictionary<string, string>
                {
                    { "key1", "value_from_ef_1" },
                    { "key2", "value_from_ef_2" }
                };
            dbContext.Values.AddRange(configValues
                .Select(kvp => new ConfigurationValue { Id = kvp.Key, Value = kvp.Value })
                .ToArray());
            dbContext.SaveChanges();
            return configValues;
        }
    }
}

The highlighted values from the database ("value_from_ef_1" and "value_from_ef_2") are displayed when the sample is run.

An EFConfigSource extension method for adding the configuration source can be used:

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace CustomConfigurationProvider
{
    public static class EntityFrameworkExtensions
    {
        public static IConfigurationBuilder AddEntityFrameworkConfig(
            this IConfigurationBuilder builder, Action<DbContextOptionsBuilder> setup)
        {
            return builder.Add(new EFConfigSource(setup));
        }
    }
}

The following code shows how to use the custom EFConfigProvider:

using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using CustomConfigurationProvider;

public static class Program
{
    public static void Main()
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        var connectionStringConfig = builder.Build();

        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            // Add "appsettings.json" to bootstrap EF config.
            .AddJsonFile("appsettings.json")
            // Add the EF configuration provider, which will override any
            // config made with the JSON provider.
            .AddEntityFrameworkConfig(options =>
                options.UseSqlServer(connectionStringConfig.GetConnectionString(
                    "DefaultConnection"))
            )
            .Build();

        Console.WriteLine("key1={0}", config["key1"]);
        Console.WriteLine("key2={0}", config["key2"]);
        Console.WriteLine("key3={0}", config["key3"]);
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

Note the sample adds the custom EFConfigProvider after the JSON provider, so any settings from the database will override settings from the appsettings.json file.

Using the following appsettings.json file:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=CustomConfigurationProvider;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "key1": "value_from_json_1",
  "key2": "value_from_json_2",
  "key3": "value_from_json_3"
}

The following output is displayed:

key1=value_from_ef_1
key2=value_from_ef_2
key3=value_from_json_3

CommandLine configuration provider

The CommandLine configuration provider receives command-line argument key-value pairs for configuration at runtime.

View or download the CommandLine configuration sample

Setup and use the CommandLine configuration provider

To activate command-line configuration, call the AddCommandLine extension method on an instance of ConfigurationBuilder:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;

public class Program
{
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args = null)
    {
        var dict = new Dictionary<string, string>
            {
                {"Profile:MachineName", "MairaPC"},
                {"App:MainWindow:Left", "1980"}
            };

        var builder = new ConfigurationBuilder();

        builder.AddInMemoryCollection(dict)
            .AddCommandLine(args);

        Configuration = builder.Build();

        Console.WriteLine($"MachineName: {Configuration["Profile:MachineName"]}");
        Console.WriteLine($"Left: {Configuration["App:MainWindow:Left"]}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

Running the code, the following output is displayed:

MachineName: MairaPC
Left: 1980

Passing argument key-value pairs on the command line changes the values of Profile:MachineName and App:MainWindow:Left:

dotnet run Profile:MachineName=BartPC App:MainWindow:Left=1979

The console window displays:

MachineName: BartPC
Left: 1979

To override configuration provided by other configuration providers with command-line configuration, call AddCommandLine last on ConfigurationBuilder:

var config = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();

Arguments

Arguments passed on the command line must conform to one of two formats shown in the following table:

Argument format Example
Single argument: a key-value pair separated by an equals sign (=) key1=value
Sequence of two arguments: a key-value pair separated by a space /key1 value1

Single argument

The value must follow an equals sign (=). The value can be null (for example, mykey=).

The key may have a prefix.

Key prefix Example
No prefix key1=value1
Single dash (-)† -key2=value2
Two dashes (--) --key3=value3
Forward slash (/) /key4=value4

†A key with a single dash prefix (-) must be provided in switch mappings, described below.

Example command:

dotnet run key1=value1 -key2=value2 --key3=value3 /key4=value4

Note: If -key2 isn't present in the switch mappings given to the configuration provider, a FormatException is thrown.

Sequence of two arguments

The value can't be null and must follow the key separated by a space.

The key must have a prefix.

Key prefix Example
Single dash (-)† -key1 value1
Two dashes (--) --key2 value2
Forward slash (/) /key3 value3

†A key with a single dash prefix (-) must be provided in switch mappings, described below.

Example command:

dotnet run -key1 value1 --key2 value2 /key3 value3

Note: If -key1 isn't present in the switch mappings given to the configuration provider, a FormatException is thrown.

Duplicate keys

If duplicate keys are provided, the last key-value pair is used.

Switch mappings

When manually building configuration with ConfigurationBuilder, a switch mappings dictionary can be added to the AddCommandLine method. Switch mappings allow key name replacement logic.

When the switch mappings dictionary is used, the dictionary is checked for a key that matches the key provided by a command-line argument. If the command-line key is found in the dictionary, the dictionary value (the key replacement) is passed back to set the configuration. A switch mapping is required for any command-line key prefixed with a single dash (-).

Switch mappings dictionary key rules:

  • Switches must start with a dash (-) or double-dash (--).
  • The switch mappings dictionary must not contain duplicate keys.

In the following example, the GetSwitchMappings method allows command-line arguments to use a single dash (-) key prefix and avoid leading subkey prefixes.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Configuration;

public class Program
{
    public static IConfiguration Configuration { get; set; }

    public static Dictionary<string, string> GetSwitchMappings(
        IReadOnlyDictionary<string, string> configurationStrings)
    {
        return configurationStrings.Select(item =>
            new KeyValuePair<string, string>(
                "-" + item.Key.Substring(item.Key.LastIndexOf(':') + 1),
                item.Key))
                .ToDictionary(
                    item => item.Key, item => item.Value);
    }

    public static void Main(string[] args = null)
    {
        var dict = new Dictionary<string, string>
            {
                {"Profile:MachineName", "RickPC"},
                {"App:MainWindow:Left", "1980"}
            };

        var builder = new ConfigurationBuilder();

        builder.AddInMemoryCollection(dict)
            .AddCommandLine(args, GetSwitchMappings(dict));

        Configuration = builder.Build();

        Console.WriteLine($"MachineName: {Configuration["Profile:MachineName"]}");
        Console.WriteLine($"Left: {Configuration["App:MainWindow:Left"]}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}

Without providing command-line arguments, the dictionary provided to AddInMemoryCollection sets the configuration values. Run the app with the following command:

dotnet run

The console window displays:

MachineName: RickPC
Left: 1980

Use the following to pass in configuration settings:

dotnet run /Profile:MachineName=DahliaPC /App:MainWindow:Left=1984

The console window displays:

MachineName: DahliaPC
Left: 1984

After the switch mappings dictionary is created, it contains the data shown in the following table:

Key Value
-MachineName Profile:MachineName
-Left App:MainWindow:Left

To demonstrate key switching using the dictionary, run the following command:

dotnet run -MachineName=ChadPC -Left=1988

The command-line keys are swapped. The console window displays the configuration values for Profile:MachineName and App:MainWindow:Left:

MachineName: ChadPC
Left: 1988

web.config file

A web.config file is required when hosting the app in IIS or IIS Express. Settings in web.config enable the ASP.NET Core Module to launch the app and configure other IIS settings and modules. If the web.config file isn't present and the project file includes <Project Sdk="Microsoft.NET.Sdk.Web">, publishing the project creates a web.config file in the published output (the publish folder). For more information, see Host ASP.NET Core on Windows with IIS.

Access configuration during startup

To access configuration within ConfigureServices or Configure during startup, see the examples in the Application startup topic.

Adding configuration from an external assembly

An IHostingStartup implementation allows adding enhancements to an app at startup from an external assembly outside of the app's Startup class. For more information, see Enhance an app from an external assembly.

Access configuration in a Razor Page or MVC view

To access configuration settings in a Razor Pages page or an MVC view, add a using directive (C# reference: using directive) for the Microsoft.Extensions.Configuration namespace and inject IConfiguration into the page or view.

In a Razor Pages page:

@page
@model IndexModel

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index Page</title>
</head>
<body>
    <h1>Access configuration in a Razor Pages page</h1>
    <p>Configuration[&quot;key&quot;]: @Configuration["key"]</p>
</body>
</html>

In an MVC view:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index View</title>
</head>
<body>
    <h1>Access configuration in an MVC view</h1>
    <p>Configuration[&quot;key&quot;]: @Configuration["key"]</p>
</body>
</html>

Additional notes

  • Dependency Injection (DI) isn't set up until after ConfigureServices is invoked.
  • The configuration system isn't DI aware.
  • IConfiguration has two specializations:
    • IConfigurationRoot Used for the root node. Can trigger a reload.
    • IConfigurationSection Represents a section of configuration values. The GetSection and GetChildren methods return an IConfigurationSection.
    • Use IConfigurationRoot when reloading configuration or for access to each provider. Neither of these situations are common.

Additional resources