A little unknown feature of dotnet is User Secrets. This is used to create local App Setting overrides to set values for local usage. This can be a handy and powerful tool for keeping your local setup separate from checking in code.
You can find more details on the Microsoft Documentation here https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets
The goal of this feature is to overwrite your App Settings with local values. For example if you have a connection string within your JSON, you are not going to want your local Username/Password stored in there to be checked. You also don’t want to be pulling other peoples settings down and having to keep changing them. Therefore, with this feature you set your values on your local machine in a different file and they get overridden, not overwritten, so they will work for your but never be checked in.
Within your project location you can run the ‘init’ command, which will create a new node in the project file. This is called ‘UserSecretId’, which contains the ID for this project. When this runs it will use the ID to match up with where the secrets are stored.
The secrets are stored in the folder ‘C:/Users/[UserName]/AppData/Roaming/Microsoft/UserSecrets’ then in a directory with the ID as the directory name. Within this folder there is then a file called ‘secrets.json’ where you will store all the secrets in a json format. You can get more detail on how to format the name properties of your App Settings on the documentation.
When you run the ‘init’ command it doesn’t create this directory and file for you, so I whipped together a script below to generate the User Secret ID and to also create the required directory/file. Before I talk about that I will also show have to use User Secrets with Dotnet Core Console Apps.
This could be something I have done wrong, but when I create a Web Application and use the feature it just works with no extra effort. However, when I created a Console App it did not just work out the box. I found I needed to do a few things to get it working, which Stafford Williams talks about here
One part he missed was when using Dependency Injection to inject where to find the User Secrets ID in the Builder as per:
varbuilder=newConfigurationBuilder()
.AddJsonFile("appsettings.json",optional:false,reloadOnChange:true)
.AddJsonFile(envJson,optional:false,reloadOnChange:true)
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
Create User Secrets
In the below code it accepts a single project path and a directory with many projects. It will find the project files and check if they have the User Secrets ID in the project.
If it doesn’t then it will go to the directory, run the ‘init’ command and then get the ID as well.
From there it can check/create the folders and files for the User Secrets.
Param (
[string]$projectPath
)
$projects;
$filesCount = 0
if ($projectPath.EndsWith('.csproj')) {
$projects = Get-ChildItem -Path $projectPath
$filesCount = 1
}
else {
if ($projectPath.EndsWith('/') -eq $false -or $projectPath.EndsWith('\') -eq $false) {
$projectPath += "/";
}
$projects = Get-ChildItem -Path "$projectPath*.csproj" -Recurse -Force
$filesCount = $projects.Length
}
Write-Host("Files Found $filesCount")
if ($filesCount -gt 0) {
$userSecretsPath = "$ENV:UserProfile/AppData/Roaming/Microsoft/UserSecrets"
if (!(Test-Path $userSecretsPath)) {
Write-Host("Create User Secrets Path")
New-Item -ItemType directory -Path $userSecretsPath
}
$currentDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Definition)
foreach ($project in $projects) {
Write-Host(" ")
Write-Host("Current Project $project")
[xml]$fileContents = Get-Content -Path $project
if ($null -eq $fileContents.Project.PropertyGroup.UserSecretsId) {
Write-Host("User Secret ID node not found in project file")
Set-Location $project.DirectoryName
dotnet user-secrets init
Set-Location $currentDir
Write-Host("User Secret Create")
[xml]$fileContents = Get-Content -Path $project
}
$userSecretId = $fileContents.Project.PropertyGroup.UserSecretsId
Write-Host("User Secret ID $userSecretId")
if ($userSecretId -ne ""){
$userSecretPath = "$userSecretsPath/$userSecretId"
if (!(Test-Path $userSecretPath)) {
New-Item -ItemType directory -Path $userSecretPath
Write-Host("User Secret ID $userSecretId Path Created")
}
$secretFileName = "secrets.json"
$secretPath = "$userSecretsPath/$userSecretId/$secretFileName"
if (!(Test-Path $secretPath)) {
New-Item -path $userSecretPath -name $secretFileName -type "file" -value "{}"
Write-Host("User Secret ID $userSecretId secrets file Created")
}
Write-Host("User Secrets path $secretPath")
}
}
}