LP13
LP13

Reputation: 34079

How to publish environment specific appsettings in .Net core app?

I have 3 environment specific appsettings files in my .Net core application

enter image description here

in project.json I have setup publishOptions like this. ( based on suggestion here)

"publishOptions": {
    "include": [
      "wwwroot",      
      "appsettings.development.json",
      "appsettings.staging.json",
      "appsettings.production.json",
      "web.config"
    ]
  },

I have 3 corresponding startup classes that uses appropriate appsettings based on environment

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true);

However when I publish the application then all 3 appsettings files end up in all environments. How do I publish environment specific appsetting file?

Upvotes: 102

Views: 93816

Answers (19)

Alan Biegun
Alan Biegun

Reputation: 21

My solution (tested in .Net 8) just in case anyone else whats to try is to add this to csproj:

No additional Startup/Program.cs code required!

<Target Name="RenameAppsettings" AfterTargets="Build" Condition="'$(Configuration)' == 'dev' or '$(Configuration)' == 'sit' or '$(Configuration)' == 'uat' or '$(Configuration)' == 'production'">
    <Move SourceFiles="$(ProjectDir)$(OutDir)appsettings.$(Configuration).json" DestinationFiles="$(ProjectDir)$(OutDir)appsettings.json" />

    <Delete Files="$(ProjectDir)$(OutDir)appsettings.dev.json"  Condition=" '$(Configuration)' != 'dev'"></Delete>
    <Delete Files="$(ProjectDir)$(OutDir)appsettings.sit.json"  Condition=" '$(Configuration)' != 'sit'"></Delete>
    <Delete Files="$(ProjectDir)$(OutDir)appsettings.uat.json"  Condition=" '$(Configuration)' != 'uat'"></Delete>
    <Delete Files="$(ProjectDir)$(OutDir)appsettings.production.json"  Condition=" '$(Configuration)' != 'production'"></Delete>
</Target>

I have 5 environments: local, dev, sit, uat, production.

Im renaming appsettings.{env}.json to just appsettings.json, and then remove all other appsettings files.

Also for local environment Im skipping all of this as there is no need to worry about those file during debuging.

Upvotes: 0

Carlos Cruz
Carlos Cruz

Reputation: 1

This work for me:

  1. Create 1 pubxml file per appsettings..json file Example: appsettings.PROD.json - profile-prod.pubxml

  2. Each pubxml file must contain the following, this means only copy the appsettings.json and appsettings..json that are necesary. This example is for my "prod environment". you do the same for all the others environments.

    <ItemGroup> <Content Update="appsettings.json" CopyToPublishDirectory="Always" />
    <Content Update="appsettings.PROD.json" CopyToPublishDirectory="Always" />
</ItemGroup>
  1. Make sure ALL your appsettings.json files are marked as "not copy to output / publish directory"

Resume: As all yours *.json are denied to be copied into the publish directory, when you execute your publish profile, only the ones explicity listed in the pubxml are copied to the publish directory.

Upvotes: 0

ALNAJJAR
ALNAJJAR

Reputation: 483

Go to Program.cs after that add WebApplicationOptions

var webAppOptions = new WebApplicationOptions()
{
    Args = args,

#if PRODUCTION
    EnvironmentName = Environments.Production,
#elif STAGING
    EnvironmentName = Environments.Staging,
#elif DEVELOPMENT
    EnvironmentName = Environments.Development,
#else
    EnvironmentName = Environments.Development,
#endif

};
var builder = WebApplication.CreateBuilder(webAppOptions);

Upvotes: -1

Ben
Ben

Reputation: 397

If someone else is wondering how to use different appsettings for multiple environments here is a possible solution.

dotnet publish --configuration [Debug|Release] will copy the appropriate appsettings.json file into the publish folder if *.csproj has a conditional logic for these files:

  • First in the .pubxml publish profile file (can be found in Properties->PublishProfiles of Visual Studio) disable that all content files are included by default
<PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <EnableDefaultContentItems>false</EnableDefaultContentItems>
</PropertyGroup>
  • Then specify conditional Debug/Release logic
<Choose>
    <When Condition="'$(Configuration)' == 'Debug'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
      </ItemGroup>
    </When>
    <When Condition="'$(Configuration)' == 'Release'">
      <ItemGroup>
        <None Include="appsettings.json" CopyToOutputDirectory="Never" CopyToPublishDirectory="Never" />
        <None Include="appsettings.prod.json" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
      </ItemGroup>
    </When>
</Choose>
  • Finally inside Startup.cs try to load both files
public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile($"appsettings.prod.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

I hope this solution, has been helpful.

Upvotes: 38

Daniel Barnes
Daniel Barnes

Reputation: 387

To use different appsettings.json depending on the publish profile (i.e. having 2 Azure publish destinations, Development & Production) for Dotnet 7:

  1. Create appsettings.(environmentname).json files for each your different publish profiles. e.g. appsettings.Development.json, appsettings.Production.json

  2. In your publish profiles (the .pubxml files in Properties\PublishProfiles), specify the publish profile name as the Environment name (eg. Development or Production) by adding an EnvironmentName in the PropertyGroup:

    XML format in the pubxml file is as follows:

     <PropertyGroup>
     <EnvironmentName>Production</EnvironmentName>
     </PropertyGroup>
    
  3. By default asp.net core will load the appsettings.(environmentname).json file. (more on this here)

    1. To test which environment you are in, you could use the following code:

      Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ??
      Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production";
      

(This is based on this Microsoft document)

Upvotes: 0

Keerthi
Keerthi

Reputation: 41

I tried below configuration in .csproj. I have three Application json file for each environment (Development, Staging, Production). I have updated the .csproj file based on the Environment Variable

<ItemGroup Condition="'$(EnvironmentName)' == 'Development'">
    <Content Update="appsettings.Production.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Staging.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

<ItemGroup Condition="'$(EnvironmentName)' == 'Staging'">
    <Content Update="appsettings.Production.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Development.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

<ItemGroup Condition="'$(EnvironmentName)' == 'Production'">
    <Content Update="appsettings.Staging.json" 
CopyToPublishDirectory="Never"/>
    <Content Update="appsettings.Development.json" 
CopyToPublishDirectory="Never"/>
</ItemGroup>

Hope this works for everyone.

Upvotes: 2

Ankit Garg
Ankit Garg

Reputation: 149

Consider you have multiple appsettings: dev,pre,prod. You can have below configuration in your web project file.

         <!-- This configuration is done for dotnet publish command.
     It will make sure only environment specific files will be copied -->
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Dev'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Pre.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(EnvironmentName)' == 'Pre'">
            <Content Remove="appsettings.Prod.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>
          <ItemGroup Condition=" '$(**EnvironmentName**)' == 'Prod'">
            <Content Remove="appsettings.Pre.json" />
            <Content Remove="appsettings.Dev.json" />
            <Content Remove="appsettings.Development.json" />
          </ItemGroup>
        

This configuration will help during publish. It will not copy the appsettings which is not required for that environment.

EnvironmentName (part of ) highlighted above should be passed as parameter in below command.

        dotnet publish -o ../../published/20191118A -c release /p:EnvironmentName=Development

Upvotes: 4

Shemeemsha R A
Shemeemsha R A

Reputation: 1550

The simple solution is to specify the environment name in publish configuration file (.pubxml). So that you don't need to add it to the Environment Variable and you can host apps with multiple configurations on the same server.

<EnvironmentName>{Enviornment name}</EnvironmentName>

enter image description here

The application will use the corresponding appsettings.json file after publishing it. You can see all the .json files after the publication, but the app will use only the Enviornment settings value.

Upvotes: 15

Ifeoluwa Oluwole
Ifeoluwa Oluwole

Reputation: 1

Updated for versions using .csproj

First, in the .csproj file, add the following to ensure the appsettings.json is included in the publish directory during publish.

  <ItemGroup>
    <None Include="appsettings.json" CopyToPublishDirectory="Always" />
  </ItemGroup>

Then update Startup.cs to include:

public Startup(IHostEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile($"appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    }

Upvotes: 0

Balaji Sadhu
Balaji Sadhu

Reputation: 21

In VS2017 Adding multiple environment

steps right click project --> Add --> NewItem - select json file - write file name as 'appsettings.staging.json' or 'appsettings.production.json'

Add appsettings.staging.json file

Output Looks like

Upvotes: 2

Almas
Almas

Reputation: 144

In my case I have several appSettings files for several live environments, e.g. appSettings.Env1Live.json, appSettings.Env2Live.json, etc.

I looked through the article https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-3.1 and added the following statements in the corresponding publish (pubxml) config files per each environment.

E.g. for PublishToEnvironment1.pubxml added:

<ItemGroup>
<Content Update="appsettings.*Live.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Development.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Env1Live.json" CopyToPublishDirectory="PreserveNewest" /></ItemGroup>

So, in the published folder I have only two necessary files, other appSettings.*Live.json were not published.

appsettings.json

appsettings.Env1Live.json

Upvotes: 3

Sean
Sean

Reputation: 15144

I have this same problem because I need to switch between different production configuration files due to multiple sites using the same codebase.

In the end, I created a powershell script for each client/site. The script copies a client-specific configuration file over the production configuration file, then runs the publish. I actually do this for both the appSettings.json file and the environment.ts file. The script looks a bit like this:

Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "--> config files removed"
Copy-Item -path ClientApp\src\environments\environment.CLIENT-SITE-NAME.ts ClientApp\src\environments\environment.prod.ts
Copy-Item -path appsettings.CLIENT-SITE-NAME.json appsettings.production.json
Write-Output "--> config files copied"
dotnet build MYPROJ.csproj -c Release /p:DeployOnBuild=true /p:PublishProfile=CLIENT-SITE-NAME 
Write-Output "--> built & published"
Remove-Item –path ClientApp\src\environments\environment.prod.ts
Remove-Item –path appsettings.production.json
Write-Output "Finished"

In each of my client sites' .pubxml files, I exclude ALL the non-production appsettings from being published, like so:

<ItemGroup>
  <Content Update="appsettings.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site1.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.site2.json" CopyToPublishDirectory="Never" />
  <Content Update="appsettings.development.json" CopyToPublishDirectory="Never" />
</ItemGroup>

I remove the production files in the end to make sure I don't accidentally deploy them to the wrong site using the Publish wizard.

(I store the password in the pubxml file, but you could include it in the script as a parameter as an alternative)

Upvotes: 2

Sensei_Shoh
Sensei_Shoh

Reputation: 709

I recently had to find a solution for this as well and I accomplished it by adding some settings to the .csproj file and a minor change to Program.cs.

<Project Sdk="Microsoft.NET.Sdk.Web">
    <!-- ... -->
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <EnvironmentName>Development</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Production</EnvironmentName>
    </PropertyGroup>
    <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Stage|AnyCPU'">
        <DebugType>pdbonly</DebugType>
        <Optimize>true</Optimize>
        <EnvironmentName>Staging</EnvironmentName>
    </PropertyGroup>
    <ItemGroup>
        <Content Remove="appsettings.json" />
        <Content Remove="appsettings.*.json" />
    </ItemGroup>
    <ItemGroup>
        <Content Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
        <Content Include="appsettings.*.json" Exclude="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="Never" />
        <Content Include="appsettings.$(EnvironmentName).json" DependentUpon="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
    </ItemGroup>

    <Target Name="RenameAppsettings" AfterTargets="Publish">
        <Move SourceFiles="$(PublishDir)\appsettings.$(EnvironmentName).json" DestinationFiles="$(PublishDir)\appsettings.overrides.json" />
    </Target>
</Project>

To explain it a little, I added an <EnvironmentName> element for each configuration so it can be used during the build process. I'm using appsettings.{EnvironmentName}.json (i.e. appsettings.Staging.json) just as an "overrides" file so I just have it rename the necessary JSON file during the build process. When you run dotnet publish -c Stage, for example, it will publish the appsettings.Staging.json file into the publish folder and rename it to appsettings.overrides.json. In your Program.cs, you will just need to include the appsettings.overrides.json file as well:

    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
    .AddJsonFile($"appsettings.overrides.json", optional: true, reloadOnChange: true)

I hope it helps!

Side note: I include appsettings.*.json and set it to CopyToOutputDirectory="Never" just so they still show up in Visual Studio when developing. Otherwise, if you only want the current environment's appsettings file to show in VS, just remove that line from the csproj file.

Upvotes: 33

Aleksei Popovich
Aleksei Popovich

Reputation: 11

I solved this question with this nuget package: https://github.com/Microsoft/slow-cheetah/blob/master/doc/transforming_files.md

It`s so easy for installation and using all configurations in Active solution configuration (In my case - I added a new "Test" configuration for test deployment).

After this you can install this extension in VS: https://marketplace.visualstudio.com/items?itemName=vscps.SlowCheetah-XMLTransforms.

Just now you can create a new config subfiles for .xml or .json application configuration settings in VS by this tool (as in manual). For example, I has Debug, Test, Release files (appsettings.Debug.json and etc.)

Next step - setting up the publish profiles for each configuration and after publishing you will have only one file with all the necessary transformations.

Transformations works like a classic .json envinronment transormation in .net core web applications.

Upvotes: 1

Gabe Szabo
Gabe Szabo

Reputation: 268

You can use MSBuild conditions to optionally include files in the compilation output (or published output).

<ItemGroup Condition="'$(Configuration)'=='Release'">
  <Content Remove="appsettings.Development.json;appsettings.Staging.json" />
  <None Include="appsettings.Development.json;appsettings.Staging.json" />
</ItemGroup>

The above ignores the Development and Staging appsettings.json file variants when the compilation target configuration is Release.

Upvotes: 8

incognito
incognito

Reputation: 2351

The easiest way i have found so far is to deploy all config files and then remove extra files after the deployment is finished. Just add few extra lines at the end of your deployment shell or batch script.

Upvotes: -1

VMAtm
VMAtm

Reputation: 28355

You need to actually add the environment variables, according the official tutorial:

var builder = new ConfigurationBuilder()
    .SetBasePath(env.ContentRootPath)
    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true)
    // do not forget to add environment variables to your config!
    .AddEnvironmentVariables();

Upvotes: 2

Thomas
Thomas

Reputation: 5230

Honestly I think that is not the right task for a build pipeline. Also the publishing features of the dotnet cli are very limited. Go to external tools like Tseng showed you. Deployment is another domain with an own set of complexity than building.

There is not a build in way on dotnet cli beyond using external tools!

Upvotes: -4

Tseng
Tseng

Reputation: 64131

One possible way would be to run prepublish or postpublic scripts/commands, for example by running an gulp task executing dotnet publish-iis (alternatively use a task in prepublish section of scripts to copy the files to the before publishing.

Add this to your project.json:

"scripts": {
  "postpublish": [ "gulp cleanconfig", "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}

You can also run a cmd or shell command here. But actually there shouldn't be any reasons why you would want to do this in the first place, just ship all 3 appconfig files, because on i.e. Azure App Service, you can switch the mode depending on the environment variables which is regulated via the Azure Portal and when publishing, the staging and production slots will be just swapped, but the environmental variables stay.

You shouldn't store secrets within the appsettings.json though (which I assume you doe and the reason you want to remove the files). Instead, use "user secrets" for development and environmental variables to set connection strings etc. for production. Works like a charm, especially with Azure App Services and docker containers.

Upvotes: 1

Related Questions