I was involved in changing an existing web application to be packaged by MSDeploy recently.

The package had to include files from external directories, as there are images, CSS and Javascript files that come outside the web application project. I needed to work out how to do this with MSDeploy.

Edit: CopyAllFilesToSingleFolderForPackageDependsOn has been renamed to CopyAllFilesToSingleFolderForMsdeployDependsOn in Visual Studio 2012. Thanks to Scott Stafford for pointing this out in his comment on the post.

This is very similar what Sayed does, except I have two targets defined in here. DefineCustomFiles creates an ItemGroup containing the files to be copied, and is defined in each project. An example looks like this.

This looks very simple, but the combination of @ and % syntax was the hard part of the exercise. I couldn’t use <FilesForPackagingFromProject Include="%(CustomFiles.Identity)"> as per Sayed’s example; using the Identity metadata prevents access to the Dir metadata previously defined to specify the destination. I ended up needing to use Include="@(CustomFilesToInclude)" so I could access the metadata. It took some more trial and error to find the correct syntax to reference the metadata of each of the items of CustomFilesToInclude, using the % syntax as shown.

I then extended the CustomCollectFiles task to check for files that already existed in the target directory.

You can see how the syntax I use in the Condition of the Error is the same as the syntax used in the DestinationRelativePath relative path above.

I toyed with the idea of adding another piece of metadata to the items within CustomFilesToInclude to indicate whether the Exists check is applicable for that item or not. But after a little experimentation I decided it was simpler to use two item groups. Any items that do not require the check go into CustomFilesToIncludeSkipExistingCheck.

So at the end of this journey I have learnt a few things: Sayed is always your first resource to search if you have MSBuild questions; the MSDeploy pipeline is extensible in a useful fashion; MSBuild can be devilishly confusing and take a fair amount of trial and error for those who don’t intimately understand it, especially when you try and do anything complex with groups of files.

To use MSDeploy’s extensibility, you need to use MSBuild. However, when not using MSDeploy, I’d like to avoid MSBuild. So the next time I start a project that promises to have any complexity, I’ll be looking for a build framework that makes it easy to leave complex behaviour outside of MSBuild. I investigated psake after seeing that Rhino.Mocks uses it, and liked what I saw. I also like that you don’t have to learn a specific “make” language – psake’s decision to leverage an existing scripting language is smart and practical.

Do you know of any way to include files that are not located on disk? I have a custom Task in which I want to generate some content that I want to publish via FilesForPackagingFromProject , but this only allows me to include files from disk.

I haven’t used MSBuild for a while now, so I’m probably not the best authority here. But I believe that you can only package files on disk. Without knowing your specifics, I think you’re going to find it much easier to extend your Task so it generates a file to disk that you can package.

This article helped me a lot. However I was unable to get it to work in Visual Studio 2013. In my frustration I ended using all the concepts here, but adding a layer where I pass the copy off to a powershell script. The powershell script does the actual copy.

I wrote a quick blog entry on it on Blogger, Check it out if anyone is interested (sorry for the formatting, It’ll improve when I have time to set up a good template).

I am building the whole solution which has one Azure Cloud Project and 1 worker role project. The basic build (With dependsOnTargets=”Publish”) is enough to create the Azure (.cspkg) file . To the same package i wanted to add extra file (.txt file).The AzureCloud Project(.csproj) is already importing “Microsoft.WindowsAzure.target”. Should i need to expilicty import any of the targets (or) should i modify existing targets(which i don’t want to )

For people still finding this article and trying to implement the above in new versions of Visual Studio. You need to create a publishing profile and add the code in the ./Properties/PublishProfiles/{profile}.pubxml, as this configuration has moved out of the project configuration. On caveat I found was that modifying the profile in the UI would strip out the $(CopyAllFilesToSingleFolderForMSdeployDependsOn); line on save, not sure if this matters.

Thanks Matt. This article is really getting quite old now. If there’s an article that explains how to do this with modern Visual Studio, I’d be happy to update this article to link to it at the top of the page.