VSTS Release pipeline - token substitution - azure-devops

I've been using Replace Tokens (from VS Marketplace) and it served me well so far for the dynamic connection string substitution in the web.config file. The way it works, in the Web.Release.config I have a construction like this:
<connectionStrings>
<add name="Default"
connectionString="__AzureDBConnectionString__"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
When I start release in VSTS, it replaces this token (in between "__") with the variable value in the release pipeline. So, I always have a connection string that corresponds to the relevant environment.
Yesterday I decided to add some more variables into this file, hoping it'll work as well, as I have a lot keys that should be parametrized for security reasons.
Well, if I add to the file something like this:
<appSettings>
<add key="mailToken" value="__mailToken__" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
it doesn't work. In the web.config which is deployed, I see value="mailToken" i.e. it's not substituted with the variable stored in the release pipeline (as for connection string).
After around a day of suffering, I discovered that the only parameter that could be replaced like that is connection string (because this is the only parameter set in auto-generated by the build "Web.SetParameters.xml" file). I tried to play around parameter.xml file as some sources suggest, but no success. It is important that all the variables are taken from the release pipeline variable list (and not hardcoded in config files).
Can someone suggest a proper way to substitute the values in appSetting or another workaround (without Replace Tokens task etc.)?

According to your description, you are using Web Deployment Package (Web.SetParameters.xml), by default the appSettings in Web.Release.config won’t be added to Web.SetParameters.xml file, but will replace the value in web.config file directly when generate the Web Deployment package, so the value of mailToken in web.config file (in zip package file) will be mailToken.
So, the simple way to replace it is using XML variable substitution in File Transforms & Variable Substitution Options section of IIS Web App Deploy or Azure App Service Deploy task. You just need to add the variable with the same name of key value (mailToken) to the release definition and check the XML variable substitution option. (Do not need to configure it in Web.Release.config)
More information, you can refer to: XML variable substitution

I struggled with something similar recently too, only today did I come to a solution. My main hangup was defining the parameters.xml file in my project directory, as this conflicted with some of the auto replacement logic defined in the vsts-tasks release task for web deploy. I'd only define parameters.xml for properties that won't be handled automatically through VSTS.
Also, I'd suggest adding this msbuild parameter to your build job under "Build Solution", this will prevent VSTS from adding connection strings in the setparameters.xml file of your web deploy package.
/p:AutoParameterizationWebConfigConnectionStrings=false
Ultimately if this doesn't work, you'll want to turn on System.debug to true in your environmental variables so you can see exactly what is going on during release. Unfortunately the debug logging you'll see with system.debug on aren't that great, but they may help.
Good luck!

Related

Why web.<environment>.config is present after File Transform in release pipeline

In my release pipeline I am using File Transform option for my web.config.
I have web.config and web.staging.config
File transform is working and changes are applied in web.config but why web.staging.config is still present in the final deployed package.
Am I missing any configuration or this a normal behaviour?
For this issue , with XML file transformation, the web.xxx.config file is required. The transform will be applied when publish web app to web deploy package or publish to server directly per to the configuration.
As stated in the documentation: You need to create a Web Application package with the necessary configuration and transform files.
So if you want to removing all config transforms, as a workaround , you could add a Delete-files task in the end to remove all configuration files. For example:
Source Folder:
$(wwwRoot)\
Contents:
**\*.staging.config
Here is a case with similar issue , you can refer to it for details.

Modifying product version in Wix

I'm trying to bind the file version from my exe file to be used as product version. Following: How can I set the WiX installer version to the current build version?
The problem is that my assembly builds in the format of e.g 2018.0.0.0. The major upgrade requires a version number of max 255, which means that I have to remove the first two numbers from my productVersion variable before setting it to the ProductVersion property. Is there a way to modify the variable through xsl or something else?.
Modifying through a custom action is no alternative since I want the property to be set in the msi file.
Any help in this djungle is appreciated,
<?define productVersion= !(bind.FileVersion.MyExe.Exe) ?>
<Product Id="*"
UpgradeCode="12345678-1234-1234-1234-123456789123"
Name="My Application"
Language ="1033"
Version="$(var.productVersion)"
Manufacturer="My Company" >
If you can't bind/infer from your assembly then you will need to have your build automation pass a wix variable into candle.exe and use that instead of your bind statement.
In a managed code / vsts / tfs environment my typical flow is that the build definition is the source of truth and it increments and sets a buildnumber during the build. A powershell script updates all the AssemblyFileVersion attributes across my AssemblyInfo files based on this and my wixproj (votive/msbuild) does a regex match on this variable to pass it through to candle.

Applying web.config transformations locally

I have a transform for my web.config that currently works if I publish to Azure, but it's not working locally when I build/run. How can I go about "Publishing" locally so the Local transformations are applied to my web.config on my dev box?
Currently I right-click > publish to Azure, do I need to create a new publish profile for my local box? Is there any way to get the transforms to work on Build instead?
It depends on which dev server you are using. As #Citronas said on this answer:
It depends on how you debug. If you are using Cassini, afaik your
web.config contents will be read regardless of the selected solution
configuration (e.g. Debug or Release).
If you are debugging with your
local IIS, it depends on what you have set the path in the IIS to. If
you have set it to your source code directory, you need to write your
local settings into your web.config. If you publish your code into a
local directory and set the IIS path accordingly, you can use
web.config transforms. (You said, that your web.config transforms are
working)
You do not need to publish locally (even though you can do that, publish to local IIS then attach the debugger to the worker process of the application pool) since it is annoying. The easiest way is to simple have your debug settings in the web.config file and then apply transforms for your "publishable" environments.
In short: do not use transforms for debug, only for publishing. Use the regular web.config to debug.

How to modify the csdef defined in a cspkg

To deploy to different azure environments I modify the csdef as part of the compilation step to change the host headers. Doing so requires building the cspkg once for each environment instead of being able to reuse the cspkg and specify different configs for deployment.
I would like to instead modify the csdef file of a cspkg after it has been created, without recompiling. Is that possible, and if so how?
I've done something similar to what you're after to differentiate between test and live environments. First of all you need to create a new .csdef file that you want to use for your alternate settings. This needs to be the complete file as we're just going to swap it out with the original one. Now we need to add this to the cloud project. Right click on the cloud project and select unload project. Right click on it again and select Edit [Name of project]. There's a section that looks a bit like this:
<ItemGroup>
<ServiceConfiguration Include="ServiceConfiguration.Test.cscfg" />
<ServiceDefinition Include="ServiceDefinition.csdef" />
<ServiceConfiguration Include="ServiceConfiguration.cscfg" />
</ItemGroup>
Add a new ServiceDefinition item that points to your newly created file. Now find the following line:
<Import Project="$(CloudExtensionsDir)Microsoft.WindowsAzure.targets" />
Then add this code block, editing the TargeProfile check to be the build configuration you're wanting to use for your alternate and ensuring that it points to your new .csdef file
<Target Name="AfterResolveServiceModel">
<!-- This should be run after it has figured out which definition file to use
but before it's done anything with it. This is all a bit hard coded, but
basically it should remove everything from the SourceServiceDefinition
item and replace it with the one we want if this is a build for test-->
<ItemGroup>
<!-- This is an interesting way of saying remove everything that is in me from me-->
<SourceServiceDefinition Remove="#(SourceServiceDefinition)" />
<TargetServiceDefinition Remove="#(TargetServiceDefinition)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetProfile)' == 'Test'">
<SourceServiceDefinition Include="ServiceDefinition.Test.csdef" />
</ItemGroup>
<ItemGroup Condition="'$(TargetProfile)' != 'Test'">
<SourceServiceDefinition Include="ServiceDefinition.csdef" />
</ItemGroup>
<ItemGroup>
<TargetServiceDefinition Include="#(SourceServiceDefinition->'%(RecursiveDirectory)%(Filename).build%(Extension)')" />
</ItemGroup>
<Message Text="Source Service Definition Changed To Be: #(SourceServiceDefinition)" />
</Target>
To go back to normal, right click on the project and select Reload Project. Now when you build your project, depending on which configuration you use, it will use different .csdef files. It's worth noting that the settings editor in is not aware of your second .csdef file so if you add any new settings through the GUI you will need to add them manually to this alternate version.
If you would want to just have a different CSDEF then you can do it easily by using CSPACK command prompt directly as below:
Open command windows and locate the folder where you have your CSDEF/CSCFG and CSX folder related to your Windows Azure Project
Create multiple CSDEF depend on your minor changes
Be sure to have Windows Azure SDK in path to launch CS* commands
USE CSPACK command and pass parameters to use different CSDEF and Output CSPKG file something similar to as below:
cspack <ProjectName>\ServiceDefinitionOne.csdef /out:ProjectNameSame.csx /out:ProjectOne.cspkg /_AddMoreParams
cspack <ProjectName>\ServiceDefinitionTwo.csdef /out:ProjectNameSame.csx /out:ProjectTwo.cspkg /_AddMoreParams
More about CSPACK: http://msdn.microsoft.com/en-us/library/windowsazure/gg432988.aspx
As far as I know, you can't easily modify the .cspkg after it is created. I guess you probably technically could as the .cspkg is a zip file that follows a certain structure.
The question I'd ask is why? If it is to modify settings like VM role size (since that's defined in the .csdef file), then I think you have a couple of alternative approaches:
Create a seperate Windows Azure deployment project (.csproj) for each variation. Yes, I realize this can be a pain, but it does allow the Visual Studio tooling to work well. The minor pain may be worth it to have the easier to use tool support.
Run a configuration file transformation as part of the build process. Similiar to a web.config transform.
Personally, I go with the different .csproj approach. Mostly because I'm not a config file transformation ninja . . . yet. ;) This was the path of least resistance and it worked pretty well so far.

Packaging with NAnt, how to handle different environments

I'm using NAnt to build an ASP.NET MVC project.
The NAnt script then creates a zip package, containing a deploy script and all the necessary files.
The deploy script backs up the current running website, sets up the newer version of the website and updates the DB.
This works fine for a single environment.
However, we're asked more and more to set up a Staging/Acceptance environment next to the production. These environments, of course, differ in file structure, DB server, config settings etc.
How can I best handle this in the deploy scripts? I don't want to create separate variables for each environment, distinguishable by name only.
Providing defaults and providing the variables in separate files seems more logical.
Does anyone have practical experiences with this?
Store the things that you think are likely to change between environments in config files.
Visual Studio can do the heavy lifting here if you like; you can create settings and specify default values from the Settings tab of a Visual Studio project's properties.
This will create the config file for you and provide strongly-typed access through Properties.Settings.Default.
As for handling multiple environments through your build, I've seen some people recommend maintaining multiple copies of the config files - one for each environment for example - and others recommend using nant to modify the config files during the build or deployment phase. You can use a property passed to nant on the command line (for example) to select which environment you are building (or deploying, depending on how you're doing it).
I don't recommend either of these approaches because:
They both require changes to your build to support new environments.
If you change a setting in a deployed environment and forget to update the build then the next deployment will reset the change (somewhat defeating the point of config settings).
If someone creates a new environment (lets say they want to explore issues arising from upgrading to a new version of SQL Server for example) and doesn't fancy creating all new config files in the build system, they might decide to just use an existing environment's settings. Let's say they choose to deploy using the live settings and forget to change something afterwards. Your new 'test' environment could now be pointing to live kit.
I create a copy of each config file (called web.config.example, for example) and comment out the settings within them (unless they have meaningful defaults). I check these in and have those deployed instead of the real web.config (that is, web.config is NOT deployed automatically. web.config.example is deployed as web.config.example.
The admin of the new environment will have to copy and rename the file to web.config and provide meaningful values). I also put all the calls to the settings behind my own wrapper class - if a mandatory setting is missing I throw an exception.
The build and my environments no longer depend on each other - one build can be deployed to any environment.
If a setting is missing (a new environment or a new setting in an existing environment) then you get a nice clear exception raised to tell the admin what to do.
Existing settings are not altered after an upgrade because only the .example files were updated. It's an admin task to compare the current settings with the latest example and revise if necessary.
To configure the deployment, you could put all the environmental settings (install paths, etc) into nant properties and move them into a separate file (settings.build for example) then use the nant include task to include that file at the top of your deployment file (deploy.build for example). You can then deploy a new version of deploy.build without overwriting your config changes as they are in settings.build. If a new property is introduced into deploy.build nant will fail with a nice message to tell you that you haven't set that property.