Add User Secrets to a .NET Core Console Application
While building my console applications I want to be able to use UserSecrets. Just like I can in an ASP Core application.
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:
- Right click
appsettings.json
in the solution explorer - Click
Properties
- Change
Build Action
toContent
- Change
Copy to Output Directory
toCopy 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