While building my console applications I want to be able to use UserSecrets. Just like I can in an ASP Core application.

In ASP .NET Core application, inside Visual Studio you can right click the project and manage your user secrets. In a console application that is not there. Unfortunately, I could not find a way of getting it there. I needed a way to at least support them, even if I must manually create the secrets and open the resulting config file myself. Something is better than nothing.

I will assume that you are using Windows, PowerShell, Visual Studio and Visual Studio Code.

First, set up the project to support using IOptions and UserSecrets, we'll also include command line arguments, environment variables and json files to make this complete. Add the following NuGet packages:

Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Abstractions
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.CommandLine
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.Json
Microsoft.Extensions.Configuration.UserSecrets
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Options.ConfigurationExtensions

Next, create the options class. We will have 2 properties, SecretOption and OpenOption. We will be setting the SecretOption to the one stored in UserSecrets.

namespace UserSecretsExample
{
    public class MyOptions
    {
        public string SecretOption { get; set; }
        public string OpenOption { get; set; }
    }
}

Now setup the service provider, I do this in a separate class to keep things clean, we'll call it the ServiceProviderBuilder

using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace UserSecretsExample
{
    public static class ServiceProviderBuilder
    {
        public static IServiceProvider GetServiceProvider(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", true, true)
                .AddEnvironmentVariables()
                .AddUserSecrets(typeof(Program).Assembly)
                .AddCommandLine(args)
                .Build();
            var services = new ServiceCollection();

            services.Configure<MyOptions>(configuration.GetSection(typeof(MyOptions).FullName));

            var provider = services.BuildServiceProvider();
            return provider;
        }
    }
}

The breakdown of the configuration is that it will first apply the appsettings.json if it exists (the first true in the parameter list). It will also refresh it if it changes, the second true. Next it applies the environment variables, they will either be added to or override the values in appsettings.json. After that it will apply the UserSecrets file, overriding or appending what is in the collection. Following that, it will apply any command line arguments.

You may notice that when I configure MyOptions I use the full name of the type. This makes it easy to know exactly what the settings are applied to when looking at the appsettings.json. It's the way I do all my configs and I consider it a best practice.

After that create the appsettings.json with the following content, to get it to apply when running the application, you will also need to set it to always copy to the output directory.

{
  "UserSecretsExample.MyOptions": {
    "SecretOption": "<<STORE IN USERSECRETS>>",
    "OpenOption": "From Appsettings"
  }
}

To mark appsettings.json to always copy, in Visual Studio:

  1. Right click appsettings.json in the solution explorer
  2. Click Properties
  3. Change Build Action to Content
  4. Change Copy to Output Directory to Copy Always

Change your program.cs to call the GetServiceProvider, get the MyOptions and write out the values.

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace UserSecretsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var services = ServiceProviderBuilder.GetServiceProvider(args);
            var options = services.GetRequiredService<IOptions<MyOptions>>();

            Console.WriteLine("OpenOption:   " + options.Value.OpenOption);
            Console.WriteLine("SecretOption: " + options.Value.SecretOption);
        }
    }
}

Now that you have that done, we need to set the UserSecretsId property in the project file.

Right click the project in the solution explorer and edit the project file. It should look something like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="appsettings.json" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.2.4" />
    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
  </ItemGroup>

</Project>

In the top PropertyGroup element of your csproj, add a new element, UserSecretsId.

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <UserSecretsId></UserSecretsId>
  </PropertyGroup>

We will now set that UserSecretsId element to a new GUID, it's up to you how you want to do that, but I generally use the Create GUID tool in Visual Studio. Click Tools in the menu, then Create GUID. Then choose Registry Format and click the Copy then Exit.

Put the GUID into that UserSecretsId element. If you used the method above, you will need to remove the { and }. The GUID should contain only numbers, the letters A-F and the 4 dashes. The PropertyGroup should now look something like this (the value of your UserSecretsId will be different):

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <UserSecretsId>2C0E0CB7-2523-4527-A3F3-5623EFB69BEF</UserSecretsId>
  </PropertyGroup>

Run the application, the output should be this:

OpenOption:   From Appsettings
SecretOption: <<STORE IN USERSECRETS>>

Now we will build the secrets.json file using PowerShell and Visual Studio Code.

First, create the correct secrets folder.

mkdir $ENV:HOME\AppData\Roaming\Microsoft\UserSecrets\<<The value in the UserSecretsId element in the csproj>>

For example,

mkdir $ENV:HOME\AppData\Roaming\Microsoft\UserSecrets\2C0E0CB7-2523-4527-A3F3-5623EFB69BEF

Now, create the secrets.json file. I use Visual Studio Code as my primary editor, so I'll use that. This is the command you will use to edit the file in the future.

code $ENV:HOME\AppData\Roaming\Microsoft\UserSecrets\<<The value in the UserSecretsId element in the csproj>>\secrets.json

For example,

code $ENV:HOME\AppData\Roaming\Microsoft\UserSecrets\2C0E0CB7-2523-4527-A3F3-5623EFB69BEF\secrets.json

Put this content in there and save it;

{
  "UserSecretsExample.MyOptions": {
    "SecretOption": "I'm a secret"
  }
}

Run the application and your output should be this:

OpenOption:   From Appsettings
SecretOption: I'm a secret

That is it. You can find the full project source code at https://github.com/veccsolutions/UserSecretsExample