I'm using vs 2010 and have a web application project.
I currently use the right-click "publish" option on my project using the "web deploy" option to publish the entire application to a server that is running msdeploy.axd.
I have to publish this project many times (100+), each time just changing the web.config.
I'm looking into what technology I should use to automate this process. I would prefer to use standard MS technology.
Should I be looking at MSBuild? The VS Command Prompt? (are these the same thing?)
What technology should I learn to best automate this scenario. I'm looking for the most standard way to do this...
In my head the script I'm going to write will:
change the web.config file
do the equivalent of right-click my project, click publish, use the web-deploy option, and deploy it to that server using the windows domain\username + password details i've saved in Visual Studio
Any help is appreciated.
I've done this exact thing just with MSBuild. The steps involved in my case were:
TFS performed a build using a custom MSBuild project, not a solution file. Any CI build system would work, nothing special about TFS in this instance.
In the custom build project, in addition to building all the projects, I copied a bunch of web.config "template" files to a special folder under the $(OutDir) folder. These would end up in the build drop from TFS. In my case, the built in configuration file transformations were nowhere close to being sophisticated enough, but if that can work for you it is simpler by far.
The web.config files actually contained references to other config files, one for each configurable feature. This used a custom configuration provider. This technique would also work if you just had a single web.config file though.
I enabled automated deployment (Publish) from the command line, as a new MSBuild target in the same custom MSBuild project that was driving the build.
It was then easy to automate the Publish step in the main build to a VM or QA machine, as well as make it possible to manually deploy to other servers (eventually the staging servers) from the command line.
The web.config templates had stuff like this:
In the connection string: "Data Source=${SQLINSTANCE};Initial Catalog=${SQLDATABASENAME}..."
It is significant that the delimiters for the replacable tokens is ${ } and not $( ), since MSBuild won't mess with the curly brackets.
In the Publish step, use an MSBuild property function to replace bits in the config files, the following is taken from MSDN's description of MSBuild inline tasks:
<UsingTask
TaskName="ReplaceToken"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<File ParameterType="System.String" Required="true" />
<Token ParameterType="System.String" Required="true" />
<Replacement ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string content = File.ReadAllText(File);
content = content.Replace(Token, Replacement);
File.WriteAllText(File, content);
]]>
</Code>
</Task>
</UsingTask>
Then in your project, you can use this task with,
<ReplaceToken
File="PathTo\Web.config"
Token="${SQLINSTANCE}"
Replacement=".\SQLEXPRESS"
/>
This is really just a rough guide, all of the parameters to ReplaceToken were also configured in MSBuild item metadata, allowing options for which database, server, security, etc., each of which could be specified individually.
So, for each build/deployment, it would perform the build, copy the config templates, do string replacements on them, then automate the Package/Publish, which is the final bit to go over.
Your best bet is to start with this blog: http://vishaljoshi.blogspot.com/2009/02/web-packaging-creating-web-packages.html which explains a bit, and this answer, which contains likes to a bunch of other related StackOverflow posts MsBuild and MsDeploy with multiple environments, then search online with http://www.bing.com/search?q=msbuild+msdeploy+command+line&go=&form=QBLH&qs=n&sk= to dig deeper. I really hate to just dump you off to a search engine for this part of it, but I've found that there are so many different scenarios it is tough to single one out. About half of the top ten responses have insight on some valuable angle. Reply with more info to help narrow down my response at this point. For my implementation, I called MSDeploy.exe directly from MSBuild using an Exec task. Some things to consider:
How to deal with security on the various publish sites. I had to set up a mess of build service accounts, and always required passing the password on the msbuild command line. If you can get by with Windows auth like you suggest it will be a bit easier.
For services, I had to use PSExec to run installutil on the remote server
There were some additional configuration items that I automated using PSExec calling appcmd on the remote server.
It is easy to open up a remote share on the server and use a "net use" command to map and unmap it during the build, you may have some other preference.
Performance is tough. For larger sites it can run quite long doing it file by file. RoboCopy isn't any faster. I found using MSDeploy to package remotely (point to local drop as source, and remote share for the package source) was very fast to Rackspace. You may need to first package to a zip file using one MSDeploy call, then push the package remotely using a second.
Hope this gets you started. Please comment or supply more detail in your question if there is something I really missed or glossed over.
Response to comment:
The "Publish" target is something along these lines,
<Target Name="Publish">
<!-- token replacement in config files, as above -->
<!-- ...lots of custom setup, selection of various properties used below -->
<PropertyGroup>
<_MsDeployExe>$(PROGRAMFILES)\IIS\Microsoft Web Deploy\msdeploy</_MsDeployExe>
<_MsDeploySourceArg>-source:contentpath="$(_BuildDropFolder)"</_MsDeploySourceArg>
<_MsDeployDestArg>-dest:contentpath=\\$(_RemoteComputerName)\DropFolder</_MsDeployDestArg>
</PropertyGroup>
<Message
Text=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
/>
<Exec
Condition="'$(DryRun)' != 'true'"
Command=""$(_MsDeployExe)" -verb:sync $(_MsDeploySourceArg) $(_MsDeployDestArg)"
ContinueOnError="false"
WorkingDirectory="$(MSBuildThisFileDirectory)"
/>
</Target>
Related
this is my situation:
I have VS2010 solution with X projects included.
Wix project that can create msi from all compiled artifacts.
I have build machine \ Jenkins that first compile (MSBuild .Net 4) all the solution, then compile the wix to package it to msi.
What\how can I inject to all artifacts\dlls the number of the product (e.g 11.2.0.4789) - as simple as possible?
Is there and command line arguments that can be passed while compiling the solution?
There are tools, such as several extensions for MSBuild, that do version stamping but each assumes a particular workflow. You might find one that works for you but a DIY method would help you evaluate them, even if it isn't your final solution.
You can add a property to the MSBuild command-line like this:
msbuild /p:VersionStamp=11.2.0.4789
Note: I assume you are going to parameterize the Jenkins build in some way or generate the number during a preceding build step. Here is a simulation of that:
echo 11.2.0.4789 >version.txt
set /p version=reading from pipe <version.txt
msbuild /p:VersionStamp=%version%
Now, the work is in getting each project to use it. That would depend on the project type and where you want VersionStamp to appear.
For a csproj, you might want to use it as the AssemblyVersion. The simplest way is to move the attribute to a cs file by itself and rewrite it every time. I would leave a comment in AssemblyInfo.cs as a clue to where it now comes from. You can include the cs file in your project either dynamically or permanently. I prefer dynamically since it is effectively an intermediate file for the build. So, in your .csproj add the following in a text editor (e.g. Visual Studio. Unload and Edit project):
<Target Name="BeforeBuild">
<PropertyGroup>
<AssemblyVersionPath>$(IntermediateOutputDir)AssemblyVersion.cs</AssemblyVersionPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(AssemblyVersionPath)" />
</ItemGroup>
<WriteLinesToFile
File='$(AssemblyVersionPath)'
Overwrite="true"
Condition="'$(ProductVersion)' != ''"
Lines='using System.Reflection%3b;
[assembly: AssemblyVersion("$(VersionStamp)")]' />
</Target>
This is sufficient but a more thorough solution would include adding the file to a list so it is cleaned with other files and only writing the file if the version changed to prevent unnecessary rebuilds, etc.
Use a similar technique for other project types.
I need to update the application's manifest with a new value for 'product'. With mage.exe I can update the name and publisher but NOT the product. In MageUI you can do this, but I need to do it on the commandline.
Is there a solution or workaround for this?
I played around and it seems the command line tool indeed lacks this capability. I guess it was done for keeping it lightweight (or maybe cost issues).
Alternative would be to use the GenerateApplicationManifest MSBuild task:
Example:
<Target Name="Build">
<GenerateApplicationManifest
AssemblyName="myapp.exe"
Product="My Product"
...
OutputManifest="SimpleWinApp.exe.manifest">
<Output
ItemName="ApplicationManifest"
TaskParameter="OutputManifest"/>
</GenerateApplicationManifest>
</Target>
This gives you lot more options (in fact everything that you can do through MageUI, can be done from here) and you bypass mage.exe (and its limitations) totally.
You should be able to use it anywhere MSBuild is supported (csproj files, TFS Build proj files etc).
Our code uses different settings for development and production environments so we were looking at using VS2010's web.config transform capabilities. After hours of trial and error, nothing has worked. We found a Web.config transformation tester and found that what we had been trying was supposed to work (according to this tool.)
We tried testing the transformations using Build, not Publish. Does it only run on Publish or could something else be wrong?
Yes, it only runs on Publish. To test, publish to a local dir. You'll want to publish your application with the correct target environment set.
You can also integrates web config transformations with MS Build.
It seems you don't have to be in Publish mode to generate a transformed Web.config file. There's just a bit more work involved.
Open the Visual Studio Command Prompt and navigate to your working project directory that contains your .csproj file. Enter the following command:
MSBuild project.csproj /t:TransformWebConfig /p:Configuration=Debug
The example above would run the Debug transformation during the build of project.csproj. This will output a Web.config file into the obj\Debug\TransformWebConfig\transformed\ directory, where Debug is whatever Configuration you set in the command above.
Copy this file to replace your root Web.config file, and you're done. You could write a batch script to run both of those items automatically, but for larger projects with many configurations it could become unwieldy.
You might be able to add those command line arguments to the build process inside of Visual Studio, but I'm not sure how - as far as I know for this method to work you would have to build from the command line instead of inside Visual Studio. You can still use Clean inside of Visual Studio to clean out the obj folder but it will only clean the solution configuration mode selected in the IDE. Clean will not revert your edited web.config file, so you may want to back it up before proceeding if you need to.
(Command line arguments found from this MSDN article.)
You don't need run Publish/Build Package in order to test Web.config transformation. There is a cool trick to quickly know the transformation result here. Scroll down until you see a comment about creating TransformConfig project. It works like a charm, note that you can safely ignore 7th step (frankly I don't know how to do that step properly but fortunately we don't need it :)).
I'm a huge fan of the addition of web.config transformations in Visual Studio 2010. See also Scott Hanselman's recent talk at MIX2011.
What sucks is that this functionality (appears at least) to only be available to web projects.
In our solution we have several Windows Services that connect to a different database dependant on the environment they are deployed under.
Has anyone come up with a nice, tidy way of achieving similar 'app.config transformation' functionality?
Note: We are using TFS 2010 to build our solutions in a Continuous Integration manner.
I realize you already have an answer, but I stumbled across SlowCheetah this morning which is the best implementation I've seen to date. There is also a blog post on getting this running from a CI server.
You can use the XML transformation functionality with any XML file - we do this all the time. It's available via an MSBuild task.
Try adding the following to your build script:
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<TransformXml Source="Path\To\Your\Xml.config"
Transform="Path\To\Your\Xml.$(Configuration).config"
Destination="Path\To\Your\Output.config" />
I wrote nice extension to automate app.config transformation like the one built in Web Application Project
Configuration Transform
Using Luke Bennett's answer to set me off on the right track. I found this to be the answer for us.
FTA (see link for code snippets):
Add a new property ProjectConfigFileName that points to your App.Config file
Add a version of App.Config for each configuration, i.e., App.Debug.config To have them nested under App.Config, edit your csproj file,
Import Microsoft.Web.Publishing.targets into your csproj file right after the Microsoft.CSharp.targets import.
Call the TransformXml task in your AfterBuild target. Note, the BeforeBuild and AfterBuild targets are commented out by default.
If you have multiple client assemblies and don't want to duplicate the same configuration data, I created Profigurator. It'll take a JSON file as input and apply the settings to an app.config or web.config.
It's a little rough as I write this, but I am currently using it on a production system for deploys and it works great.
Background: We have a ClickOnce-deployed WPF app, that talks to WCF Services, which in turn both talk to our own SQL database and also to SharePoint via the Client OM. To set up the WCF and the ClickOnce, we have a Setup project, which takes in details about server paths and database connection strings from the installing user and fires an Installer class to do fun stuff like writing config XML and updating the ClickOnce strapper for that deployment URL and such.
We need to add some BDC Models to SharePoint via this installer, so that end users can use SharePoint list interfaces to configure some of the rarely-changed table values in our database. (As "one-click" an install process as possible is a requirement being imposed by the client.)
Including a BDC Model project in our Visual Studio 2010 solution, we can get a packaged WSP for our BDC stuff, which sounds great...
One problem with this, however, is that in the feature.xml that gets packaged into this WSP, this hard-coded line appears:
<Property Key="SiteUrl"
Value="http://BuildingWorkstationSharePointInstanceUrl/" />
Visual Studio won't build with the feature SiteUrl set to anything other than a SharePoint instance local to the machine (which is pretty lame), so we can't change that pre-WSP.
Furthermore, the .bdcm files themselves have hard-coded connection string information:
<LobSystemInstance Name="DatabaseName">
<Properties>
<Property Name="AuthenticationMode" Type="System.String">PassThrough</Property>
<Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>
<Property Name="RdbConnection Data Source" Type="System.String">DatabaseServer</Property>
<Property Name="RdbConnection Initial Catalog" Type="System.String">DatabaseName</Property>
<Property Name="RdbConnection Integrated Security" Type="System.String">SSPI</Property>
<Property Name="RdbConnection Pooling" Type="System.String">True</Property>
<Property Name="ShowInSearchUI" Type="System.String" />
</Properties>
</LobSystemInstance>
This would also have to be re-written by the installer once the installing user has provided the database connection information.
I'm also not sure what the best approach will be for actually installing the WSP on the server via the MSI (trying to execute a powershell script is all I've thought of so far).
It seems to me like designing BDC models for a third party shouldn't be that obscure of a scenario, but I can't find any information or support on how to overcome any of these problems!
Anyone have any advice?
I ran into this issue as well. I'd like to package our BDC model into a WSP and deploy it via the WSP. Unfortunately (like you've indicated) the BDC model contains specific environment information that must be configured per environment.
What we've landed on is keeping the different BDC models and just importing them instead of packaging them in a WSP. From the sounds of it, you may need to ask for the specific environment information at the time of install and somehow use that.
Two methods you could employ:
If you're using a "Custom" assembly type (instead of a DotNetAssembly as your LobSystem Type),
you can implement IAdministrable to allow you to change properties (either the LobSystem or LobSystemInstance) in the Central Admin. It doesn't seem to work for DotNetAssemblies, even if IAdministrable is implemented.
Alternatively, you can change properties by importing Resource Files. Easiest way to do this is to import your model, then export it as a Resource file and edit the file down to the properties you need changed. Then import the bdcr (resource) file and you'll see an indication that the properties had been changed.
Advanced Installer offers some support for this. Basically, through its XML editor you can use Windows Installer properties instead of hard-coded values in your manifest files.
The other solution I can think of is to use a custom action to modify the files after install.
Either way, this requires a complex installer, like an MSI package. ClickOnce doesn't support it.
If you want to deploy your BDC to a specific siteURL when you deploy it, go to your project folder for your bcd model when you view your solution and in the properties of the folder you should see something called "Feature Properties".
Click on the elipsis to expand the properties and add a heading called "SiteUrl" and set it to be the root of the site you want to deploy it too: i.e "http://spsite".
It will be deployed to that site instead of the Local one.
In our case, we implemented a custom Feature Receiver using instructions located at SharePoint 2010, deploying a BCS Model using a farm property bag to have a dynamic siteurl
It allows to deploy in any environment because the Site Url is discovered during the feature activation.