Tiny Happy Features #3 - Publishing Improvements, chained Config Transforms and Deploying ASP.NET Apps from the Command Line
(UPDATE: See other Tiny Happy Features)
At some point soon lots of people are going to start writing these epic blog posts about Visual Studio 2012. They will include LOTS of screenshots (some good and some bad), some small code samples and minimal context. I can't speak for other teams; I can only talk about what we worked on. The <AngleBrackets/> folks in Azure Platform and Tools (ASP.NET, IIS, WCF, EF, Azure much and more) have been putting a lot of work into what I sometimes call "Death by a Thousand Tiny Cuts." It's the little irritants that are as frustrating (or more so) as the big missing features.
Rather than a giant super post (although I'll do that at some point) I thought I'd showcase some Tiny Happy Features that the team worked on just because it made life better. Some are large some are small, but all are tiny happy features.
Publishing and Deployment Improvements
Visual Studio 2012 has a number of subtle but significant improvements to publishing. Of course you likely know you can push code to Azure or AppHarbor using Git. You can publish your web app with Web Deploy which I demonstrated at Mix in my talk Web Deployment Made Awesome: If You're Using XCopy, You're Doing It Wrong. You can see a quick closed-captioned video I did of Web Deployment to Azure in just 4 minutes over here.
Visual Studio added web.config transforms a while back so you can have a Web.config along with a Web.debug.config with the changes you want for debug time, and web.release.config for release time. You can add the free SlowCheetah add-on and get transform support for all your config files, not just web.config.
However, things fell down because Web Deploy and Visual Studio didn't have a way to easily represent Dev, Testing, Staging, Production, Whatever. You could have build configurations but they didn't relate to deployments, which doesn't reflect a developer's reality.
Publishing with Environment-specific Configurations
Publish Profiles are either created manually, or now in VS2012 downloaded as .publishsettings from your host and imported into Visual Studio. In Visual Studio 2012 publish profiles are stored along with your code in the Properties folder so they can be checked in and used by others.
You can rename a profile whatever you like by changing the name. Here I've named my publish profile "Production." There's the coolest part.
I can make a Web.config file with the same name as my Publish Profile and that transform will be run after the build transform.
Even better you can right click on a Transform now and select Preview Transform and see not only the results, but a Diff of the results.
See right there, and in the large image above? It says "Transformed Web.config ( transforms applied: Web.Release.config, Web.Production.config)." Build transforms happen first, then Publish Transforms. Right now you need to create the file with the same name yourself but we'll be adding tooling for this.
This means I can do things in Web.debug.config like changing compilation options while Web.production.config is for changing connection strings, setting log levels, and adding specific Production settings.
Deploying from the Command Line
Deploying your web application from the command line has long been possible but it's been a pretty obscure and frustrating affair. Now that the Publish Profiles can live with the project and Web.config transforms can be chained in you can publish from the command line much easier.
msbuild MySolution.sln /p:DeployOnBuild=true;PublishProfile=Production;Password=poo
If you have a untrusted certificate on the deployment server you haven't added to your local certs you'll need to add AllowUntrustedCertificate=true as an acknowledgement.
I just make a Deploy.bat
that looks like this with a %1 for the Profile Name.
msbuild MySolution.sln /p:DeployOnBuild=true;PublishProfile=%1;AllowUntrustedCertificate=true;Password=poo
Then I can just do
Deploy Production
or
Deploy Staging
I've found these outwardly small but impactful changes have made it a lot easier for me to deploy sites. Deployment shouldn't be hard. I stand by my original statement: If you are using XCopy (or Windows Explorer or FileZilla) to deploy your website, you're doing it wrong.
If you are using Web Sites rather than Web Products, head over to the Web Team blog (and subscribe, too) and add your voice to the comments around our teams' plans regarding Website projects and Web Deployment Projects.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
If you get the chance, could you ask someone on the Publishing Pipeline team to take a look at the following Stack Overflow question? Basically, I'd like to "publish" to a package and later "publish" that package to another profile (without rebuilding it).
http://stackoverflow.com/questions/11683243/deploying-an-existing-package-using-publish-profiles
I looked at your question and it made me realize that accomplishing what you want is more difficult than what I thought. One of the tricky bits is to construct the full MSDeploy service URL. But I have created an add in which I think you may find useful. Check out my 5 minute video. I could update that to support .pubxml/.publishSettings files. Let me know what you think. I could also try and put a solution together strictly in MSBuild. Let me know what you prefer.
FYI Our VS publish experience is "out-of-band", meaning that we can update it later easily, so we will be shipping updates on a regular basis. One feature that we are considering adding is to let you see the file on your PC and the file on the server which is getting updates in a diff viewer. You would get this functionality on the publish dialog, during the file preview. For web.config it will show you the diff of the web.config on your local PC after MSDeploy updates the values with parameters and the web.config on the server. Would this meet your needs?
I don't actually mind holding onto the pubxml. The main issue is that there's not a solid enough separation of "package" and "deploy" in System.Web.Publishing.targets
Specifically, the "MSDeployPublish" target can't simply write out a SetParameters and use the package as a source, it has to also _define_ the parameters (which causes other problems).
I'm happy to continue this chat on the Stack Overflow question, as that site supports a chat mode.
We have to use a few "hacks" in our project files now to transform files like conenctionstrings.config and whatnot just cause of this silly hard-coded limitation of only transforming the file named 'web.config'
This limitation to only web.config makes no sense at all since the transforms only happen when you publish/deploy, not when you just build.
It's a great feature that falls short due to a pointless file name limitation.
Dumb question: how is this practically any different from having one build configuration per deploy environment? If my deploy is "msbuild my.sln ..." as above, then deployment and build are the same step in my workflow.
What I would really like is a build step that creates a package "tarball" that is either debug or release bits. Then to deploy it to staging, I take that tarball and apply a config transformation for the staging environment. Then when QA wants to, they can take a tarball that has been approved on staging and deploy it to production, *with no changes or recompilation* other than applying the production config transformation. They should be able to do this with 100% certainty that the ONLY difference between staging and release has been that final config transformation.
If instead build and deploy are one step as above, then every deployment package is a unique output of the *compiler*, not just of config transformation.
I feel that I must be missing something really basic. :-)
1. Is there a way to have devops manage production credentials without them being in the solution or at least not be accessible by devs?
2. Is there a way to use this in VS2010?
Do you know if there is a way to make publish wizard to copy App_offline.htm while it is publishing the site?
But let me to describe a related publishing issue which We've faced with it in our recent project. When I start to publish our MVC App using VS through FTP, It takes very very long time to complete and most of the time I'll regret it and try XCopy instead :( It should be much faster! I don't know what's the problem.
FYI you should look at my 5 minute video at http://sedodream.com/2012/03/14/PackageWebUpdatedAndVideoBelow.aspx
The main issues with using the standard targets for this are outlined in the script's comments, but are:
UseDeclareParametersXMLInMsDeploy=true doesn't work because MSDeployPublish->ExportParametersFile uses DeclareSetParameterFile which causes the arguments to be _declared_ (not set). Changing this to SetParameterFile fixes it, but only makes sense when publishing from a package.
UseDeclareParametersXMLInMsDeploy=false doesn't work because MSDeployPublish->VSMSDeploy and MSDeployPublish->MSdeploy both use SetParameterItems for inlined parameters, which doesn't seem to apply them. Changing this to SimpleSetParameterItems fixes the issue (which may also not be relevant to non-package based publishing).
Let me know if either of these issues are fixed in the 15/08 release and I'll update my script accordingly.
Fortunately you can get this support from my add in at SlowCheetah. I realize its not ideal (because it's an add-in) but that is where we are at.
Sayed Ibrahim Hashimi
But is there any way that we could publish our websites/apps to our own servers in a "AppHarbor-stylish way" ?
I mean so that we see if an application is working correcly and can revert to a previous version if we need to?
But I don't see that you solved how anyone can save credentials for production environment in TFS yet, without writing custom scripts or making a mess in TFS.
I'm trying to deploy a Web Role to Azure and the only way to have the web.config being transformed as I want is to go to the project and define a build configuration for each different environment, which is exactly what I did before VS2012 anyway. And all that just to have the web.config transformed for each different environment.
Is there a reason the Windows Azure Tools don't support this otherwise great feature?
Checkout my 5 minute video. http://sedodream.com/2012/03/14/PackageWebUpdatedAndVideoBelow.aspx
If you are interested in this being included in VS please let us know!!! You can vote at http://aspnet.uservoice.com/forums/41199-general-asp-net/suggestions/3474938-add-the-ability-to-transform-web-config-without-re
"1. Is there a way to have devops manage production credentials without them being in the solution or at least not be accessible by devs?"
We do not have a good story for this today.
2. Is there a way to use this in VS2010?
Yes you can get this exact experience in VS2010. Just install the Azure SDK for VS2010 from http://www.windowsazure.com/en-us/develop/net/#.
If you are not seeing this please email me at sayedha[at]{microsoftDOTcom}.
I get following error message from msbuild:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(609,5):error : The OutputPath property is not set for project 'MyProject.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Debug' Platform='BWS'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project.
Tried adding the .pubxml - suffix and installing (& importing) MSBuild.Community.Tasks as suggested in this post without success.
Another attempt was to add /p:VisualStudioVersion=11.0 as mentioned by Sayed Ibrahim Hashimi in that post.
To be sure to be up to date I also updated to ASP.NET and Web Tools 2012.2 as published here.
My recent command looks like this:
msbuild absolute\path\to\MyProject.csproj /p:VisualStudioVersion=11.0;DeployOnBuild=true;PublishProfile=profile
Can anyone point out if I'm doing something obviously wrong?
Govinda
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<ExcludeApp_Data>False</ExcludeApp_Data>
<publishUrl>\\myServer\c$\inetpub\wwwroot\myWebsite</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
So it really seems like msbuild is just not recognising/using the profile.
Ultimately I want the following
Package.zip
Dev.SetParameters.xml
Dev.deploy.cmd
UAT.SetParameters.xml
UAT.deploy.cmd
PROD.SetParameters.xml
PROD.deploy.cmd
So that for each check in we get one package, that can be moved from environment to environment by Developer, Tester or Support Staff dependant on an approval process.
I am interested in the PackageWeb scenario, but do not like the manual PowerShell step, we are looking for a One Click solution that can be implemented across environments.
Unfortunatelly your one line msbuild command:
msbuild MySolution.sln /p:DeployOnBuild=true;PublishProfile=Production;Password=poo
... does not pick up the Build configuration assigned to the Publish Profile. The proper way to go would be:
msbuild MySolution.sln /p:DeployOnBuild=true;PublishProfile=Production;Password=poo;Configuration=Production
Thats actually quite frustrating and should be fixed.
Regards
Mike
Comments are closed.