web.config changes via TFS 2015 Release Management - powershell

In the past I've using web.config transforms when manually deploying code to set environment specific setting values and attributes. I am transitioning from environment specific manual builds to a single TFS 2015 Build deployed to multiple environments via Release Management. Environment specfic application settings values configured in the web.config are tokenized. This method essentially inserts tokens into setting values during the build process. When deployed the tokens are replaced with matching Release definition configuration values.
This method is insufficient setting attributes of non-settings however. Examples of these transforms include:
<httpCookies requireSSL="true" xdt:Transform="Insert" />
<compilation xdt:Transform="RemoveAttributes(debug)" />
<httpRuntime xdt:Transform="RemoveAttributes(executionTimeout,maxRequestLength,useFullyQualifiedRedirectUrl,minFreeThreads,minLocalRequestFreeThreads,appRequestQueueLimit,enableVersionHeader)"/>
<httpRuntime enableVersionHeader="false" maxRequestLength="12288" xdt:Transform="SetAttributes"/>
<customErrors mode="On" xdt:Transform="SetAttributes"/>
What is the best way to update these attributes during release?

Both Web Deploy's parameters.xml method and transforms can be used with Release Management. Transforms would be triggered from Build and the process of replacing tokens created by a publish would be triggered by Release Management.
To trigger transforms during the build, you can do this one of two ways:
Add the following MSBuild parameters to force the transformation to happen during the build
/p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false
Create a publish profile using the MSDeploy Package option and then trigger the packaging in Build using the following MSBuild parameters:
/p:DeployOnBuild=true /p:PublishProfile=[nameOfProfile]
Either of the above methods will cause normal Web.config XDT's to run. If you need other XML files to be transformed, you'll need to first install SlowCheetah.
Token Replace and Parameters
Now that you have a build artifact with XDT's run, you can use token replacement and the WinRM tasks from Release Management. These will take the Web Deploy package from the Build and execute the SetParameters command before deploying it. The trick is to take the SetParameters.xml file and run a token replace on it first, swapping out Release environment variables first.

User Sumo gave a proper answer, but I want to record some comments related to what instead of how.
IMHO there are different categories of settings to consider, let's exemplify. The database connection string changes at each environment, while requiring SSL should be turned on for all testing and production environments.
In this perspective, you should have settings applied as early as possible, traditionally at build time and called Debug/Release builds; and last-minute settings, environment dependent, up to runtime settings, like Feature toggles.
So in my view you can use a single tool or multiple tools, but it is important that you properly categorize your settings accordingly.

Related

ASP Net Core multiple environment publishing

Coming from the .NET MVC world, I am confused on how .NET Core deals with multi-environment deployments. (Dev, Test, Production)
The tech used here are Bamboo (Build Server) + Octopus Deploy (CD).
.NET Core appears to be using appsettings instead, and web.config is only used for IIS Hosting.
Upon reading some guides, which suggests to add an Environment Variable "ASPNETCORE_ENVIRONMENT" to the project to define the environment currently in.
This is the command I used to build in Bamboo.
dotnet publish -c Test ${bamboo.build.working.directory}\HelloWorld.sln
Questions...
1. I have appsettings.json, appsettings.Test.json, appsettings.Production.json.
It looks like the app knows which appsettings file to read from, based on the
ASPNETCORE_ENVIRONMENT value.
How can I tell Octopus to use the correct file based on the environment I am deploying to?
Having a variable and multiple config files packaged up make things overly complicated, especially if you're using Octopus Deploy.
The only reason for having multiple .environment.json files is because these are probably held in source control and they have their values already set - so environment variables are probably all coming from the source code, rather than the release manager. Otherwise, they'd be identical files and Octopus would still be transforming them (which makes them redundant)
My advice would be to move to a single file, and move the variables into Octopus Deploy. Remove the ASPNETCORE_ENVIRONMENT and you only need one file appSettings.json, which can be transformed during deployment to whatever environment.
ASP.NET Core Web Applications - Octopus Deploy Documentation
Hope this helps

Visual Studio Online / Azure stopping and starting web applications using Powershell

I'm using Visual Studio Online's build tools to deploy web applications from a single solution. I've occasionally been running into file locking issues.
Error: Web Deploy cannot modify the file 'Microsoft.CodeAnalysis.CSharp.dll' on the destination because it is locked by an external process.
After some Googling, I believe the "fix" is to stop the web applications before deployment on Azure and start it back up after. Sounds legit.
However, there does not seem to be a straight forward way to do this directly on VSO's build definitions. I've created an "Azure Powershell" build task, but it wants a PS1 file from the repository. It doesn't seem to let me just run Azure Powershell commands (e.g. Stop-AzureWebsite) from here. My team has created a work-around where we have a "run.ps1" that just executes the command you pass as a parameter, but none of us are satisfied by that.
What are we missing? There has got to be an easier way to do this without having a PS1 script checked into source control.
I solved this by installing Azure App Services - Start and Stop extension from Visual Studio Marketplace.
When installed, it will allow you to wrap the Deploy Website to Azure task in your Release definition with Azure AppServices Stop and Azure AppServices Start tasks, effectively eliminating the lock issues.
Check if you are using "/" on the "Web Deploy Package" path for folder separators instead of "\".
i.e. change
$(System.DefaultWorkingDirectory)/My Project/drop/MyFolder/MyFile.zip
for
$(System.DefaultWorkingDirectory)\My Project\drop\MyFolder\MyFile.zip
I noticed that was the only difference between the one I was getting the error and the others (the Restart step I added was not helping). Once I modified the path, I got it working.
Sounds crappy, but fixed my issue.
Did you use the Build Deployment Template that sets the correct msbuild parameters for you for your package? You can see how here. I would create a build using that template and see if you have the same issues. If so ping me on Twitter #DonovanBrown and I will see if I can figure what is going on.
As a rule it is good practice to have any scripts or commands required to deploy your software to be checked into source control as part of your build. They can then be easily run repeatedly with little configuration at the build level. This provides consistency and transparency.
Even better is to have deployment scripts output as part of the build and use a Release Management tool to control the actual deployment.
Regardless having configuration as code is a mantra that all Dev and Ops teams should live by.

How to differentiate TFS Builds and manual builds using macros in Post build event

In TFS post build script of a .proj file I want to find whether the project build is happening through TFS triggered build or manually triggered build.
Can someone suggest me how to do this using macros in Post Build event.
Short answer: you can make use of the IsDesktopBuild MSBUILD property within your csproj file to differentiate between TFS and local build.
Long Answer:
Developer or Team Build?
To differentiate the build environments we have to implement a mechanism that detects in which environment the build is being executed. In other words, we need to know if we running a local build that is executed by the developer or a team build running on the build server.
In fact, there are 3 different build environments we need to consider:
· Visual Studio Build – a build executed by a developer, on their own development machine inside the Visual Studio IDE
· Team Build – a build executed by TFS (manually or scheduled), on the build.
· Desktop Build – a build explicitly executed manually, on the development workstation using the command 'msbuild.exe tfsbuild.proj'.
A ‘DesktopBuild’ and a ‘TeamBuild’ are very similar in nature except that ‘DesktopBuild’ does not perform a ‘GetLatest’ function from source repository, will not ‘Label’ the source tree and will not determine the change set.
When using MSBUILD tasks (as we will use primarily in following sections), one common way to achieve this is to use the ‘IsDesktopBuild’ and ‘BuildingSolutionFile’ properties as conditions to test in the tasks.The ‘IsDesktopBuild’ property is declared in the ‘Microsoft.TeamFoundationBuild.targets’. The ‘BuildingSolutionFile’ property is declared and assigned automatically by MSBUILD.
The following table lists the values of each of these properties in each of the build environments.
Environment IsDesktopBuild BuildingSolutionFile
Visual Studio Build (empty) (empty)
Desktop Build true true
Team Build false true
One caveat with using the ‘IsDesktopBuild’ property is that it is not defined in many target files by default. This property will have an ‘empty’ value in a Visual Studio build, so we initialize it to a value of ‘true’ as the default value. Therefore we need to be explicitly define it in all MSBUILD target files where it will be tested.
We simply add the following element to all target files where we need to differentiate between a build on the development machine and a build on the build server (within the first section).
<IsDesktopBuild Condition="'$(IsDesktopBuild)' == ''">true</IsDesktopBuild>
Update: thank you #dbardakov. Starting VS 2012 we can use the property to find if the build is happening within Visual Studio:
BuildingInsideVisualStudio
MSDN SOURCE - for BuildingInsideVisualStudio
MSDN SOURCE

Deploy website to drop folder on TFS build server

I am using TFS 2012 on a build server to do continuous integration, and also builds for other environments. I am deploying a .net 4.0 webforms solution containing two websites and a console app to a build server and in the drop folder I get a _PublishedWebsites folder containing the deployment package for the custom configuration I have specified (ie not Debug or Release). I get the correct .Web_Package for each website.
I am using the MSBuildArgumments setting in the build process as follows:
/p:DeployOnBuild=true /p:PublishProfile=Development/p:VisualStudioVersion=10.0
Everything works well, but the team lead wants the actual deployment in the drop-folder and not the deployment package.
I have looked at the MS Documentation but it does not appear to help in my case.
I have tried creating a script, but I dont want to go into a huge series of powershell scripts, and I struggle with powershell anyway. I just cant get it to do what I want.
My question is: can this be done, and what is the best way of doing it? If it does mean creating a script then so be it ( perhaps one line for each object) - I am really having a hard time working out which direction I should be going in.
I thought something a script containing one line for each deployment -3 lines like this:
_PublishedWebsites/<project name>.Web_Package/<project name>.deploy.cmd /T: /M:<site> /U:<user> /P:<password>
But where to put the script and how to call?
I ended up using the solution as described in this blog post:
http://blog.degree.no/2012/03/automatic-config-transformations/
To get what I needed, I needed to change the project files of the projects to be deployed - adding this extra node at the top of each.csproj file
<Target Name="TransformConfigFiles" AfterTargets="AfterBuild" Condition="'$(TransformConfigFiles)'=='true'">
<ItemGroup>
<DeleteAfterBuild Include="$(WebProjectOutputDir)\Web.*.config" />
</ItemGroup>
<TransformXml Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(WebProjectOutputDir)\Web.config" />
<Delete Files="#(DeleteAfterBuild)" />
</Target>
and adding this switch to the MSBuildArguments in the build definition:
/p:TransformConfigFiles=true
When I run my builds using the DeployOnBuild switch, my builds output two folders, one with the Package, and one with just all the website files (both under PublishedWebsites). Is that not what you want?
I have:
BuildDrop_PublishedWebsites\Website
BuildDrop_PublishedWebsites\Website_Package

How to parameterize Bamboo builds?

Please note, although my specific example here involves Java/Grails, it really applies to any type of task available in Bamboo.
I have a task that is a part of a Bamboo build where I run a Java/Grails app like so:
grails run-app -Dgrails.env=<ENV>
Where "<ENV>" can be one of several values (dev, prod, staging, etc.). It would be nice to "parameterize" the plan so that, sometimes, it runs like so:
grails run-app -Dgrails.env=dev
And other times, it runs like so:
grails run-app -Dgrails.env=staging
etc. Is this possible, if so, how? And does the REST API allow me to specify parameter info so I can kick off different-parameterized builds using cURL or wget?
This seems to be a work around but I believe it can help resolve your issue. Atlassian has a free plugin call Bamboo Inject Variables Plugin. Basically, with this plugin, you can create an "Inject Bamboo Variables from file" task to read a variable from a file.
So the idea here is to have your script set the variable to a specific file then kick off the build; the build itself will read that variable from the file and use it in the grails task.
UPDATE
After a search, I found that you can use REST API to change plan variables (NOT global). This would make your task simpler: just define a plan variable (in Plan Configuration -> tab Variables) then change it every time you need to. The information on how to change is available at Bamboo Knowledge Base