Dynamically change config files in .Net Application based on environments through Bamboo - c#

I have a windows service written in .net c#. We have three different environments say dev, test and prod. I also have 3 different config files for 3 environments say devAppConfig , TestAppConfig and ProdAppConfig.
Earlier, we used to deploy manual, so we used to replace the config files and deploy the binaries.Now the deployments are to be automated for that we are using Bamboo.
Now my question is how do I dynamically change the AppConfig for different environment deployments.
I have 3 different stages in Bamboo naming DevDeploy , TestDeploy, ProdDeploy. When I run these stages, it has to change the config file and do the deployment, but I'm not sure how.
Can anyone guide me in the right direction for my issue?

I suppose this would work:
Create few configuration files in some directory in the solution in the format Config.ConfigurationName.xml, eg Config.DEV.xml, Config.Test.xml...
Add PreBuild event (by the csproj properties window or manually in the csproj file), for example: <PropertyGroup><PreBuildEvent>xcopy /y (ProjectDir)\Configs\Config.$(Configuration).xml $(ProjectDir)\CONFIG\Config.xml</PreBuildEvent></PropertyGroup>
or in VS rigth click on the project -> Properties:
Add few configurations in VS in Configuration Manager for different configuration eg. DEV, Test.
My project configuration:
Execute in the Bamboo msbuild with arguments: /p:SolutionConfiguration=%CONFIGURATION% where %CONFIGURATION% is a variable in Bamboo saying on which ENV you are deploying, eg DEV, Test etc.

Related

Azure DevOps XML Transformation During Release Not Working

I want to configure our pipeline to allow one build to be used for multiple environments without having to create separate builds. According to the docs, it seems like it is possible, as it says:
You can use this technique to create a default package and deploy it to multiple stages.
I named my stage as my environment (preview), and I created a web.config file for that environment (web.preview.config) file. All my environment configuration files in the same path as Web.Config file.
The logs say transformation was complete:
2018-11-17T00:26:52.0383966Z [command]D:\a_tasks\AzureRmWebAppDeployment_497d490f-eea7-4f2b-ab94-48d9c1acdcb1\3.4.13\ctt\ctt.exe s:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.config t:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.Release.config d:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.config pw i
2018-11-17T00:26:52.4335280Z [command]D:\a_tasks\AzureRmWebAppDeployment_497d490f-eea7-4f2b-ab94-48d9c1acdcb1\3.4.13\ctt\ctt.exe s:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.config t:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.Preview.config d:D:\a_temp\temp_web_package_06958915987488234\Content\D_C\a\1\s\Microsoft.Xbox.Mvp\Microsoft.Xbox.Mvp.Api\obj\Preview\Package\PackageTmp\bin\Web.config pw i
2018-11-17T00:26:52.5443873Z XML Transformations applied successfully
I can see that it first transformed to release and then it applied preview as the doc says (release then environment). However, although it says XML Transformations applied successfully, when I check the config variables, they are not changed. The only way I could make the transformation work was to define the buildConfiguration variable when I queue a new build, which blocks me from using the same build for different environments.
When I was researching, I found this from this link:
Web.config is transformed during the build process, if you generate the deployment package from "Build" and then deploy it in "Release", then you cannot transform it before deployment.
But the doc said I can use one default package for multiple stages...Does that still mean I have to create separate build for each environment? Is XML transformation not what I should be looking at for the scenario I wanna solve?
Thank you in advance!
++ Edit:
Release Settings:
Release steps (I think? I have a strong feeling that this is what you are looking for...):
1) Make sure you transform works. Test it
here.
2) Ensure in your VS project that you are including the transform file, Web.Preview.config, and copying to output dir.
3) Disable the config transform during the build, you just need to add argument /p:TransformWebConfigEnabled=False in MSBuild Arguments section of your Build task. You also need to add /p:AutoParameterizationWebConfigConnectionStrings=False if you want to update the connection string during the release. This will use the Web.Preview.config to "transform" the web.config.
4) Double check that in your release for the IIS Web App Deploy task under File Transforms & Variable Substitution Options you have XML transformation checked.
None of the answers I found on the internet worked for me on their own for my build and release pipeline. The web.config I got from the release pipeline was always pointing to the non transformed values.
After a few hours pulling my hair I got it to work though.
Some short info about my setup
I want to be able to deploy on all environments with just one build and one release pipeline.
My setup:
One build pipeline that builds all of our standard branches (test,
release, master).
One release pipeline that has different stages
depending on branch that started the release.
Our test stage releases the test branch on our test server.
Stage/Production comes from the same release branch but have their own transform files.
Solution
I followed some of the guide from Microsoft and set up my web.<environment_name>.config to match the release stage names.
I did not need to remove the <Dependent Upon> rows from my .csproj for each transform. Instead all I did was set the property Build Action of each transform to Content as shown by the image bellow.
I then added these commands to the build pipeline's Build Solution -> MSBuild Arguments:
/p:MarkWebConfigAssistFilesAsExclude=false
/p:TransformWebConfigEnabled=false
/p:AutoParameterizationWebConfigConnectionStrings=False
The build now does not try to transform the .config on it's own and also does not exclude the transform files from the artifact, allowing the release pipeline to do the transformation instead. Also, keeping the <Dependent On> for the transform files lets us have a "cleaner" look inside our code editors.
I just got this working so I could have one build with deployment to multiple environments. This is what I did.
In the code, I set each Web.<Environment>.config property to Build Action = "Content". I also set all mine to Copy to Output Directory = "Copy Always". I also unloaded the project and edit the csproj file, then removed the <DependendUpon>Web.config</DependentUpon> lines. This dumps all your web.configs to the root (no file nesting).
In the build, I set pipeline variable BuildConfiguration = "Release". I don't have a Web.Release.config in my project.
In the release, I named the deployment stage after the environment (in my case, Development, Staging, and Production). In all stages, on the Azure deployment task, I checked the XML transform checkbox.
In Azure, I set the ASPNETCORE_ENVIRONMENT to the naming of the staging environment, in my case, Development, Staging, and Production).
I just got this working as well. My issue was actually at the Visual Studio Solution level. I had the MVC project pointed to a different Configuration than the others. So always double check the configs!

web.config not app.config being published to xxx.dll.config in Visual Studio 2015

I have an ASP.NET project that outputs a class library as a dll which is then deployed to IIS. Per standard builds a config file is also output along with the dll as xxx.dll.config and it contains many useful environment variables such as connection strings (i.e. it conforms to the application settings architecture requirements).
In VS 2012 the contents of xxx.dll.config file were sourced from app.config in the project root directory.
In VS 2015 it appears xxx.dll.config's contents are instead sourced from web.config in the project root directory.
Steps to reproduce:
Create a ASP.NET application with VS2015.
Add a app.config file in this application.
Build this application.
Open the bin folder and find the application.dll.config file and open it. The content is the same with Web.config.
Do the same operations from Step 1 to Step 4 in Visual Studio 2012, the content is the same with App.config.
This is causing me a bit of a headache as I was relying on app.config to hold variables (connection strings, auth providers) for my functional tests while web.config held variables for deployed code.
Most of the reading I've done on AppDomains and their configuration variables seems to indicate that web.config shouldn't factor into the build output unless explicitly instructed and even then it isn't exactly easy to transform a web.config file, nevermind have it end up as the xxx.dll.config file.
I could rework my test fixtures to set the environment variables directly from the tests but for things like ASP.Identity that requires writing quite a few new classes to load a RoleManager and associated providers which I'd like to avoid.
If anyone could suggest how to engineer the VS build to output a config file that is easily used by the assembly AND the nunit tests when run via the nunit runner or Resharper that would be awesome.
To restore the original behaviour of Visual Studio 2015 back to what I was accustomed to in Visual Studio 2012 I added a post build event:
Go to Project-> Properties
Select Build Events
Add the following to the "Post-build event command line" :
copy /Y "$(ProjectDir)\App.config" "$(TargetDir)\$(TargetName).dll.config"
The .dll.config is now once again the App.config instead of Web.config

Using Go CD with .NET

I would like to use Go CD with .NET Web API projects and Git.
I like the idea of promoting a specific build artifact to some environments via pipelines. Currently we're using TeamCity and branching in Git. We use MSBuild to build and deploy with a specific Config Transform (Test, Staging, Live).
Our MSBuild command line arguments are the following:
/p:Configuration=%DeployConfiguration% /p:Platform=AnyCPU /t:WebPublish /p:WebPublishMethod=FileSystem /p:DeleteExistingFiles=True /p:publishUrl=%DeployPath%
%DeployConfiguration% is a parameter for each environment; Test, Staging, Live and uses .NET Config Transforms to transform the Web.config for a specified environment.
%DeployPath% is a parameter for each project for each environment. So AuthAPI Live would be something like: \\liveServer\path\to\AuthAPI\.
My one question regarding this is: How to use a specific environment config (using .NET Config Transforms) when the build artifact is promoted to the next stage in the pipeline? So for example, when the build is promoted to testing environment I want to use Web.Test.config, but when promoted to staging environment I want to use Web.Staging.config, etc.
Also I would like to know if you have any experience using Go CD with .NET projects?
I have not experience using Go CD with .NET projects, but maybe my advise will be helpfull.
1.Create TransformStaging.csproj file with content:
<Project ToolsVersion="4.0" DefaultTargets="Transform" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildProjectDirectory)\MSBuild\Microsoft.Web.Publishing.targets" />
<Target Name="Transform">
<Copy SourceFiles="$(Source)" DestinationFiles="$(Source).tmp"/>
<TransformXml Source="$(Source).tmp" Transform="Web.Staging.config" Destination="$(Source)" />
</Target>
</Project>
2.Call msbuild with
msbuild.exe TransformStaging.csproj /p:Source=$configFilePath
There are parts of configuration that are environment specific (you will have a different db connection for Dev,QA, UAT, Production environments for example), and then there are configurations settings that do not change from one environment to the other, lets call these application specific config values. Example of those would be perhaps you setup a logger component and disregarding what environment app is in you want the log files to go to a certain folder, c:\logs.
I would suggest keep application specific settings in your App.Config file (these are deployed from one env to other using xcopy), but the ones that are environment specific, take out. Separate deployment of environment configuration from your app deployment. These should be deployed to machine when it is built/configured.
Environment specific variables can be set stored in OS Environment Variables, Machine.config, known file location etc.
That way your app just has to query: Environment.GetEnvironmentVariable("MyAppDBName")
And Dev, QA, and Production environments/servers will have different answers.
And you will be deploying the same binaries across each environment.
Nermin Dibek is pointing the correct way to do things, but I will like to add something more specific to his answer.
I implemented the process in my organization and after you migrate to Environment Variables (With this you avoid commit the Configuration file that contains Secret Keys(Security Leaks)), in GoCD there is a place call environment that will allow you to customize the pipeline where you are running. For example in my Org I have Staging env, Testing env, Prod and you can go on as many as you like, this give you a nice feature because you can assign to differents environment different agents and different pipelines, for example we are running only unit test in Testing so I put the slowest agent for that task.

How to pass a parameter to TeamCity Test Runner?

How to pass a parameter to a TeamCity Test Runner?
I've added a build configuration to my teamcity project which copies out a test project from TFS into a local location on the server then it runs the tests with NUnit and display the results.
The problem is that when the tests run from my local pc, they should be using some configurations, when they run from Dev Automated Build and Test Automated Build, they should be using other configurations and all should be in TFS.
For example, BaseUrl and Connection string configurations...
private static readonly string BaseUrl = ConfigurationManager.AppSettings[AppSettingKey.BaseUrl];
Currently, I've added a web.config file to my project which works fine only locally.
So, how to pass these parameters to a TeamCity Build Configuration which is configured on Dev and Test environemnts?
Even if I create separate web.config files (e.g. dev.web.config and staging.web.config), I need a way to tell the Build Configuration to use which configuration file?
Hope the question is clear.
many thanks.
Enlightened, found a solution (should have thought more before submiting a question here)!
I'd just create separate dev.app.config and staging.app.config files then on the "Version Control Settings" page on teamcity I can define rules such as copy this file somewhere.
So I just Copy the file onto the root app.config; that's it!
Rule:
+: SolutionName\Tests\ConfigFiles\Dev\App.config=>SolutionName\Tests\App.config
Consider using the configuration parameter i.e. Build | Release
Use your debug build locally and release on TeamCity. You can do this with an MSBuild parameter (/p:Configuration=Release)
Then use the solution here (How to select different app.config for several build configurations) to use the correct app.config.
I prefer this solution as it keeps the definitions within the solution / projects rather than within the build.
[I'm adding this just for completeness - I had originally followed this approach but then found the other solution]
Rules in Version Control Settings works only for directories so the better approach is to use Build step - command line
Go to Build Step in Settings section
and Add Build Step with following settings
Command Line
Custom script: copy TeamCity.App.config App.config
Execute: Only if all previous steps were successful
Reorder this step and run it before compilation step

Using separate MSI build for config files for each environment

For one client, I have to deliver a build they use in QA and production. The checksum of the build file has to match - it cannot change at all between QA and production. The configuration for each environment is different, so I have a build that contains just the code and then a separate build for each environment that contains just the environment specific config files. The config file builds puts the files in the same location regardless of environment, so the code can always load c:\myapp\myconfig.xml which will contain the settings for that environment. Most articles I read about this (such as Scott Hanselman's) involve different builds for each environment, but this won't work because the checksum values will be different. Should I deploy these config files some other way, or do I have a viable solution? The one problem with my current solution is it requires multiple config files that are nearly identical but not quite. One change therefore has to be added to multiple files, which is something I would like to avoid, but I don't know how to do it except at build time or with an external script that copies the proper file.
Not sure if your setup is more complicated, but we have a similar problem and we added a custom actions class that updates the config files based on the environment (which the user selects during installation), then you add this custom actions project to your setup project. That way you use one setup exe no matter what environment you're installing to.
Let me know if you're interested and I can post some samples or more info on how we accomplished it.
Here are some more details:
Add a new dialog to your Setup project to request the environment from the user (we use a 4 radio button dialog with the 4 environments we have: dev, qa, staging and production)
Configure the values of the 4 radio buttons and the property that this value will be setting i.e. "environment" (to latter be used by the CustomActions class)
Add a dll project to your solution with a single class (CustomActions)
in the CustomAction class, you read the property we configured in step two as:
if(!this.Context.Parameters.ContainsKey("environment"))
{
string error = "'environment' argument is null. Please configure config file manually";
//...handle your error, etc.
return;
}
string env = this.Context.Parameters["environment"];
now your env variable contains the value we assigned to each radio button in step. You then can use a switch statement to decide what environment the user selected. and update your config file appropriately with:
Configuration config = ConfigurationManager.OpenExeConfiguration(this.servicePath);
//for example, to change your connection strings you'd use:
config.ConnectionStrings.ConnectionStrings["oracle"] = "dev conn string here";
Back in your setup project, add the output of the CustomActions project to your CustomActions editor (View menu -> Editor -> Custom Actions)
Finally, configure the CustomActionData property of your setup project to pass the environment and other variables to the CustomAction class (mine looks something like this:
/serviceFolder="[TARGETDIR]\" /serviceExe="blahblah.exe" /serviceName="MyServiceName" /environment="[ENVIRONMENT]"
Hope that makes sense and applies to your solution!
Assuming:
1) XML config files
2) Total number of changes between builds are few (even if copied to many config files)
I would have the installer handle updating the config files at install time
For instance we use WiX v3 for our installer and update several config files with values during install using the XmlFile element
<util:XmlFile Action="setValue" File="[DIRECTORIES.WEBSERVICES]web.config" ElementPath="//configuration/system.web/compilation" Name="debug" Value="false" Permanent="yes" />

Categories

Resources