I have an .net core 2.0 application, having multiple appsettings files. I have appsettings.json, appsettings.Development.json and a custom appsettings.Secure.json.
//appSettings.json
{
"AzureAd" : {
"ClientId" : "CID"
}
}
//appSettings.Development.json
{
"AzureAd" : {
"Random" : "random2"
}
}
//appSettings.Secure.json
{
"AzureAd" : {
"ClientSecret" : "CSECRET"
}
}
I always want my appSettings.Secure.json configs to be loaded. Here's how the json files are configured.
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional : false)
.AddJsonFile("appSettings.Development.json")
.AddJsonFile("appsettings.Secure.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
The problem is that my "AzureAd:ClientSecret" gets loaded in the config (gets listed in the Providers section of the config object) but when I inject IOption, the ClientSecret property is null.
AzureAdOptions is the class to which the properties are mapped, and it's registered as follows:
services.Configure<AzureAdOptions>(Configuration.GetSection("AzureAd"));
JSON is not secure in any way, especially so considering that this is likely being committed to your source control.
The appsettings.json file is for generic, environment in-specific config. Then, you should have appsettings.{environment}.json files for each environment, but still no secrets should go in any of these files. In development, you can utilize User Secrets to store your secrets. In production, you should be using something like Azure Key Vault and/or environment variables for secrets.
Long and short, your appsettings.Secure.json file should not exist in the first place. It's not an environment and it's not secure.
Related
Most of our ECS Services are not in the "new" format that allow tags to be set.
We recently added default tags to our aws provider e.g.:
provider "aws" {
region = local.workspace["aws_region"]
profile = local.workspace["aws_profile"]
default_tags {
tags = {
Environment = local.workspace["releaseStage"]
Owner = "terraform"
Project = "infrastructure"
Account = local.workspace["releaseStage"]
}
}
}
However, if we run terraform apply, it barks at ecs service resources as they don't support tagging:
Error: error updating ECS Service (arn:aws:ecs:us-east-1:xxxx:service/myservice) tags: error tagging resource (arn:aws:ecs:us-east-1:xxxx:service/myservice): InvalidParameterException: Long arn format must be used for tagging operations
If I override the tags in the resource e.g.:
resource "aws_ecs_service" "myservice" {
name = "myservice"
...
tags = {
Environment = ""
Owner = ""
Project = ""
Account = ""
}
}
It works, but I never get a clean terraform plan as it always needs to evaluate the merged tags.
Is there a way to exclude tagging of default_tags with certain resources?
You should be able to use lifecycle block with ignore_changes to tags which will exclude any external changes. I'm not sure whether this would work. But you can try.
I migrate a WebJobs project to 3.0 and I've run into a peculiar issue. My project has an appsettings.json and various appsettings.environment.json files.
When running under any environment, instead of the environment settings overriding the base settings, the reverse is occurring: When any settings exist in the base settings and environment settings, the base settings is used.
I've tried variations of using HostBuilder.ConfigureAppConfiguration() and HostBuilder.ConfigureHostConfiguration() and in each case I notice something peculiar when I look at hostInstance.Configuration.Providers: There are always three instances of JsonConfigurationProvider, with either two inside ChainedConfigurationProvider when configuring host or just as part of the main provider array when using application. The first instance is always my base, the second is always the environment and the third is always the base again.
So I believe my problem is that this third JsonConfigurationProvider is getting added, but I don't know how it is getting added.
Here is the relevant code from my WebJob:
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
?? Environment.GetEnvironmentVariable("ENV")
?? "development";
var builder = new HostBuilder()
.UseEnvironment(envName)
.ConfigureAppConfiguration(b =>
{
b.SetBasePath(Environment.CurrentDirectory)
.AddCommandLine(args, StartupSettings.SwitchMapping)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{envName}.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
});
builder.ConfigureWebJobs((context, b) =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
b.AddTimers();
});
builder.ConfigureLogging((context, b) =>
{
if (false && context.HostingEnvironment.IsDevelopment())
{
b.SetMinimumLevel(LogLevel.Debug);
}
else
{
b.SetMinimumLevel(LogLevel.Information);
b.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning);
}
b.AddConsole();
var applicationInsightsKey = context.Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
b.AddApplicationInsights(o => o.InstrumentationKey = applicationInsightsKey);
})
.UseConsoleLifetime();
builder.ConfigureServices((context, services) =>
{
/*...*/
});
return builder.Build();
Just figured it out.
The extension method for ConfigureWebJobs() calls ConfigureAppConfiguration() and automatically adds appsettings. Since I was calling it after calling ConfigureAppConfiguration() that was the reason the last appsettings.json was the base one.
Code from WebJobsHostBuilderExtensions.ConfigureWebJobs:
builder.ConfigureAppConfiguration(config =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddEnvironmentVariables();
});
The worst part is that these seems so superfluous.
I have done this in .NET Web Forms for years:
Dim conn As New SqlConnection(f1.fUseThisConnection(Server.MachineName))
Public Function fUseThisConnection(ByVal strThisMachine As String) As String
If strThisMachine = "T61" Then
fUseThisConnection = ConfigurationManager.AppSettings("DevHome")
Else
fUseThisConnection = ConfigurationManager.AppSettings("Production")
End If
End Function
And the AppSettings are in Web.config:
<appSettings>
<add key="Production" value="server=xxx;uid=xxx;pwd=xxx;database=xxx;pooling=false" />
<add key="DevHome" value="server=xxx;uid=xxx;pwd=xxx;database=xxx;pooling=false" />
But I have yet to search and find anything so simple for my C# Razor Pages. Anything from Microsoft howto's just boggles my mind.
Here is my appSetting.json in my Razor Pages project:
"ConnectionStrings": {
"DefaultConnection": "Data Source=Txx\\SQLxxx;Initial Catalog=xxx;Persist Security Info=True",
"Production": "Data Source=xxx;Initial Catalog=xxx;Integrated Security=True;User ID=xxx;Password=xxx"
},
And here is where I reference the connections string--which is hardcoded--but I am hoping there is a way to do an "If" statement: "If environment= whatever, then get this connection string".
services.AddDbContext<MyDbContext>(
options => { options.UseSqlServer(#"Data Source=Txxx;Initial Catalog=xxx;Integrated Security=True"); });
Just check the configuration documentation, I suppose you are using Asp Net Core, and you are adding Mvc in your startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMvc();
...
}
By default, it will try to load 2 configurations files, appsettings.json and appsettings.{Environment}.json, the latest overrides the contents from the final configuration, and also by default, core already has two environments, development and production, all depends in which profile you are using (in visual studio by default will be development).
So, your config file could be renamed to appsettings.json, then you declare your connection strings for production, and create a file appsettings.Development.json were you can override the connection with the development connection string.
You can also load more files o change the default ones by using something like this
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddInMemoryCollection(arrayDict);
config.AddJsonFile("json_array.json", optional: false, reloadOnChange: false);
config.AddJsonFile("starship.json", optional: false, reloadOnChange: false);
config.AddXmlFile("tvshow.xml", optional: false, reloadOnChange: false);
config.AddEFConfiguration(options => options.UseInMemoryDatabase("InMemoryDb"));
config.AddCommandLine(args);
})
.UseStartup<Startup>();
If you want the environment variable
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT ")
But please, consider being more secure
For sensible data something more secure could be to configure your connection string as environment variable in your server, this way it can be removed from your code and keep more private.
This and other solution using a secret manager can be found in the secrets documentation.
Edit
Otherwise, it defaults to the "appSettings.json" file (this would be
in production)
No, it will not load one or another, first it will load appsettings.json, this is always, no matter what Environment you have.
Then, after it loads appsettings.json it will try to load appsettings.{Environment}.json and it will add the keys that were not there before, and it will override an old key if exists.
If you have in appSettings
{
"SomeConfigJustInAppSettings": "some value",
"ThisWillBeOverride": "some value"
}
And your appsettings.Development.json
{
"SomeConfigJustInDev": "some other value",
"ThisWillBeOverride": "some other value"
}
Your config in dev would ended up being:
{
"SomeConfigJustInAppSettings": "some value",
"SomeConfigJustInDev": "some other value",
"ThisWillBeOverride": "some other value"
}
what determines what "ASPNETCORE_ENVIRONMENT" holds?
Is an environmental variable, is set in the Operative System. Also, for development Visual studio has the capacity to preload some variables for when you are developing your app, just check your launchSettings.json, example:
{
"profiles": {
"MyProject": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:50051"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
}
}
}
Specially this part
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
It would be nice to just find what the server name is and use that.
Because I would only check for my dev box servername, everything else
would mean production
Right now it is even more simple, if the variable does not exist, it just loads the appsettings which could contain you prod settings. When you are developing, visual studio (or VS code, or Rider), are already adding the variable with the value Development, so all you need to do is to add the file appsettings.Development.json.
Hell, you can even just create a new project, the default project layout already creates this two files for you!
In the configuration I have to specify the paths to .js and .ts files defining entities:
MikroORM.init({
...
entitiesDirs: ["build/entities"],
entitiesDirsTs: ["src/entities"],
});
So, when I will go to release or distribute the application. Will I need distribute the typescript code too? or will I need distribute only the cache generated? or will I need distribute both? or... none?
As of MikroORM v2.2
Now you can work with default metadata provider, it will require entity source files only if you do not provide entity or type options in your decorators (you can use entity callback to use reference to entity class instead of using string name in type, handle for refactoring via IDE like webstorm).
Original answer:
You should ship the typescript code too, and let the cache regenerate on the server - cache would be rebuilt anyway as it checks absolute path to cached entity for invalidation.
You could implement your own cache adapter or metadata provider to get around this, if you don't want to ship the typescript code.
This is how you could implement custom metadata provider that simply throws error when the type option is missing:
import { MetadataProvider, Utils } from 'mikro-orm';
import { EntityMetadata } from 'mikro-orm/dist/decorators';
export class SimpleMetadataProvider extends MetadataProvider {
async loadEntityMetadata(meta: EntityMetadata, name: string): Promise<void> {
// init types and column names
Object.values(meta.properties).forEach(prop => {
if (prop.entity) {
prop.type = Utils.className(prop.entity());
} else if (!prop.type) {
throw new Error(`type is missing for ${meta.name}.${prop.name}`)
}
});
}
}
Then provide this class when initializing:
const orm = await MikroORM.init({
// ...
metadataProvider: SimpleMetadataProvider,
});
The value of type should be JS types, like string/number/Date... You can observe your cached metadata to be sure what values should be there.
Also keep in mind that without TS metadata provider, you will need to specify entity type in #ManyToOne decorator too (either via entity callback, or as a string via type).
I have a set of terraform codes in a directory called myproject:
\myproject\ec2.tf
\myproject\provider.tf
\myproject\s3.tf
....
the provider.tf shows:
provider "aws" {
region = "us-west-1"
profile = "default"
}
so, if I terraform apply in myproject folder, a set of aws resources are launched in us-west-1 under my account.
Now I want to introduce a AWS Glue resource, which is only available in a different region us-west-2. then how do I layout glue.tf file?
Currently I store it in a sub-directory under myproject and run terraform apply in that sub-directory i.e.
\myproject\glue\glue.tf
\myproject\glue\another_provider.tf
another_provider.tf is:
provider "aws" {
region = "us-west-2"
profile = "default"
}
Is it the only way to store a file launching resources in different regions? any better way?
If there is no better way, then I need to have another backend file in glue sub-folder as well, besides, some common variables in myproject directory cannot be shared.
--------- update:
I followed the link posted by Phuong Nguyen,
provider "aws" {
region = "us-west-1"
profile = "default"
}
provider "aws" {
alias = "oregon"
region = "us-west-2"
profile = "default"
}
resource "aws_glue_connection" "example" {
provider = "aws.oregon"
....
}
But I saw:
Error: aws_glue_connection.example: Provider doesn't support resource: aws_glue_connection
you can use provider alias to define multiple providers, .e.g.
# this is default provider
provider "aws" {
region = "us-west-1"
profile = "default"
}
# additional provider
provider "aws" {
alias = "west-2"
region = "us-west-2"
profile = "default"
}
and then in your glue.tf, you can refer to alias provider as:
resource "aws_glue_job" "example" {
provider = "aws.west-2"
# ...
}
More details at Multiple Provider Instances section: https://www.terraform.io/docs/configuration/providers.html
Read my comment ...
Which basically means that you should keep out aws profiles and regions and what not from your terraform code as much as possible and use them as configuration as follows:
terraform {
required_version = "1.0.1"
required_providers {
aws = {
version = ">= 3.56.0"
source = "hashicorp/aws"
}
}
backend "s3" {}
}
provider "aws" {
region = var.region
profile = var.profile
}
Than use tfvars configuration files:
cat cnf/env/spe/prd/tf/03-static-website.backend-config.tfvars
profile = "prd-spe-rcr-web"
region = "eu-north-1"
bucket = "prd-bucket-spe"
foobar = "baz"
which you will apply during the terraform plan and apply calls as follows:
terraform -chdir=$tf_code_path plan -var-file=<<line-one-^^^>>.tfvars
terraform -chdir=$tf_code_path plan -var-file=<<like-the-one-^^^>>.tfvars -auto-approve
As a rule of thumb you SHOULD separate your code and configuration always, the more mixed they are the deeper you will get into troubles ... this applies to ANY programming language / project etc. Now some wise heads will argue that terraform code is in itself configuration , but no it is not. The terraform code in your application is the declarative source code, which is used to provision your binary infrastructure used by the application source code etc. in your application ...