Automated Deployment Fun First Item From List Msbuild

Last week was an education in making Msbuild jump through hoops to automate our deployments.   Having a development background it is always strange switching context into the land of Msbuild.  I find myself wanting to create variables and control structures, alas that is not how it works.

One interesting challenge I was able to overcome was figuring out how to pick the first item from an item when the item is a list.  We have multiple destiation servers we deploy to and we keep them in one item as a list, ‘DeployDestination’. I only want to use one of them as the source for the backup, however. To do the deployment I iterate through the server items calling a task to delete the code at that destination and copy in the new code per the below:



<PropertyGroup>
<BackUpRoot>C:\BackupRoot</BackUpRoot>
<SourceFileLocation>C:WorkingBuildLocation\</SourceFileLocation>
</PropertyGroup>

<ItemGroup>
    <DeployDestination Include="\\serv1\root\" >
      <ServerName>Web1</ServerName>
      <Environment>Production</Environment>
    </DeployDestination>
    <DeployDestination="\\serv2\root\" >
      <ServerName>Web2</ServerName>
      <Environment>Production</Environment>
    </DeployDestination>

<DeployDestination="\\serv3\root\" >
      <ServerName>Web3</ServerName>
      <Environment>Production</Environment>
    </DeployDestination>
</ItemGroup>

<!--Backup From One Server Trying to figure out how-->


<!-- Deploy To All Servers -->
<MSBuild Projects="$(MSBuildThisFile)" Targets="DeleteAndCopy" 
Properties="Env=%(DeployDestination.Environment);
MachineName=%(DeployDestination.ServerName);
SourceFiles=$(SourceFileLocation);
FolderDestination=%(DeployDestination.FullPath)"  />



<!--More deployment fun below-->

The above works fine.  However, for the purpose of taking a backup I want to only grab the code from one of the machine’s we are deploying to.  I was hoping to be able to grab the first item in the ‘DeployDestination’ list, perhaps using an index that would look like the below:




<MSBuild Projects="$(MSBuildThisFile)" Targets="Copy" 
Properties="SourceFiles=@(DeployDestination)[0];
FolderDestination=$(BackUpRoot)"  />



<!--More deployment fun below-->

Of course that is the developer in me, and the above does not work.  I knew I could just create another property that had the source for the backup hard coded and just reference that, but that seemed wrong, why update the location in two places?  Luckily I was using Msbuild 4 and the relatively new item functions were able to save me!  By adding a meta data item to the destination that should be the backup source I was able to get just the one value I am looking for.

Now I can get what I’d like using the following:



<PropertyGroup>
<BackUpRoot>C:\BackupRoot</BackUpRoot>
<SourceFileLocation>C:WorkingBuildLocation\</SourceFileLocation>
</PropertyGroup>

<ItemGroup>
    <DeployDestination Include="\\serv1\root\" >
      <ServerName>Web1</ServerName>
      <Environment>Production</Environment>
         <BackupSource>True</BackupSource>
    </DeployDestination>
    <DeployDestination="\\serv2\root\" >
      <ServerName>Web2</ServerName>
      <Environment>Production</Environment>
    </DeployDestination>

<DeployDestination="\\serv3\root\" >
      <ServerName>Web3</ServerName>
      <Environment>Production</Environment>
    </DeployDestination>
</ItemGroup>

<!--Backup From One Server -->
<MSBuild Projects="$(MSBuildThisFile)" Targets="Copy" Properties=
"SourceFiles=@(DeployDestination->WithMetadataValue('BackupSource','True'));
FolderDestination=$(BackUpRoot)"  />

<!-- Deploy To All Servers -->
<MSBuild Projects="$(MSBuildThisFile)" Targets="DeleteAndCopy" 
Properties="Env=%(DeployDestination.Environment);
MachineName=%(DeployDestination.ServerName);
SourceFiles=$(SourceFileLocation);
FolderDestination=%(DeployDestination.FullPath)"  />


<!--More deployment fun below-->