VOOZH about

URL: https://codewithmukesh.com/blog/aws-parameter-store-dotnet/

⇱ AWS Systems Manager Parameter Store for .NET Developers – Free Configuration Management - codewithmukesh


Skip to main content
Article complete

Get one like this every Tuesday at 7 PM IST.

Back to blog
dotnet aws 11 min read Lesson 39/57

AWS Systems Manager Parameter Store for .NET Developers – Free Configuration Management

Learn how to use AWS Systems Manager Parameter Store with ASP.NET Core for managing application configurations. A free-tier alternative to Secrets Manager with full IConfiguration integration, hierarchical parameters, and SecureString encryption.

Learn how to use AWS Systems Manager Parameter Store with ASP.NET Core for managing application configurations. A free-tier alternative to Secrets Manager with full IConfiguration integration, hierarchical parameters, and SecureString encryption.

dotnet aws

parameter store ssm configuration dotnet on aws

👁 Mukesh Murugan
Mukesh Murugan
Software Engineer
Chapter · 39 of 57 Module 8 of 15 Free
View course

AWS for .NET Developers

From dotnet new to docker push — REST, EF Core 10, auth, caching, Clean Architecture, observability. 57 hands-on lessons, source on GitHub.

If you’ve worked with cloud applications, you know the pain of managing configuration across environments. Hardcoding values in appsettings.json works for local development, but production demands something more secure and centralized. AWS offers two primary services for this: Secrets Manager and Parameter Store.

I’ve already covered Secrets Manager in a previous article - it’s excellent for sensitive credentials that need automatic rotation. But here’s the thing: Secrets Manager costs $0.40 per secret per month. If you have dozens of configuration values that don’t require rotation, those costs add up quickly.

Enter AWS Systems Manager Parameter Store - the free-tier friendly alternative that integrates seamlessly with ASP.NET Core’s IConfiguration system. It is one of the essential AWS services every .NET developer should know. In this article, we’ll explore how to use Parameter Store for managing your .NET application configurations, when to choose it over Secrets Manager, and how to wire it up with the Options pattern you’re already familiar with.

Parameter Store vs Secrets Manager – When to Use Which

Before diving into implementation, let’s clarify when to use each service. This decision can save you significant costs and complexity.

FeatureParameter Store (Standard)Secrets Manager
CostFREE (up to 10,000 parameters)$0.40/secret/month
API CallsFree (Standard tier)$0.05 per 10,000 calls
Automatic RotationNoYes (built-in Lambda integration)
Max Size4 KB (Standard) / 8 KB (Advanced)64 KB
VersioningYesYes
EncryptionOptional (SecureString with KMS)Always encrypted
Cross-account AccessYesYes

Use Parameter Store when:

  • You need to store configuration values (feature flags, URLs, non-sensitive settings)
  • Cost is a concern and you have many parameters
  • You don’t need automatic secret rotation
  • You want simple key-value storage with hierarchical organization

Use Secrets Manager when:

  • You’re storing database credentials, API keys, or tokens that need rotation
  • You need automatic rotation with Lambda
  • You require cross-region replication for secrets
  • The cost is justified by the security requirements

For most applications, you’ll use both: Parameter Store for general configuration and Secrets Manager for credentials that need rotation. In this article, we focus on Parameter Store.

Read nextCompanion article

Secrets in ASP.NET Core with AWS Secrets Manager

For sensitive credentials that require automatic rotation, check out my detailed guide on AWS Secrets Manager integration.

Prerequisites

Here’s what you need to follow along:

  • AWS Account (Free Tier works perfectly)
  • .NET 10 SDK
  • Visual Studio 2026 or VS Code
  • AWS CLI configured with your credentials (setup guide here)
  • Basic familiarity with ASP.NET Core configuration

The complete source code for this article is available on GitHub.

Understanding Parameter Store Concepts

Parameter Store organizes parameters using a hierarchical path structure, similar to a file system. This is incredibly powerful for organizing configurations by application and environment.

/production/myapp/database/connectionstring
/production/myapp/features/enablecaching
/development/myapp/database/connectionstring
/development/myapp/features/enablecaching

Parameter Types

Parameter Store supports three types:

  1. String – Plain text values (URLs, feature flags, simple settings)
  2. StringList – Comma-separated values stored as a single parameter
  3. SecureString – Encrypted values using AWS KMS (for sensitive data that doesn’t need rotation)

Standard vs Advanced Tier

FeatureStandardAdvanced
Max parameters10,000100,000+
Max size4 KB8 KB
Parameter policiesNoYes
CostFree$0.05 per parameter/month

For most .NET applications, Standard tier is more than sufficient.

Creating Parameters via AWS Console

Let’s start by creating some parameters in the AWS Console to understand the workflow.

  1. Navigate to AWS Systems ManagerParameter Store
  2. Click Create parameter

👁 AWS Parameter Store Dashboard

Let’s create a hierarchical structure for our demo application. We’ll create parameters for both development and production environments.

Creating Your First Parameter

For the first parameter:

  • Name: /development/demoapp/AppSettings__ApplicationName
  • Tier: Standard
  • Type: String
  • Value: Demo API - Development

👁 Creating a String Parameter

Notice the naming convention: we use double underscores (__) to represent the hierarchy that ASP.NET Core’s configuration system expects. When loaded, /development/demoapp/AppSettings__ApplicationName becomes AppSettings:ApplicationName in your configuration.

Create Additional Parameters

Create the following parameters for our demo:

NameTypeValue
/development/demoapp/AppSettings__ApplicationNameStringDemo API - Development
/development/demoapp/AppSettings__MaxItemsPerPageString25
/development/demoapp/AppSettings__EnableFeatureXStringtrue
/development/demoapp/Database__ConnectionStringSecureStringHost=devserver;Database=devdb;Username=dev;Password=devpass123
/production/demoapp/AppSettings__ApplicationNameStringDemo API - Production
/production/demoapp/AppSettings__MaxItemsPerPageString50
/production/demoapp/AppSettings__EnableFeatureXStringfalse
/production/demoapp/Database__ConnectionStringSecureStringHost=prodserver;Database=proddb;Username=prod;Password=supersecret

For SecureString parameters, AWS will encrypt the value using your default KMS key (or you can specify a custom one).

👁 Parameters organized by environment

Reading Parameters with AWS SDK

Let’s start with the basics - reading parameters directly using the AWS SDK. Create a new ASP.NET Core Web API project:

Terminal window
dotnetnewwebapi-nParameterStoreDemo
cdParameterStoreDemo

Install the required NuGet packages:

Terminal window
dotnetaddpackageAWSSDK.SimpleSystemsManagement
dotnetaddpackageAWSSDK.Extensions.NETCore.Setup
dotnetaddpackageScalar.AspNetCore

Basic Parameter Retrieval

Here’s a minimal API that reads a single parameter:

usingAmazon.SimpleSystemsManagement;
usingAmazon.SimpleSystemsManagement.Model;
usingScalar.AspNetCore;
varbuilder=WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
// Register AWS services
builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions());
builder.Services.AddAWSService<IAmazonSimpleSystemsManagement>();
varapp=builder.Build();
app.MapOpenApi();
app.MapScalarApiReference();
app.MapGet("/parameter", async (stringname, IAmazonSimpleSystemsManagementssm) =>
{
varrequest=newGetParameterRequest
{
Name=name,
WithDecryption=true// Required for SecureString parameters
};
try
{
varresponse=awaitssm.GetParameterAsync(request);
returnResults.Ok(new
{
Name=response.Parameter.Name,
Value=response.Parameter.Value,
Type=response.Parameter.Type.Value,
Version=response.Parameter.Version,
LastModified=response.Parameter.LastModifiedDate
});
}
catch (ParameterNotFoundException)
{
returnResults.NotFound($"Parameter '{name}' not found");
}
});
app.Run();

Run the application and navigate to /scalar/v1 to test the API. Use the name query parameter with a value like /development/demoapp/AppSettings__ApplicationName. You should see the parameter value returned.

Read nextCompanion article

Swagger is Dead? Here's the Alternative!

Learn why .NET 10 uses Scalar instead of Swagger and how to set it up.

👁 Retrieving a single parameter

Retrieving Multiple Parameters by Path

The real power of Parameter Store comes from hierarchical retrieval. You can fetch all parameters under a path with a single API call:

app.MapGet("/parameters/{environment}", async (stringenvironment, IAmazonSimpleSystemsManagementssm) =>
{
varrequest=newGetParametersByPathRequest
{
Path=$"/{environment}/demoapp/",
Recursive=true,
WithDecryption=true
};
varparameters=newList<Parameter>();
string? nextToken=null;
do
{
request.NextToken=nextToken;
varresponse=awaitssm.GetParametersByPathAsync(request);
parameters.AddRange(response.Parameters);
nextToken=response.NextToken;
} while (!string.IsNullOrEmpty(nextToken));
returnResults.Ok(parameters.Select(p=>new
{
Name=p.Name,
Value=p.Value,
Type=p.Type.Value
}));
});

Now you can call /parameters/development to get all parameters for the development environment in a single request. This is exactly how the configuration provider works under the hood.

👁 Retrieving all parameters under a path

Integrating with ASP.NET Core Configuration

Manually calling the SDK works, but the real magic happens when you integrate Parameter Store directly into ASP.NET Core’s configuration system. This allows you to use IConfiguration, IOptions<T>, and all the patterns you’re already familiar with.

Install the official AWS configuration provider:

Terminal window
dotnetaddpackageAmazon.Extensions.Configuration.SystemsManager

Basic Configuration Setup

Update your Program.cs to add Parameter Store as a configuration source:

usingAmazon.Extensions.Configuration.SystemsManager;
usingScalar.AspNetCore;
varbuilder=WebApplication.CreateBuilder(args);
// Add Parameter Store as a configuration source
varenvironment=builder.Environment.EnvironmentName.ToLower();
builder.Configuration.AddSystemsManager($"/{environment}/demoapp/");
builder.Services.AddOpenApi();
varapp=builder.Build();
app.MapOpenApi();
app.MapScalarApiReference();
app.MapGet("/config", (IConfigurationconfig) =>
{
returnResults.Ok(new
{
ApplicationName=config["AppSettings:ApplicationName"],
MaxItemsPerPage=config["AppSettings:MaxItemsPerPage"],
EnableFeatureX=config["AppSettings:EnableFeatureX"],
ConnectionString=config["Database:ConnectionString"]
});
});
app.Run();

The AddSystemsManager method loads all parameters under the specified path and transforms them into the standard configuration format. The double underscores (__) in parameter names become colons (:) in the configuration keys.

When you run this with ASPNETCORE_ENVIRONMENT=Development, it loads from /development/demoapp/. Change it to Production, and it loads from /production/demoapp/.

👁 Configuration loaded from Parameter Store

Using the Options Pattern

Now let’s bind these configurations to strongly-typed classes using the Options Pattern.

Create the settings classes:

publicclassAppSettings
{
publicstringApplicationName { get; set; } =string.Empty;
publicintMaxItemsPerPage { get; set; }
publicboolEnableFeatureX { get; set; }
}
publicclassDatabaseSettings
{
publicstringConnectionString { get; set; } =string.Empty;
}

Register them in Program.cs:

usingAmazon.Extensions.Configuration.SystemsManager;
usingScalar.AspNetCore;
varbuilder=WebApplication.CreateBuilder(args);
// Add Parameter Store configuration
varenvironment=builder.Environment.EnvironmentName.ToLower();
builder.Configuration.AddSystemsManager($"/{environment}/demoapp/");
// Register Options
builder.Services.AddOptions<AppSettings>()
.BindConfiguration(nameof(AppSettings))
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<DatabaseSettings>()
.BindConfiguration(nameof(DatabaseSettings));
builder.Services.AddOpenApi();
varapp=builder.Build();
app.MapOpenApi();
app.MapScalarApiReference();
app.MapGet("/settings", (IOptions<AppSettings> appSettings, IOptions<DatabaseSettings> dbSettings) =>
{
returnResults.Ok(new
{
App=appSettings.Value,
Database=new { dbSettings.Value.ConnectionString } // Be careful exposing this!
});
});
app.Run();

The configuration from Parameter Store seamlessly integrates with ASP.NET Core’s Options pattern. You get all the benefits: strong typing, validation, and IntelliSense support.

👁 Strongly-typed configuration with Options pattern

Environment-Based Configuration Loading

In real applications, you want to load different configurations based on the environment without changing code. This builds on the same idea covered in my guide to environment-based configuration in ASP.NET Core. Here’s the recommended pattern:

usingAmazon.Extensions.Configuration.SystemsManager;
usingScalar.AspNetCore;
varbuilder=WebApplication.CreateBuilder(args);
// Only load from Parameter Store in non-development environments
if (!builder.Environment.IsDevelopment())
{
varenvironment=builder.Environment.EnvironmentName.ToLower();
builder.Configuration.AddSystemsManager(config=>
{
config.Path=$"/{environment}/demoapp/";
config.ReloadAfter=TimeSpan.FromMinutes(5); // Optional: auto-reload
});
}
builder.Services.AddOptions<AppSettings>()
.BindConfiguration(nameof(AppSettings));
builder.Services.AddOptions<DatabaseSettings>()
.BindConfiguration(nameof(DatabaseSettings));
builder.Services.AddOpenApi();
varapp=builder.Build();
app.MapOpenApi();
app.MapScalarApiReference();
app.MapGet("/settings", (IOptions<AppSettings> appSettings) =>
{
returnResults.Ok(appSettings.Value);
});
app.Run();

With this setup:

  • Development: Uses appsettings.Development.json (local development, no AWS calls)
  • Production/Staging: Loads from Parameter Store with environment-specific paths

Your appsettings.Development.json provides local defaults:

{
"AppSettings": {
"ApplicationName": "Demo API - Local Development",
"MaxItemsPerPage": 10,
"EnableFeatureX": true
},
"DatabaseSettings": {
"ConnectionString": "Host=localhost;Database=localdb;Username=local;Password=local"
}
}

Auto-Reloading Configuration Changes

One powerful feature of the Parameter Store configuration provider is automatic reloading. If someone updates a parameter in AWS, your application can pick up the change without restarting.

builder.Configuration.AddSystemsManager(config=>
{
config.Path=$"/{environment}/demoapp/";
config.ReloadAfter=TimeSpan.FromMinutes(5);
});

To react to these changes in your code, use IOptionsMonitor<T> instead of IOptions<T>:

app.MapGet("/settings/live", (IOptionsMonitor<AppSettings> appSettings) =>
{
returnResults.Ok(appSettings.CurrentValue);
});

Important: Be mindful of API costs if you set a very short reload interval. Every reload triggers GetParametersByPath API calls. For Standard tier, these calls are free, but it’s still good practice to use reasonable intervals (5-15 minutes).

Read nextCompanion article

Options Pattern in ASP.NET Core

Learn more about IOptions, IOptionsSnapshot, and IOptionsMonitor for handling configuration reloads.

Working with SecureString Parameters

For sensitive values that don’t need automatic rotation (like internal service URLs with embedded credentials, or encryption keys), use SecureString parameters. These are encrypted at rest using AWS KMS.

When using the configuration provider, SecureString parameters are automatically decrypted - no additional code required. The provider handles the WithDecryption = true flag internally.

However, if you’re using the SDK directly, remember to set WithDecryption:

varrequest=newGetParameterRequest
{
Name="/production/demoapp/Database__ConnectionString",
WithDecryption=true// Essential for SecureString!
};

Without WithDecryption = true, you’ll get the encrypted ciphertext instead of the actual value.

When to Use SecureString vs Secrets Manager

ScenarioRecommendation
Database password that changes rarelySecureString in Parameter Store
API key for external service (no rotation)SecureString in Parameter Store
Database credentials with auto-rotationSecrets Manager
OAuth client secretsSecrets Manager
Simple feature flagsPlain String in Parameter Store

Complete Example

Here’s the complete Program.cs bringing everything together. You can find the full source code with additional files on GitHub.

usingAmazon.Extensions.Configuration.SystemsManager;
usingMicrosoft.Extensions.Options;
usingScalar.AspNetCore;
varbuilder=WebApplication.CreateBuilder(args);
// Add Parameter Store configuration for non-development environments
if (!builder.Environment.IsDevelopment())
{
varenvironment=builder.Environment.EnvironmentName.ToLower();
builder.Configuration.AddSystemsManager(config=>
{
config.Path=$"/{environment}/demoapp/";
config.ReloadAfter=TimeSpan.FromMinutes(5);
});
}
// Register strongly-typed configuration
builder.Services.AddOptions<AppSettings>()
.BindConfiguration(nameof(AppSettings))
.ValidateDataAnnotations()
.ValidateOnStart();
builder.Services.AddOptions<DatabaseSettings>()
.BindConfiguration(nameof(DatabaseSettings));
builder.Services.AddOpenApi();
varapp=builder.Build();
app.MapOpenApi();
app.MapScalarApiReference();
// Endpoint using IOptions (cached at startup)
app.MapGet("/settings", (IOptions<AppSettings> appSettings) =>
{
returnResults.Ok(appSettings.Value);
});
// Endpoint using IOptionsMonitor (reflects live changes)
app.MapGet("/settings/live", (IOptionsMonitor<AppSettings> appSettings) =>
{
returnResults.Ok(appSettings.CurrentValue);
});
// Health check endpoint
app.MapGet("/health", (IOptions<AppSettings> appSettings, IWebHostEnvironmentenv) =>
{
returnResults.Ok(new
{
Status="Healthy",
Application=appSettings.Value.ApplicationName,
Environment=env.EnvironmentName
});
});
app.Run();
publicclassAppSettings
{
publicstringApplicationName { get; set; } =string.Empty;
publicintMaxItemsPerPage { get; set; } =20;
publicboolEnableFeatureX { get; set; }
}
publicclassDatabaseSettings
{
publicstringConnectionString { get; set; } =string.Empty;
}

IAM Permissions

Your application needs the following IAM permissions to read from Parameter Store:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Resource": "arn:aws:ssm:us-east-1:YOUR_ACCOUNT_ID:parameter/production/demoapp/*"
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:us-east-1:YOUR_ACCOUNT_ID:key/YOUR_KMS_KEY_ID",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:PARAMETER_ARN": "arn:aws:ssm:us-east-1:YOUR_ACCOUNT_ID:parameter/production/demoapp/*"
}
}
}
]
}

The kms:Decrypt permission is only needed if you’re using SecureString parameters. Scope your policies to specific paths for better security - avoid wildcards like parameter/*. For the IAM credentials your application uses to make these calls, see how to set up AWS credentials for .NET applications.

Best Practices

  1. Use hierarchical naming: Organize parameters by /{environment}/{application}/{category}/{key} for easy management and access control.
  2. Environment isolation: Use IAM policies to restrict which environments an application can access. Production workloads should never be able to read development parameters.
  3. Prefer SecureString for sensitive data: Even if you don’t need rotation, SecureString adds encryption at rest.
  4. Use reasonable reload intervals: 5-15 minutes is typically sufficient. Shorter intervals increase API calls unnecessarily.
  5. Local development fallback: Always provide appsettings.Development.json so developers can work without AWS access.
  6. Validate on startup: Use ValidateOnStart() to catch configuration issues immediately rather than at runtime.
  7. Tag your parameters: Use AWS tags for cost allocation, ownership tracking, and easier management.

Summary

AWS Systems Manager Parameter Store provides a free, simple, and powerful way to manage configuration for your .NET applications. With native integration into ASP.NET Core’s configuration system, you get:

  • Zero cost for Standard tier (up to 10,000 parameters)
  • Hierarchical organization with path-based retrieval
  • Environment separation using naming conventions
  • Encryption support via SecureString and KMS
  • Auto-reload capability for dynamic configuration updates
  • Seamless integration with IOptions, IConfiguration, and the patterns you already use

For most configuration needs, Parameter Store is the right choice. Reserve Secrets Manager for credentials that genuinely need automatic rotation.

Read nextCompanion article

Automate RDS Credential Rotation with AWS Secrets Manager

The natural next step - rotate database credentials automatically and consume them in ASP.NET Core with zero downtime.

The complete source code for this article is available on GitHub. Clone it, try it out, and adapt it to your needs!

If this article helped you, share it with your colleagues - and let me know in the comments what AWS + .NET topics you’d like me to cover next.

More from the archive.

View all articles

Keep digging. 8 more from the archive.

What's your take?

Push back, share a war story, or ask the obvious question someone else is wondering. I read every comment.

View on GitHub

Weekly .NET tips · free

Subscribed · Tue 7 PM IST

You're in.
Welcome to the crew.

Tuesday's issue lands in your inbox at 7 PM IST. One last step: confirm your email so it actually arrives.

01 · Check your inbox

02 · Every Tuesday

Benchmarks, architecture insights, and production tips that never make it to the blog.

Privacy notice · 30s read

Cookies, but only the useful ones.

I use cookies to understand which articles get read and which CTAs actually work. No third-party advertising trackers, ever. Read the privacy policy →