# ASP.NET Core Configuration Guide

# ASP.NET Core Configuration Guide

## Overview

ASP.NET Core uses a **layered configuration system** where later sources override earlier ones for the same key.

* * *

## Configuration Priority Order

| Priority | Source | When Loaded |
| --- | --- | --- |
| 1 (lowest) | `appsettings.json` | Always |
| 2 | `appsettings.{Environment}.json` | Always |
| 3 | User Secrets | Development only |
| 4 | Environment variables | Always |
| 5 | Command-line arguments | Always |
| 6 (highest) | Azure Key Vault (if registered) | When explicitly added in code |

> **Rule**: Later sources override earlier ones for the same key.

* * *

## Environment Detection

ASP.NET Core determines the current environment from the `ASPNETCORE_ENVIRONMENT` variable.

Common values:

*   `Development`
    
*   `Staging`
    
*   `Production`
    

This is typically set in `launchSettings.json` for local development:

```json
{
    "profiles": {
        "Dev": {
            "commandName": "Project",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://localhost:5056"
        },
        "Production": {
            "commandName": "Project",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Production"
            },
            "applicationUrl": "http://localhost:5055"
        }
    }
}
```

### Checking environment in code

```csharp
// During host building 
if (builder.Environment.IsDevelopment()) 
{ 
  // development-only setup 
}
// After app is built 
if (app.Environment.IsProduction()) 
{ 
   // production-only setup 
}
```

* * *

## 1\. appsettings.json Files

### Base configuration (always loaded)

### appsettings.json

```json
{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        }
    },
    "AllowedHosts": "*"
}
```

### Environment-specific overrides

### appsettings.Development.json

```json
{
    "Logging": {
        "LogLevel": {
            "Default": "Debug"
        }
    },
    "ConnectionStrings": {
        "MyDiaryDb": "Server=127.0.0.1;Port=5432;Database=MyDiaryDb_Dev;UserId=postgres;Password=12345;"
    }
}
```

### appsettings.Production.json

```json
{
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    }
}
```

### How it merges

When running in Development:

appsettings.json → LogLevel: Default = "Information" appsettings.Development.json → LogLevel:Default = "Debug" ← wins

The environment-specific file **overrides** matching keys, but non-overlapping keys from `appsettings.json` are still available.

* * *

## 2\. User Secrets

### What are they?

A **development-only** mechanism for storing sensitive config values (passwords, API keys) **outside** the project directory so they are never committed to source control.

### Where are they stored?

| OS | Path |
| --- | --- |
| Windows | `%APPDATA%\Microsoft\UserSecrets\<UserSecretsId>\secrets.json` |
| Linux/Mac | `~/.microsoft/usersecrets/<UserSecretsId>/secrets.json` |

### Setup

#### 1\. Initialize (one-time per project)

```powershell
dotnet user-secrets init
```

This adds a `<UserSecretsId>` to your `.csproj`:

```xml
<PropertyGroup>
	<TargetFramework>net10.0</TargetFramework>
	<UserSecretsId>a1b2c3d4-e5f6-7890-abcd-ef1234567890</UserSecretsId>
</PropertyGroup>
```

#### 2\. Set secrets

> dotnet user-secrets set "ConnectionStrings:MyDiaryDb" "Server=127.0.0.1;Port=5432;Database=MyDiaryDb\_Dev;User Id=postgres;Password=SecretPassword;"

#### 3\. List secrets

```powershell
dotnet user-secrets list
```

#### 4\. Remove a secret

```powershell
dotnet user-secrets remove "ConnectionStrings:MyDiaryDb"
```

#### 5\. Clear all secrets

```powershell
dotnet user-secrets clear
```

### How they are loaded

You do **not** need to register them manually. `WebApplication.CreateBuilder(args)` automatically loads User Secrets when `ASPNETCORE_ENVIRONMENT=Development`:

> This happens internally — you don't write this yourself:

```csharp
if (builder.Environment.IsDevelopment()) 
{ 
  builder.Configuration.AddUserSecrets(); 
}
```

### Priority

User Secrets override `appsettings.Development.json` for the same key:

> appsettings.json → ConnectionStrings:MyDiaryDb = (not set) appsettings.Development.json → ConnectionStrings:MyDiaryDb = "local-dev-string" User Secrets → ConnectionStrings:MyDiaryDb = "secret-string" ← wins

### Key characteristics

| Question | Answer |
| --- | --- |
| In source control? | No |
| Works in Production? | No, Development only |
| Overrides appsettings.Development.json? | Yes |
| Needs manual registration? | No, automatic in Development |
| Shared across team? | No, per-developer |

### When to use

*   Local database passwords
    
*   API keys for third-party services during development
    
*   Any secret you don't want in Git
    

* * *

## 3\. Environment Variables

### How they work

Environment variables override all file-based config. Use `__` (double underscore) as a section separator:

#### Linux/Mac

```powershell
export ConnectionStrings\_\_MyDiaryDb="Server=prod-server;..."
```

#### Windows (PowerShell)

```powershell
$env:ConnectionStrings\_\_MyDiaryDb = "Server=prod-server;..."
```

### In code

```csharp
// Read normally — the framework resolves the source automatically var connectionString = builder.Configuration.GetConnectionString("MyDiaryDb");
```

### Priority

Environment variables override User Secrets and appsettings files:

> appsettings.Development.json → ConnectionStrings:MyDiaryDb = "dev-string" User Secrets → ConnectionStrings:MyDiaryDb = "secret-string" Environment Variable → ConnectionStrings:MyDiaryDb = "env-string" ← wins

### Common use cases

*   CI/CD pipelines
    
*   Docker containers
    
*   Azure App Service (Application Settings)
    

* * *

## 4\. Azure Key Vault

### What it does

Stores secrets centrally in Azure. Your app fetches them at startup and they become part of the configuration system.

### Setup

#### Install package

```powershell
dotnet add package Azure.Identity 
dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets

```
#### Register in Program.cs (conditionally)

```csharp
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
// Only connect to Key Vault in non-Development environments 
if (!builder.Environment.IsDevelopment()) 
{ 
  var keyVaultUrl = new Uri("https://your-vault-name.vault.azure.net/"); 
  builder.Configuration.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential()); 
}
// Connection string is resolved from whatever source is available 
builder.Services.AddDbContext<DataContext>(options => { 
  options.UseNpgsql(builder.Configuration.GetConnectionString("MyDiaryDb")); 
});
```

### Secret naming in Key Vault

Use `--` as separator (Key Vault doesn't allow `:`):

| Key Vault Secret Name | Maps to Configuration Key |
| --- | --- |
| `ConnectionStrings--MyDiaryDb` | `ConnectionStrings:MyDiaryDb` |
| `Jwt--Key` | `Jwt:Key` |
| `Jwt--Issuer` | `Jwt:Issuer` |

### Priority

Key Vault is the **highest priority** when registered (added last):

> appsettings.json → ConnectionStrings:MyDiaryDb = (not set) appsettings.Production.json → ConnectionStrings:MyDiaryDb = (not set) Environment Variables → ConnectionStrings:MyDiaryDb = "env-string" Azure Key Vault → ConnectionStrings:MyDiaryDb = "vault-string" ← wins

### Authentication methods (DefaultAzureCredential)

`DefaultAzureCredential` tries these in order:

1.  Environment variables (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET`)
    
2.  Managed Identity (Azure App Service, VM, Container Apps)
    
3.  Visual Studio credentials
    
4.  Azure CLI (`az login`)
    
5.  Azure PowerShell
    

### Why wrap in environment check

```csharp
// BAD — tries to connect to Azure even in Development 
var keyVaultUrl = new Uri("https://your-vault.vault.azure.net/"); 
builder.Configuration.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential());

// GOOD — only connects in Production 
if (!builder.Environment.IsDevelopment()) 
{ 
  var keyVaultUrl = new Uri("https://your-vault.vault.azure.net/"); 
  builder.Configuration.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential()); 
}

```

Without the check, `AddAzureKeyVault` attempts to authenticate to Azure **immediately** at startup. If credentials are not available locally, the app crashes or hangs before it ever reads your local config files.

* * *

## 5\. Command-Line Arguments

Highest priority among built-in sources (before Key Vault):

> dotnet run --ConnectionStrings:MyDiaryDb="Server=override;..."

Useful for one-off overrides during testing.

* * *

## Complete Example: Program.cs

```csharp
using Azure.Identity; using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Configuration is already loaded in this order: 
  // 1. appsettings.json 
  // 2. appsettings.{Environment}.json 
  // 3. User Secrets (Development only, automatic) 
  // 4. Environment variables 
  // 5. Command-line args
  // 6. Azure Key Vault (Production only, manual) 
  if (!builder.Environment.IsDevelopment()) 
  { 
    var keyVaultUrl = new Uri("https://your-vault.vault.azure.net/"); 
    builder.Configuration.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential()); 
  }
// This resolves from whichever source has the highest priority 
  var connectionString = builder.Configuration.GetConnectionString("MyDiaryDb");
  builder.Services.AddDbContext<DataContext>(options => { options.UseNpgsql(connectionString); });
  var app = builder.Build(); app.Run();

```

* * *

## Summary: Which source to use when

| Environment | Recommended secret source |
| --- | --- |
| Local Development | User Secrets |
| CI/CD Pipeline | Environment variables |
| Azure App Service | Azure Key Vault or App Settings |
| Docker/Container | Environment variables |

* * *

## Quick Reference: Common Commands

##### Initialize user secrets

> dotnet user-secrets init

##### Set a secret

> dotnet user-secrets set "ConnectionStrings:MyDiaryDb" "your-connection-string"

##### List all secrets

> dotnet user-secrets list

##### Clear all secrets

> dotnet user-secrets clear

##### Azure CLI login (for local Key Vault access)

> az login

* * *

## Common Mistakes

### 1\. Key Vault without environment check

```csharp
// Crashes locally if no Azure credentials 
  builder.Configuration.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential());
```

### 2\. Secrets in appsettings.json committed to Git

```csharp
// DON'T — this goes into source control 
{
    "ConnectionStrings": {
        "MyDiaryDb": "Server=prod;Password=secret123;"
    }
}
```

### 3\. Using Production profile for local development

```csharp
// Ensure your local profile uses Development 
{
    "Dev": {
        "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
        }
    }
}
```

### 4\. Assuming User Secrets work in Production

They don't. User Secrets are **Development-only** by design.

* * *

## References

*   [ASP.NET Core Configuration](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/)
    
*   [Safe storage of secrets in Development](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets)
    
*   [Azure Key Vault configuration provider](https://learn.microsoft.com/en-us/aspnet/core/security/key-vault-configuration)
