I create release pipeline on Azure DevOps server and i have a some problem.
How i can change properties in .net core configuration file (appsettings.EnvName.json).
When I create application on framework I had parameters.xml where I set XPath to value, default value and property name. And on pipeline I set key-value. But on net core app this method don't work =)
I want to use about the same approach. What would I indicate the path to the value and its value. For example:
ConnectionStrings.Db1="Server={DB1.Server};Database={DB1.DbName};Trusted_Connection = True;"
ConnectionStrings.Db2="Server={DB2.Server};Database={DB2.DbName};Trusted_Connection = True;"
Now I have added a step to execute an arbitrary powershell script on a remote server
$jsonFile = 'appsettings.Template.json'
$jsonFileOut = 'appsettings.Production.json'
$configValues =
'ConnectionStrings.Db1="Server={DB1.Server};Database={DB1.DbName};Trusted_Connection = True;"',
'ConnectionStrings.Db2="Server={DB2.Server};Database={DB2.DbName};Trusted_Connection = True;"'
$config = Get-Content -Path $jsonFile | ConvertFrom-Json
ForEach ($item in $configValues)
{
$kv = $item -split "="
Invoke-Expression $('$config.' + $kv[0] + '="' + $kv[1] + '"')
}
$config | ConvertTo-Json | Out-File $jsonFileOut
But I don’t really like this solution, how can I do the same in a more beautiful way
dotnet core handles this in a different way. Full framework based on app.config transformation. It means that you defined one file which later was trasnformed for given build configuration (like Debug, Release, or your own). In dotnet core you define appsettings.json for each environment. This works very well because all settings are in your compiled app. And then at runtime bases on ASPNETCORE_ENVIRONMENT environment variable a proper settings is selected. Thus you may have one package for your all environments without recompilation. To benefit from that you must define file per each enviroment, but this is not transformation. This is full file.
For instance file for your local development may look like this:
{
"ConnectionStrings": {
"BloggingDatabase": "Server=(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
And file for your dev enviroment appsettings.dev.json like this:
{
"ConnectionStrings": {
"BloggingDatabase": "Server=102.10.10.12\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
And then to configure loading this file you have to have configured Startup method:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
This will load all your appsettings file and later use proper file based on enviroment variable.
To set this variable you may use this command in command prompt setx ASPNETCORE_ENVIRONMENT Dev or this in Powershell [Environment]::SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Dev", "Machine")
I hope it help you understand how settings works on dotnet core. If you need more guidance please check this links:
Configuration in ASP.NET Core
Use multiple environments in ASP.NET Core
To sum up you don't need to change your settings in release pipeline. You need to preapre full file per enviromnet where you are going to host your app. You can be interested in replacing some values in file based on variables in your pipeline. You can consider few options here like
token replacement
JSON variable substitution example
This is usefult when you don't want to keep your secrets directly in source code.
EDIT
If you want to replace values in you appsettings file one of the option is token replace. For this you must first instead of values keep token in your file. For instance #{SomeVariable}# will be replaced with value of SomeVariable` from your pipeline for this confirguration of token replace task.
Related
I want my .Net Core 5 console application to select settings from the appropriate appsettings file based on the DOTNET_ENVIRONMENT environment variable. I'm testing this by running it in the Visual Studio 2019 debugger and fetching the environment from my launchSettings.json file.
In a .Net Core 5 console application I have 4 "appsettings" files:
appsettings.json
appsettings.Development.json
appsettings.Staging.json
appsettings.Production.json
Each file Properties is set to Build Action : Content, and Copy to Output Directory: Copy if newer.
In my launchSettings.json I have my environment set to "Staging" like so:
{
"profiles": {
"MyAppName": {
"commandName": "Project",
"dotnetRunMessages": "true",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Staging"
}
}
}
}
I need access to my configuration in the "Main" method in Program.cs, so in that class I am setting a module-level string variable "_environment" like so in the static constructor:
_environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
This works; the value "Staging" gets loaded into the variable _environment.
I then load my Configuration into a static variable like so: (EDIT--this was my mistake, assuming this static property loaded AFTER the static ctor. In fact it loaded BEFORE the static ctor. This meant the _environment variable was not set, which means my environment-specific appsettings file never loaded).
private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{_environment}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
When I then examine the contents of the Configuration, I see that it is only loading values from appsettings.json. It is not loading values from appsettings.Staging.json.
The specific value I am looking for is "ConnectionStrings". This is how the ConnectionStrings section looks in appsettings.json:
"ConnectionStrings": {
"ConnectionStringName": "Data Source=SqlDevelopment; Initial Catalog=MyTable; Integrated Security=SSPI;",
}
And this is how that same section looks in appsettings.Staging.json:
"ConnectionStrings": {
"ConnectionStringName": "Data Source=SqlStaging; Initial Catalog=MyTable; Integrated Security=SSPI;",
}
But when I read the DataSource from the Configuration it is always "SqlDevelopment", even though the environment is Staging.
After trying and failing, I tried loading these 4 Nuget packages, but it had no effect:
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.EnvironmentVariables
Microsoft.Extensions.Configuration.Json
What am I missing?
Console applications check the DOTNET_ environment variables, not the ASPNETCORE_ variables. This isn't a new change. It goes back at least to .NET Core 3.1. You need to set DOTNET_ENVIRONMENT instead.
ASP.NET Core applications use any environment variables prefixed with ASPNETCORE_ in addition to the DOTNET_ variables.
From the docs :
The default configuration loads environment variables and command line arguments prefixed with DOTNET_ and ASPNETCORE_. The DOTNET_ and ASPNETCORE_ prefixes are used by ASP.NET Core for host and app configuration, but not for user configuration. For more information on host and app configuration, see .NET Generic Host.
PS: Just 10 minutes ago I had the same problem and realized I had set DOTNETCORE_ENVIRONMENT instead of DOTNET_ENVIRONMENT
I need access to my configuration in the "Main" method in Program.cs, so in that class I am setting a module-level string variable "_environment" like so in the static constructor:
_environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
That's not how it works. You need to use IHostEnvironment.EnvironmentName. If you need access to your configuration in your Main() method, you're doing something wrong, and reading the environment variable is the wrong way to read the (ASP).NET Core Environment name.
The "(ASP).NET Core Environment name" can be set in multiple ways, one way being OS Environment variables. But they don't have to be, they can also be provided through command line arguments.
IHostEnvironment.EnvironmentName is the proper way to get the environment name.
Thank you everyone and especially #CodeCaster for helping me.
The issue is that the _environment variable was an empty string when the static Configuration was set. I assumed that since I was setting it in the static constructor it was available, but the static ctor was actually running after I set my static Configuration, so _environment was an empty string, so the "Staging" configuration was never loaded.
I altered the code so that there is no chance that the runtime will set variables in an order that I did not expect:
private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.Build();
A number of posters essentially told me "you're doing it wrong." I realize that the runtime provides a dependency-injected Configuration after Host.CreateDefaultBuilder() is called. For reasons outside the scope of this question I happen to need Configuration before that call occurs, inside Program.Main. If I did not need Configuration in Program.Main, before the call to Host.CreateDefaultBuilder, none of this would be necessary.
I'm trying to set an environment variable for an API key that I don't want in my code. My source javascript looks something like this :
.get(`http://api-url-and-parameters&api-key=${process.env.API_KEY}`)
I'm using webpack and the package dotenv-webpack https://www.npmjs.com/package/dotenv-webpack to set API_KEY in a gitignored .env file and it's all running fine on my local. I'd like to also be able to set that variable when deploying through Netlify, I've tried adding it through to GUI to the 'build environment variables', and also to set it directly in the build command, but without success.
Any idea what might be the issue ?
WARNING: If this is a secret key, you will not want to expose this environment variable value in any bundle that gets returned to the client. It should only be used by your build scripts to be used to create your content during build.
Issue
dotenv-webpack expects there to be a .env file to load in your variables during the webpack build of your bundle. When the repository is checked out by Netlify, the .env does not exist because for good reason it is in .gitignore.
Solution
Store your API_KEY in the Netlify build environment variables and build the .env using a script prior to running the build command.
scripts/create-env.js
const fs = require('fs')
fs.writeFileSync('./.env', `API_KEY=${process.env.API_KEY}\n`)
Run the script as part of your build
node ./scripts/create-env.js && <your_existing_webpack_build_command>
Caveats & Recommendations
Do not use this method with a public facing repository [open] because any PR or branch deploy could create a simple script into your code to expose the API_KEY
The example script above is for simplicity so, make any script you use be able to error out with a code other than 0 so if the script fails the deploy will fail.
You can set Dotenv-webpack to load system environment variables as well as those you have declared in your .env file by doing the following:
plugins: [
new Dotenv({
systemvars: true
})
]
I.e Setting the systemvars attribute of your webpack dotenv plugin to true.
Note that system environment variables with the same name will overwrite those defined in your .env file.
Source: https://www.npmjs.com/package/dotenv-webpack#properties
if you go to corresponding site's settings in Netlify, under build&deploy you can find a section called environment variables you can easily add your environment variables from there. if you add MY_API_KEY variable to environment variables you will be able to access it inside your project via process.env.MY_API_KEY.
If you're using Nuxt JS there is a more "straight forward" approach.
Just edit the nuxt.config.js like so:
module.exports = {
env: {
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY
},
// ...
Then add the GOOGLE_API_KEY to Netlify through the build environment variables as usual.
Credit goes to yann-linn and his answer on github.
What you can also do is also to define a global constant in Webpack. Netlify environment variables defined in UI will work with it. You don't need dotenv or dotenv-webpack.
webpack.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.DefinePlugin({
"process.env.API_KEY": JSON.stringify(process.env.API_KEY)
}),
]
}
However again, of course you shouldn't do it just inputting enviornmental variables in the frontend if your API key is confidential and project public. The API key will appear in the source code of the website and will be easily accessible for everyone visiting it. Lambda function would be a better option.
You can use the Netlify's config file also ...
You can find documentation here.
Also i wanted to have the same ENV variables with with different values per branch/environment.
This workaround worked for me:
Create a netlify.toml file like:
[build]
NUXT_ENV_BASE_API = "/api"
NUXT_ENV_HOST_DOMAIN = "https://your-domain.gr"
[context.branch-deploy]
environment = { NUXT_ENV_BASE_API = "/dev-api", NUXT_ENV_HOST_DOMAIN = "https://dev.your-domain.gr" }
[context.production]
environment = { NUXT_ENV_BASE_API = "/api", NUXT_ENV_HOST_DOMAIN = "https://your-domain.gr" }
And deploy in Netlify ...
I have ASP.NET Core (2.1) project that has appsettings.json. I use WebHost.CreateDefaultBuilder(). The appsettings.json file has following configuration in File Properties:
Build Action: Content
Copy to Output Directory: Do not copy
After build the appsettings.json ends up in bin\Debug\netcoreapp2.1\MyProj.runtimeconfig.json.
The ASP.NET Core runtime loads it fine.
I created WebJobs (for .Net Core 2.1) and wanted to do the same - set Build Action to Content and let it loaded. In the Main() of Program.cs I have code like
var builder = new HostBuilder()
...
.ConfigureAppConfiguration(b =>
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
b.SetBasePath(Directory.GetCurrentDirectory());
b.AddJsonFile("appsettings.json", false, true);
b.AddJsonFile($"appsettings.{environment}.json", true, true);
b.AddEnvironmentVariables();
// Adding command line as a configuration source
if (args != null)
{
b.AddCommandLine(args);
}
}
But the runtime tries to load appsettings.json (instead of MyWebJobProj.runtimeconfig.json). So I had to set Build Action to None and Copy to Output Directory to Always.
However I would prefer the same approach like in ASP.NET Core - it handles somehow the file name transformation. Although in WebHost.CreateDefaultBuilder() is basically the same code like I have in my WebJob. What does the magic file name transformation in the configuration and why it works only in one type of project?
The file [ProjName].runtimeconfig.json has a completely different meaning than appsettings.json. Ensure that appsettings.json was copied to output (set 'Copy to output' to 'always' or 'newer').
I'm using the sbt run command to run my project. My project uses the Logback logging mechanism and if I would like to enable logging, then I have to use the following command:
sbt -Dlogback.configurationFile=/path/to/log/file/app-logger.xml run
Is there a way that I could set this programmatically? I mean I would like to just say
sbt run
and it picks up automagically the app-logger.xml by itself via the application.
This is how I do it!
def loadLogger() = Option(System.getProperty("logback.configurationFile")) match {
case Some(logXml) =>
logger.info(s"using logger $logXml")
case None =>
val path = s"${System.getProperty("user.dir")}/conf/app-logger.xml"
System.setProperty("logback.configurationFile", path)
logger.info(s"using logger $path")
}
OK so we have something that is currently using OpenExeConfiguration for reading a config file, however this doesn't work when running in the web context.
I've tried a variety of different ways of opening the web.config programmatically but I can't seem to get it to read the correct web.config file. In case it matters I am currently debugging it in VS 2008.
1. config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(System.Web.HttpContext.Current.Request.ApplicationPath);
2. config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap { ExeConfigFilename = "web.config" }, ConfigurationUserLevel.None);
3. config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
4. config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
5. System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
It either opens up the wrong config file (either the machine config, or the VS /IDE/Web.config) or complains about the error:
{System.Configuration.ConfigurationErrorsException: An error occurred loading a configuration file: Failed to map the path '/'. ---> System.InvalidOperationException: Failed to map the path '/'.
Edit -
OK so a combination of
config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
AND running Visual Studio 2008 As Administrator worked. Am hoping we don't run into security/permission issues when we deploy to our web server / client environments!
So in the end I used this code (had to handle whether the web application was running, or if our unit test code was running).
System.Configuration.Configuration config = null;
if (System.Web.HttpContext.Current != null && !System.Web.HttpContext.Current.Request.PhysicalPath.Equals(string.Empty))
config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
else
config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
Also have to be running Visual Studio in Administrator mode - which I found out you can set as a property on your shortcut so you don't need to remember each time in Windows 7 to right click and run as administrator :)