Converting ASP.NET Core user secrets to environment variables at runtime
Tags: csharp, aspdotnet, azurefunctions, blazor
Published: 2023-08-11 16:06:15

I'm currently in the process of building [Panache Sports](https://www.panachesports.com) with a front end coded as a server side [Blazor](https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor) web application backed by serverless [Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview).

For the front end I'm using the [secret manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) to prevent me from checking configuration details into GitHub.

This works well and is simple to use.  In my project directory I run the following in the .NET CLI:

```
dotnet user-secrets init
```
This creates a 'secrets.json' specific to the project on my local machine in this location:

```
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
```
I can then add new secrets using the command:

```
dotnet user-secrets set "Mysecrets:ApiKey" "12345"
```
Or, I can use the 'Manage User Secrets' option when right-clicking on my project in Visual Studio:

![Manage Secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dmx3n4c7ln4acd0tyw9.png)

Which allows me to directly edit the json file.

In my code I can then simply refer to these via the IConfiguration interface as if they had been placed in 'appsettings.json' within the project, so for example in the 'Program.cs' file of my project I could do the following to get the secret I created above:

```
var mySecret = builder.Configuration.GetValue<string>("Mysecrets:ApiKey");
```
So I can create and access configuration details when running the code locally, knowing that my API Keys and other data won't be checked into GitHub for others to see.

When I push this project from my local development environment up to an [Azure Web App Service](https://azure.microsoft.com/en-gb/products/app-service/web) the configuration setup will be stored as environment variables, but Azure will link everything up so that my 'builder.Configuration.GetValue' call still works.

**Azure Functions**

When developing the Azure Functions locally that I use for the backend data access I can use environment variables to get the same type of details.

The Azure Function project in Visual Studio includes a 'local.settings.json' file, which Git ignores by default, where I can store my configuration details.

I can then access those values via the 'System.Environment' call:

```
var mySecret = Environment.GetEnvironmentVariable("My_Secret_Api_Key");
```
And again, this works locally and when I publish my Function to Azure.

## The shared code issue

Now comes the problem I faced.  I have some code that I'm using in my Azure Function that I would like to re-use in my Web application, and in the Azure Function that's using a 'Environment.GetEnvironmentVariable' call.

No problem right, that call will work quite happily when I push the code to Azure, so the following lookup:

```
var mySecret = Environment.GetEnvironmentVariable("My_Secret_Api_Key");
```
will return the exact same value as getting it from the configuration via. 

```
var mySecret = builder.Configuration.GetValue<string>("My_Secret_Api_Key");
```
So I can reuse the code in both projects without any changes and all will work fine in Azure.

But what about when running locally?  That's where I now have an issue.

```
var mySecret = Environment.GetEnvironmentVariable("My_Secret_Api_Key");
```
looks in a different place to 'appsettings.json' or the secret manager.  It looks at the environment variables stored in 'launchSettings.json', which are also available via the launch profiles screen:

![Launch Profiles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzjtk5wd99u8i73uw87u.png)

Although I can avoid checking these into GitHub as well the main issue is that I'm now storing some configuration in the secret manager while the rest needs to be stored in the build configuration.

That's not a major problem, but it is a pain to maintain two areas.  It would be much nicer if I could put everything in the secret manager and not have to change any of my calls.

Well it turns out I can.

## Setting environment variables via code

Not only can I read environment variables at runtime, but I can also set them at runtime with the following call:

```
Environment.SetEnvironmentVariable("My_Secret_Api_Key", "12345");
```

Now I can read my secret from the secret manager and set it as an environment variable at runtime:

```
var mySecret = builder.Configuration.GetValue<string>("My_Secret_Api_Key");

Environment.SetEnvironmentVariable("My_Secret_Api_Key", mySecret);
```

The only issue with this is that it is unnecessary when I'm running in Azure, so I can limit it to only setting the environment variable when running in my (local) development environment.

You can determine the environment in different ways, but in my case I simply do the following in the 'Program.cs' of my web application:

```
var mySecret = builder.Configuration.GetValue<string>("My_Secret_Api_Key");

var app = builder.Build();

if (app.Environment.IsDevelopment()) 
{
Environment.SetEnvironmentVariable("My_Secret_Api_Key", mySecret);
}

```

Now when I'm running locally I store all my configuration in the secret manager and convert them at runtime to environment variables if required.

When I push the code to Azure everything gets set using the configuration tab of the Azure web app service and the code works without any changes.

Pete

[![Buy Me A Coffee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whmy70aj858tvqekv5n7.png)](https://www.buymeacoffee.com/panachesoftware)