Introduction
In the previous article (can be found here), we covered -
- What are MSBuild?
- It’s structure, its nodes
- A simple example of how to run it.
This article will be about the different tasks that are commonly achieved with MSBuild scripts for automating publishing/deployment of an application.
Why do we need automation?
Automating a build processes has following advantages –
- Minimised risks
- Require lesser skills for running efficiently.
- Easy to run as for a deployment person, it’s much easier to run .bat file that has the MSBuild command in it for publish than actually opening the application with VS and checking all dependencies and configurations.
Common Properties
While individual MSBuild script may have varied scripts, there are quite a few properties that are common and are found in almost all MSBuild scripts. In MSBuild scripts properties are named by developers. So while the actual name may differ amongst scripts but the purpose of those properties are common. Such common properties (names can be standardised at project, team and geography level to increase readability) are as follows: -
Configuration
– This property checks the configuration of publish i.e. whether it’s to be Release or Debug. TF
– A property that maps as to where the TF.exe resides on the system. TF .exe is important when you want to execute TFS based commands MSTestExe
– A property that maps as to where the MSTest.exe resides on the system. This is important if you want to run the automatically with MSBuild scripts. TFSProject
– A property to be declared to get the location of the solution that you want to publish from TFS. TFSBuildWorkSpaceName
– A property to be declared to get the workspace name. WorkingDirectory
- A property declared to map the location at which the commands are to be executed SolutionName
– The property declared to map the actual solution file.
The list continues ... so let’s walk through couple of targets and get to have a better understanding. This is to be noted that properties are accessible via "$" prefixes to the property name
Tasks / Targets
Scope of the tasks that could be achieved with MSBuild is vast. Given below is a very concise set of activities –
Target - GetVersion
Purpose - Get the version specified in text file and display the same.
Definition of Target:
<Target Name="GetVersion">
<Message Text="GetVersion: Reading version number from VersionInfo.txt" />
<Attrib Files="$(WorkingFolder)\VersionInfo.txt" Normal="true" />
<Version VersionFile="$(WorkingFolder)\Build\VersionInfo.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<Message Text="GetVersion: $(Major).$(Minor).$(Build).$(Revision)" />
</Target>
Properties to be declared:
<PropertyGroup>
<WorkingFolder>C:\MSBUILD tests</WorkingFolder>
</PropertyGroup>
Command
- Description - Specifying the target to run with default value for the declared property
msbuild GetVersion.msbuild /t:GetVersion
This has a pre-condition that the a VersionInfo.txt file should exist at C:\MSBUILD tests\Build
Output

- Description - Specifying the target to run with a specific value different from for the declared property
msbuild GetVersion.msbuild /t:GetVersion /property:WorkingFolder = "C:\MSBUILD tests\Build\V"
This has a pre-condition that the a VersionInfo.txt file should exist at C:\MSBUILD tests\Build\V\Build
Output
N.B.- This is to be noted that the MSBuild has multiple command options that could explored with help accessible via MSBuild /help command. Please explore the same.
- Team Foundation Activities
This is to be noted that in order to execute any TF commands TF.exe should be installed.
Target - UnLock
Purpose – Will unlock all files in the solution specified
Target Definition:
<Target Name="UnLock">
< Message Text="Unlocking...."></ Message >
<Exec Command="$(TF) lock "$(TFSProject) /assemblyinfo.*" /recursive /LOCK:NONE /workspace:$(TFSWorkSpaceName) /server:$(TFSServer) " WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
<Message Text="Unlocking Complete...."></ Message >
</ Target >
Property Declaration:
<PropertyGroup>
<WorkingFolder>C:\Projects\MSBUILD</WorkingFolder>
<TFSProject>$/[server location]/[solution folder name]</TFSProject>
<TFSWorkSpaceName>SomeWorkSpace</TFSWorkSpaceName >
<TFSServer>tfs.thisdomain.com</TFSServer>
</PropertyGroup>
Command Declaration
msbuild Unlock.msbuild /t: UnLock
N.B. – This is to be noted that since these are related to TFS, hence the TFS specifc details have been suppressed and actual output is not displayed
Target - GetLatest
Purpose – Will get latest of the solution
Target Definition:
<Target Name="GetLatest">
<RemoveDir Directories="$(WorkingFolder)" />
<MakeDir Directories="$(WorkingFolder)" />
<Exec Command="$(TF) workspace /delete $(TFSWorkSpaceName) /server:$(TFSServer) /noprompt" WorkingDirectory="$(WorkingFolder)" ContinueOnError="true" />
<Message Text="Temporary build Workspace deleted" />
<Exec Command="$(TF) workspace /new $(TFSWorkSpaceName) /server:$(TFSServer) /noprompt" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
<Message Text="New temporary build Workspace created" />
<Exec Command="$(TF) workfold /workspace:$(TFSWorkSpaceName) /server:$(TFSServer) /unmap $/" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
<Message Text="Default working folder for new temporary build workspace unmapped" />
<Exec Command="$(TF) workfold /workspace:$(TFSWorkSpaceName) /server:$(TFSServer) /map $(TFSProject) $(WorkingFolder)" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
<Message Text="Working directory mapped to new temporary build workspace" />
<Exec Command="$(TF) get "$(TFSProject)" /recursive /version:T /noprompt" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
</Target>
Property Declaration:
<PropertyGroup>
<WorkingFolder>C:\Projects\MSBUILD</WorkingFolder>
<TFSProject>$/[server location]/[solution folder name]</TFSProject>
<TFSWorkSpaceName>SomeWorkSpace</TFSWorkSpaceName>
<TFSServer>tfs.thisdomain.com</TFSServer>
</PropertyGroup>
Command Declaration :
msbuild GetLatest.msbuild /t: GetLatest
N.B. – This is to be noted that since these are related to TFS, hence the TFS specifc details have been suppressed and actual output is not displayed
Snapshot of Other Tasks
The list of tasks that could be achieved is both extensive and diverse. Just for up start up, very concise list of activities that could be furthur achieved is given below:
Checkout:
<Exec Command="$(TF) lock "$(TFSProject) /*.*" /recursive /LOCK:NONE" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
<Exec Command="$(TF) checkout /lock:checkout /recursive "*.*"" WorkingDirectory="$(WorkingFolder)" ContinueOnError="false" />
Check in:
<Exec Command="$(TF) checkin /recursive "*.*"" WorkingDirectory="$(WorkingFolder)" ContinueOnError="true" />
<Exec Command="$(TF) label "SetVersion $(Major).$(Minor).$(Build).$(Revision)" $(TFSProject) /comment:"MSBuild Automated Build Label" /owner:$(username) /recursive" WorkingDirectory="$(WorkingFolder)" ContinueOnError="true" />
Set Version (depending on some other tasks):
<Target Name="SetVersion" DependsOnTargets="GetVersion;CheckOut">
<Message Text="SetVersionInfo: Updating Versions in all files" />
<CreateItem Include="$(WorkingFolder)\**\*.*">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
<Attrib Files="@(Files)" Normal="true" />
<FileUpdate Files="@(Files)"
Regex="FileVersionAttribute\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)"
ReplacementText="FileVersionAttribute("$(Major).$(Minor).$(Build).$(Revision)")"
/>
<FileUpdate Files="@(Files)"
Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)"
ReplacementText=" FileVersion ("$(Major).$(Minor).$(Build).$(Revision)")"
/>
<FileUpdate Files="@(Files)"
Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)"
ReplacementText="FileVersion("$(Major).$(Minor).$(Build).$(Revision)")"
/>
</Target>
N.B. -
- Output Parameters from commands executed are accessed via "@" prefixes to the parameter name
- DependsOnTargets specifies whether the Target in question depends on the successful run of other targets or not. Multiple target dependencies are specified by comma separation.
Well that’s it as of now. We will continue to with customizing TFS activities with C# in the next part in the series.
The above set of TF activities is only a snap shot of how to achieve tfs related activities through the MSBuild. The list of tasks that could be done through TF commands in MSBuild is exhaustive. The best possible way to understand them is to practice.