<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lab49 Blog &#187; MSBuild</title>
	<atom:link href="http://blog.lab49.com/archives/category/msbuild/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.lab49.com</link>
	<description>Technology and industry insights from Lab49.</description>
	<lastBuildDate>Sat, 04 Feb 2012 23:02:03 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>How PowerShell can Automate Deployment to Multiple Windows Azure Environments</title>
		<link>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/fPzgpAWCTfM/</link>
		<comments>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/fPzgpAWCTfM/#comments</comments>
		<pubDate>Fri, 25 Feb 2011 14:04:56 +0000</pubDate>
		<dc:creator>Doug Finke</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[MSBuild]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.dougfinke.com/blog/?p=1185</guid>
		<description><![CDATA[The two blog posts below are providing insight into how to create an automated delivery pipeline to the Cloud, Microsoft Azure. I believe the same fundamentals can and ought be applied to the software development life cycle whether you have a hybrid or Enterprise model. The first post is the updated SDK and PowerShell cmdlets [...]]]></description>
			<content:encoded><![CDATA[<p>The two blog posts below are providing insight into how to create an automated delivery pipeline to the Cloud, <a href="http://www.microsoft.com/en-us/cloud/developer/default.aspx?CR_CC=200028940&amp;WT.srch=1&amp;WT.mc_id=85C588D4-A353-4E3D-8824-823A9551AA51&amp;CR_SCC=200028940">Microsoft Azure</a>. I believe the same fundamentals can and ought be applied to the software development life cycle whether you have a hybrid or Enterprise model.</p>
<p>The first post is the updated SDK and PowerShell cmdlets for MSBuild and Azure.</p>
<p>The second is a walk through showing how to automatically build and deploy your Windows Azure applications to  multiple accounts and use configuration transforms to modify your  Service Definition and Service Configuration files.</p>
<ul>
<li>Scott Densmore <a href="http://goo.gl/Fm5JQ">- Another Update for the Deployment scripts for Windows Azure </a></li>
<li>Tom Hollander &#8211; <a href="http://goo.gl/h0tNg">Using MSBuild to deploy to multiple Windows Azure environments </a></li>
</ul>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fwww.dougfinke.com%2Fblog%2Findex.php%2F2011%2F02%2F25%2Fhow-powershell-can-automate-deployment-to-multiple-windows-azure-environments%2F&amp;title=How%20PowerShell%20can%20Automate%20Deployment%20to%20Multiple%20Windows%20Azure%20Environments"><img src="http://www.dougfinke.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a> </p>
<p><img src="http://feeds.feedburner.com/~r/DevelopmentInABlink/~4/fPzgpAWCTfM" height="1" width="1"/></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/fPzgpAWCTfM/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Note to self: How to programmatically get the MSBuild path in PowerShell</title>
		<link>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/ANxv4qm7UHU/</link>
		<comments>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/ANxv4qm7UHU/#comments</comments>
		<pubDate>Thu, 02 Dec 2010 02:01:14 +0000</pubDate>
		<dc:creator>Doug Finke</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[MSBuild]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.dougfinke.com/blog/index.php/2010/12/01/note-to-self-how-to-programmatically-get-the-msbuild-path-in-powershell/</guid>
		<description><![CDATA[Other ways I’ve seen have been to get the directory from the registry. I prefer this approach. Function Get-MSBuild { $lib = [System.Runtime.InteropServices.RuntimeEnvironment] $rtd = $lib::GetRuntimeDirectory() Join-Path $rtd msbuild.exe } This gets the directory for the latest installed .NET runtime and joins it with msbuild.exe. Usage &#038; (Get-MSBuild) Microsoft (R) Build Engine Version 4.0.30319.1 [Microsoft [...]]]></description>
			<content:encoded><![CDATA[<p>Other ways I’ve seen have been to get the directory from the registry. I prefer this approach.</p>
<pre class="PowerShellColorizedScript"><span style="color: #00008b">Function</span> <span style="color: #8a2be2">Get-MSBuild</span> <span style="color: #000000">{</span>
    <span style="color: #ff4500">$lib</span> <span style="color: #a9a9a9">=</span> <span style="color: #008080">[System.Runtime.InteropServices.RuntimeEnvironment]</span>
    <span style="color: #ff4500">$rtd</span> <span style="color: #a9a9a9">=</span> <span style="color: #ff4500">$lib</span><span style="color: #a9a9a9">::</span><span style="color: #000000">GetRuntimeDirectory</span><span style="color: #000000">(</span><span style="color: #000000">)</span>
    <span style="color: #0000ff">Join-Path</span> <span style="color: #ff4500">$rtd</span> <span style="color: #8a2be2">msbuild.exe</span>
<span style="color: #000000">}</span></pre>
<p>This gets the directory for the latest installed .NET runtime and joins it with msbuild.exe.</p>
<h3>Usage</h3>
<pre style="width: 539px; height: 184px" class="PowerShellColorizedScript"><span style="color: #a9a9a9">&amp;</span> <span style="color: #000000">(</span><span style="color: #0000ff">Get-MSBuild</span><span style="color: #000000">)</span>            

<span style="color: #8b0000">Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

MSBUILD : error MSB1003: Specify a project or solution file.
The current working directory does not contain a project or solution file.</span></pre>
<p><img src="http://feeds.feedburner.com/~r/DevelopmentInABlink/~4/ANxv4qm7UHU" height="1" width="1"/></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/ANxv4qm7UHU/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Build Visual Studio Projects on Remote Boxes Using PowerShell and MSBuild</title>
		<link>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/Nuimm5IQzfM/</link>
		<comments>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/Nuimm5IQzfM/#comments</comments>
		<pubDate>Thu, 21 Oct 2010 00:20:22 +0000</pubDate>
		<dc:creator>Doug Finke</dc:creator>
				<category><![CDATA[MSBuild]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://www.dougfinke.com/blog/index.php/2010/10/20/build-visual-studio-projects-on-remote-boxes-using-powershell-and-msbuild/</guid>
		<description><![CDATA[We are setting up a new Continuous Build server and as we tweak parameters, things happen. For example, false positives. The build worked on the server but fails locally. As a sanity check and major time saver, this script runs MSBuild on a remote computer. You specify the name of the remote box and the [...]]]></description>
			<content:encoded><![CDATA[</p>
<p>We are setting up a new <a href="http://en.wikipedia.org/wiki/Continuous_integration">Continuous Build</a> server and as we tweak parameters, things happen. For example, false positives. The build worked on the server but fails locally. </p>
<p>As a sanity check and major time saver, this script runs MSBuild on a remote computer. You specify the name of the remote box and the Visual Studio solution path and filename. </p>
<pre style="width: 362px; height: 21px" class="PowerShellColorizedScript"><span style="color: #0000ff">Invoke-RemoteMSBuild</span> <span style="color: #8a2be2">ServerX</span> <span style="color: #8a2be2">G:\Build\Test.sln</span></pre>
<h3>No Fuss, No Muss</h3>
<p>You run this script on your box, the MSBuild runs remotely. The MSBuild output is returned to your local box. No remoting in, no login, simple and easy.</p>
<pre style="width: 559px; height: 282px" class="PowerShellColorizedScript"><span style="color: #00008b">Function</span> <span style="color: #8a2be2">Invoke-RemoteMSBuild</span> <span style="color: #000000">{</span>
    <span style="color: #00008b">param</span> <span style="color: #000000">(</span>
        <span style="color: #ff4500">$computerName</span> <span style="color: #a9a9a9">,</span>
        <span style="color: #ff4500">$vsSolution</span> <span style="color: #a9a9a9">=</span> <span style="color: #000000">$(</span><span style="color: #00008b">throw</span> <span style="color: #8b0000">&quot;Please specify the solution file&quot;</span><span style="color: #000000">)</span>
    <span style="color: #000000">)</span>            

    <span style="color: #0000ff">Invoke-Command</span> <span style="color: #000080">-ComputerName</span> <span style="color: #ff4500">$computerName</span> <span style="color: #000000">{</span>
        <span style="color: #00008b">param</span> <span style="color: #000000">(</span><span style="color: #ff4500">$vsSolution</span><span style="color: #000000">)</span>            

        <span style="color: #ff4500">$msbuildpath</span> <span style="color: #a9a9a9">=</span> <span style="color: #8b0000">&quot;$($env:windir)\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe&quot;</span>
        <span style="color: #a9a9a9">&amp;</span> <span style="color: #ff4500">$msbuildpath</span> <span style="color: #ff4500">$vsSolution</span>            

    <span style="color: #000000">}</span> <span style="color: #000080">-ArgumentList</span> <span style="color: #ff4500">$vsSolution</span>
<span style="color: #000000">}</span></pre>
<p>You need to have PowerShell installed on both boxes, run Enable-PSRemoting on both boxes and you need to be in the admin group on the server.</p>
<p>Thanks to colleague <a href="http://blog.lab49.com/archives/2650">Jason Dolinger</a> who is quickly ramping up on PowerShell and asked if this could be done.</p>
<p><img src="http://feeds.feedburner.com/~r/DevelopmentInABlink/~4/Nuimm5IQzfM" height="1" width="1"/></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/Nuimm5IQzfM/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PowerShell and MSBuild Extensible Task Factories</title>
		<link>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/NCvOy1Iw_JQ/</link>
		<comments>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/NCvOy1Iw_JQ/#comments</comments>
		<pubDate>Sat, 20 Feb 2010 13:30:36 +0000</pubDate>
		<dc:creator>Doug Finke</dc:creator>
				<category><![CDATA[MSBuild]]></category>
		<category><![CDATA[PowerShell]]></category>

		<guid isPermaLink="false">http://dougfinke.com/blog/index.php/2010/02/20/powershell-and-msbuild-extensible-task-factories/</guid>
		<description><![CDATA[One of the cool new features of MSBuild 4.0 is the extensible task factory. Writing your own task factory will allow you to write inline tasks in Perl, Python, or in this case… Windows PowerShell. 
Blog post MSBuild Task Factories: guest starring Windows Powershell. 
Sample task factory on MSDN Code Gallery MSBuild Windows PowerShell Task [...]]]></description>
			<content:encoded><![CDATA[<p>One of the cool new features of MSBuild 4.0 is the extensible task factory. Writing your own task factory will allow you to write inline tasks in Perl, Python, or in this case… Windows PowerShell. </p>
<p>Blog post <a href="http://blogs.msdn.com/visualstudio/archive/2010/02/20/msbuild-task-factories-guest-starring-windows-powershell.aspx?utm_source=twitterfeed&amp;utm_medium=twitter">MSBuild Task Factories: guest starring Windows Powershell</a>. </p>
<p>Sample task factory on MSDN Code Gallery <a href="http://code.msdn.microsoft.com/PowershellFactory/Release/ProjectReleases.aspx?ReleaseId=3926">MSBuild Windows PowerShell Task Factory</a>.</p>
<p><img src="http://feeds.feedburner.com/~r/DevelopmentInABlink/~4/NCvOy1Iw_JQ" height="1" width="1"/></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/DevelopmentInABlink/~3/NCvOy1Iw_JQ/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Top 5 Secrets of .Net Desktop Deployment Wizards</title>
		<link>http://feedproxy.google.com/~r/ScottWeinstein/~3/shXnFg0t0og/top-5-secrets-of-net-desktop-deployment-wizards.aspx</link>
		<comments>http://feedproxy.google.com/~r/ScottWeinstein/~3/shXnFg0t0og/top-5-secrets-of-net-desktop-deployment-wizards.aspx#comments</comments>
		<pubDate>Mon, 25 Aug 2008 03:45:00 +0000</pubDate>
		<dc:creator>Administrator</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[MSBuild]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[<p>Deployment is one of the software project taxes that are often neglected or shunted aside to have another team deal with.</p> <p>There are two big downsides when a team does not directly own it's deployment. The first is obvious, if the quality of the deployment infrastructure isn't any good, users perception of your software will suffer. Rightfully so, any you find yourself spending time fixing bugs that are only tangential to your project. </p> <p>The second risk has to do with how outsourcing (to another group, another department or other company) deployment infrastructure can affect your project.&#160; If the amount of time it takes to get a new deployment ready is more than an hour - you'll find yourself adjusting the release schedule to accommodate this - typically in the direction of longer delays between releases.&#160; No need to rehash the downside of that. </p> <p>Here are some tips to taking control of deployment.</p> <p><strong>Secret #1</strong></p> <blockquote> <p>To install to the desktop, use ClickOnce.</p></blockquote> <p>You no longer need to be dependent on the installation and packaging department.</p> <p>The core ClickOnce technology continues to improve from its inception in .Net 2.0 - In .Net 3.5 SP1 ClickOnce has support for the reduced client profile, FireFox installs, and file type registration.</p> <p>&#160;</p> <p>For enterprise environments there are a few things that you'll need/want to do to make ClickOnce really work for you</p> <p>The most important thing is having support for multiple environments - this isn't built in, and if you attempt to deploy two different ClickOnce builds with the same deployment name to different sites, the latest build will take precedence and effectively overwrite the existing deployment on the desktop.</p> <p>The fix for this is relatively straightforward - you need to provide different deployment name for each build. Like so -</p><pre>&#60;MSBuild 
   Projects="ClickOnce.csproj"
   Targets="Publish"
   Properties="
            MinimumRequiredVersion=$(MinimumRequiredVersion);
            ApplicationVersion=$(ApplicationVersion);
            ApplicationRevision=$(ApplicationRevision);
            CodeBranch=$(CodeBranch);
            DeployEnv=$(DeployEnv)
            AssemblyName=ClickOnce.$(DeployEnv);
            PublishUrl=$(PublishUrl);
            ProductName=ClickOnce $(CodeBranch) $(DeployEnv)" /&#62;
</pre>
<p>The one limitation of this approach is that project references will no longer work. Use file based assembly refs, and it'll be fine.</p>
<p>For a more polished look obtain a code signing certificate. Instead of getting an install dialog that says "Unknown publisher", you can indicate the name of your group. It also make the auditors feel warm and fuzzy.</p>
<p>And finally, you can make your desktop software URL addressable. Even though most workflow software in the enterprise has moved to the web, the need for desktops software still exists, and often this desktop software can benefit by supporting URLs.&#160; And rather than telling your colleague how to navigate to what you're looking at, or sending a screen shot of what you're looking at, you could send them a URL - which would open up to the screen you're using.</p>
<p>If you were installing the software via windows installer you could rely on modifying the registry and <a href="http://msdn.microsoft.com/en-us/library/aa767914.aspx" target="_blank">register a protocol handler</a>.&#160; That approach won't work for non-administrator ClickOnce installs, but you can work around that limitation by enabling "<a href="http://msdn.microsoft.com/en-us/library/ms172242.aspx" target="_blank">URL parameters to be passed to the application</a>" (thanks to my co-worker Eugene for discovering this one)</p>
<p>Update - Cedric in the comments wonders if ClickOnce limits the application to a ".Net Sandbox?" In many ways, not really. In full trust mode, ClickOnce applications can run unmanaged code via pinvoke and Reg-Free COM. For applications that truly need unmanaged component installs you can use the setup bootstrapper, or if you are in an enterprise situation, install the core components separately from the application.</p>
<p><strong>Secret #2</strong></p>
<blockquote>
<p>To get the server components and ClickOnce files to the deployment site, use WiX to prepare a <a href="http://en.wikipedia.org/wiki/Windows_Installer" target="_blank">Windows Installer</a> packages.</p></blockquote>
<p>Sure, you could use Visual Studio Setup Projects, or even shell out some real money for InstallShield or Wise software. But WiX has a number of nice features</p>
<ol>
<li>cost 
<li>text (well xml) based files so you can do the decent diffs and source control on them 
<li>active community support 
<li>reasonable learning curve - start with everyone's <a href="http://www.tramontana.co.hu/wix/" target="_blank">favorite WiX tutorial</a></li></ol>
<p><strong>Secret #3</strong></p>
<blockquote>
<p><a href="http://weblogs.asp.net/sweinstein/archive/2008/08/24/dealing-with-the-configuration-nightmare.aspx" target="_blank">Get your configuration files under control</a></p></blockquote>
<p><strong>Secret #4</strong></p>
<blockquote>
<p>Although WiX is great, sometimes installs just too difficult to be done in Windows Installer technology. In this case, use PowerShell.</p></blockquote>
<p>If the install involves modifying xml files, executing database commands, or invoking web services and it's well outside the normal range for windows installer. Read than trying to force everything into one technology, simply distribute a PowerShell setup scripts in the installer package. Run them from the msi or by hand afterwards.</p>
<p><strong>Secret #5</strong></p>
<blockquote>
<p>Working on deployment can help your career.</p></blockquote>
<p>It's true.&#160; Responsibility and influence on a project naturally flow to those that get things done (not sure if it matter that you're <a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html" target="_blank">smart</a> <a href="http://steve-yegge.blogspot.com/2008/06/done-and-gets-things-smart.html" target="_blank">or not</a>). Few things are a more visible indication getting things done then a release, and if you own deployment, other project items will naturally flow your way. </p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2ftop-5-secrets-of-net-desktop-deployment-wizards.aspx"><img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2ftop-5-secrets-of-net-desktop-deployment-wizards.aspx" border="0"></a><img src="http://weblogs.asp.net/aggbug.aspx?PostID=6562058" width="1" height="1"><div class="feedflare">
<a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=jIKjJlbt"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=41" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=PXnTYAXX"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=43" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=wRDPhlgn"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=52" border="0"></img></a>
</div><img src="http://feeds2.feedburner.com/~r/ScottWeinstein/~4/shXnFg0t0og" height="1">]]></description>
			<content:encoded><![CDATA[<p>Deployment is one of the software project taxes that are often neglected or shunted aside to have another team deal with.</p>
<p>There are two big downsides when a team does not directly own it&#8217;s deployment. The first is obvious, if the quality of the deployment infrastructure isn&#8217;t any good, users perception of your software will suffer. Rightfully so, any you find yourself spending time fixing bugs that are only tangential to your project. </p>
<p>The second risk has to do with how outsourcing (to another group, another department or other company) deployment infrastructure can affect your project.&nbsp; If the amount of time it takes to get a new deployment ready is more than an hour &#8211; you&#8217;ll find yourself adjusting the release schedule to accommodate this &#8211; typically in the direction of longer delays between releases.&nbsp; No need to rehash the downside of that. </p>
<p>Here are some tips to taking control of deployment.</p>
<p><strong>Secret #1</strong></p>
<blockquote><p>To install to the desktop, use ClickOnce.</p>
</blockquote>
<p>You no longer need to be dependent on the installation and packaging department.</p>
<p>The core ClickOnce technology continues to improve from its inception in .Net 2.0 &#8211; In .Net 3.5 SP1 ClickOnce has support for the reduced client profile, FireFox installs, and file type registration.</p>
<p>&nbsp;</p>
<p>For enterprise environments there are a few things that you&#8217;ll need/want to do to make ClickOnce really work for you</p>
<p>The most important thing is having support for multiple environments &#8211; this isn&#8217;t built in, and if you attempt to deploy two different ClickOnce builds with the same deployment name to different sites, the latest build will take precedence and effectively overwrite the existing deployment on the desktop.</p>
<p>The fix for this is relatively straightforward &#8211; you need to provide different deployment name for each build. Like so -</p>
<pre>&lt;MSBuild
   Projects="ClickOnce.csproj"
   Targets="Publish"
   Properties="
            MinimumRequiredVersion=$(MinimumRequiredVersion);
            ApplicationVersion=$(ApplicationVersion);
            ApplicationRevision=$(ApplicationRevision);
            CodeBranch=$(CodeBranch);
            DeployEnv=$(DeployEnv)
            AssemblyName=ClickOnce.$(DeployEnv);
            PublishUrl=$(PublishUrl);
            ProductName=ClickOnce $(CodeBranch) $(DeployEnv)" /&gt;
</pre>
<p>The one limitation of this approach is that project references will no longer work. Use file based assembly refs, and it&#8217;ll be fine.</p>
<p>For a more polished look obtain a code signing certificate. Instead of getting an install dialog that says &#8220;Unknown publisher&#8221;, you can indicate the name of your group. It also make the auditors feel warm and fuzzy.</p>
<p>And finally, you can make your desktop software URL addressable. Even though most workflow software in the enterprise has moved to the web, the need for desktops software still exists, and often this desktop software can benefit by supporting URLs.&nbsp; And rather than telling your colleague how to navigate to what you&#8217;re looking at, or sending a screen shot of what you&#8217;re looking at, you could send them a URL &#8211; which would open up to the screen you&#8217;re using.</p>
<p>If you were installing the software via windows installer you could rely on modifying the registry and <a href="http://msdn.microsoft.com/en-us/library/aa767914.aspx">register a protocol handler</a>.&nbsp; That approach won&#8217;t work for non-administrator ClickOnce installs, but you can work around that limitation by enabling &#8220;<a href="http://msdn.microsoft.com/en-us/library/ms172242.aspx">URL parameters to be passed to the application</a>&#8221; (thanks to my co-worker Eugene for discovering this one)</p>
<p>Update &#8211; Cedric in the comments wonders if ClickOnce limits the application to a &#8220;.Net Sandbox?&#8221; In many ways, not really. In full trust mode, ClickOnce applications can run unmanaged code via pinvoke and Reg-Free COM. For applications that truly need unmanaged component installs you can use the setup bootstrapper, or if you are in an enterprise situation, install the core components separately from the application.</p>
<p><strong>Secret #2</strong></p>
<blockquote>
<p>To get the server components and ClickOnce files to the deployment site, use WiX to prepare a <a href="http://en.wikipedia.org/wiki/Windows_Installer">Windows Installer</a> packages.</p>
</blockquote>
<p>Sure, you could use Visual Studio Setup Projects, or even shell out some real money for InstallShield or Wise software. But WiX has a number of nice features</p>
<ol>
<li>cost
<li>text (well xml) based files so you can do the decent diffs and source control on them
<li>active community support
<li>reasonable learning curve &#8211; start with everyone&#8217;s <a href="http://www.tramontana.co.hu/wix/">favorite WiX tutorial</a></li>
</ol>
<p><strong>Secret #3</strong></p>
<blockquote>
<p><a href="http://weblogs.asp.net/sweinstein/archive/2008/08/24/dealing-with-the-configuration-nightmare.aspx">Get your configuration files under control</a></p>
</blockquote>
<p><strong>Secret #4</strong></p>
<blockquote>
<p>Although WiX is great, sometimes installs just too difficult to be done in Windows Installer technology. In this case, use PowerShell.</p>
</blockquote>
<p>If the install involves modifying xml files, executing database commands, or invoking web services and it&#8217;s well outside the normal range for windows installer. Read than trying to force everything into one technology, simply distribute a PowerShell setup scripts in the installer package. Run them from the msi or by hand afterwards.</p>
<p><strong>Secret #5</strong></p>
<blockquote>
<p>Working on deployment can help your career.</p>
</blockquote>
<p>It&#8217;s true.&nbsp; Responsibility and influence on a project naturally flow to those that get things done (not sure if it matter that you&#8217;re <a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html">smart</a> <a href="http://steve-yegge.blogspot.com/2008/06/done-and-gets-things-smart.html">or not</a>). Few things are a more visible indication getting things done then a release, and if you own deployment, other project items will naturally flow your way. </p>
<p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2ftop-5-secrets-of-net-desktop-deployment-wizards.aspx"><img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2ftop-5-secrets-of-net-desktop-deployment-wizards.aspx" border="0"></a><img src="http://weblogs.asp.net/aggbug.aspx?PostID=6562058" width="1" height="1">
<div class="feedflare">
<a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=jIKjJlbt"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=41" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=PXnTYAXX"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=43" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=wRDPhlgn"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=52" border="0"></img></a>
</div>
<p><img src="http://feeds2.feedburner.com/~r/ScottWeinstein/~4/shXnFg0t0og" height="1"></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/ScottWeinstein/~3/shXnFg0t0og/top-5-secrets-of-net-desktop-deployment-wizards.aspx/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dealing with the Configuration Nightmare</title>
		<link>http://feedproxy.google.com/~r/ScottWeinstein/~3/dov3BGdhZ5o/dealing-with-the-configuration-nightmare.aspx</link>
		<comments>http://feedproxy.google.com/~r/ScottWeinstein/~3/dov3BGdhZ5o/dealing-with-the-configuration-nightmare.aspx#comments</comments>
		<pubDate>Sun, 24 Aug 2008 22:00:00 +0000</pubDate>
		<dc:creator>Administrator</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[MSBuild]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[<p>Typically during an overview of some new technology they will say "<strong>and it's totally configurable</strong>" at which point my inner sarcastic voice pipes up in response "great, now we have two problems". </p> <p>Configuration files are getting bigger and more complex. In some ways this is a good thing as there's a move towards a declarative approach, but in other ways it's unneeded as the cost of compilation has gone down. The real problems with the move towards configurable everything is the configuration files often lack designer support, and do not get verified at compile time. </p> <p>I can't help with the lack of designers or verification, but I do have some solutions for managing the proliferation of config files and multiple environments.</p> <p>In a typical enterprise development project, you may have as many as five environments to execute against.</p> <ol> <li>localDev - the PC you develop and unit test on  <li>sharedDev - a shared environment with minimal expectations around quality  <li>UAT/QA -&#160; a shared an environment with explicit quality expectations - typically this means that developers publish a list of known bugs to testers before the release and the new bugs discovered by testers will be addressed  <li>Production - this system that users use  <li>COB/DR - the backup system that users will use "in case of"</li></ol> <p>&#160;</p> <p>I've seen a number of approaches for dealing with configuration files and multiple environments</p> <p>&#160;</p> <blockquote> <p>0.&#160; Edit the configuration file by hand right after you deploy.</p></blockquote> <p>If this approach works for you, then god bless. You can stop reading this post. On the other hand, if this turns your hair gray, or if you're deploying technology has has tamper protection (such as ClickOnce), then keep reading</p> <blockquote> <p>1. Put all environmental information into the single configuration file. Typically this approach it is combined with a UI addition allowing the user the option to select which environment to use.</p></blockquote> <p>This approach has some merits. Everything is in a single location, so it's easy to find and maintain.&#160; On the other hand this approach makes it way too easy to run un-tested development code against a production environment, and doesn't completely solve the configuration problem - and that it doesn't tell a server component what environment it's currently running in.</p> <p>&#160;</p> <blockquote> <p>2.&#160; Keep 5 separate configuration files, one for each environment.</p></blockquote> <p>This approach looks the <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" target="_blank">DRY principle</a> in the eye, and says " what are you going to do about it?" It's fine if you're feeling tough, and in the mood for a maintenance headache. For example if you add a new logging sink, you need to edit 5 files.</p> <p>&#160;</p> <blockquote> <p>3. Treat your configuration files as code to be compiled</p></blockquote> <p>Benefits: no duplication of effort, automated, safe, unambiguous, auditable. </p> <p>Cons: Takes about 1 developer day to setup.</p> <p>Here's how it works. The configuration files for each assembly are always kept in <strong>localDev</strong> configuration. A new file, call it Deployment.Properties is the added of root of the solution.&#160; For each assembly, a new file is applied called DeployTransforms.Properties is added.</p> <p>The <strong>Deployment.Properties</strong> file is kept in MSBuild format and represents the golden definition of environment settings. It might look something like this</p><pre>&#60;PropertyGroup Condition="'$(DeployEnv)' == 'sharedDev'"&#62;
  &#60;AccountsDBServer&#62;accountsdev.company.com&#60;/AccountsDBServer&#62;
&#60;/PropertyGroup&#62;
&#60;PropertyGroup Condition="'$(DeployEnv)' == 'UAT'"&#62;
      &#60;AccountsDBServer&#62;accountsUAT.company.com&#60;/AccountsDBServer&#62;
&#60;/PropertyGroup&#62;
&#60;PropertyGroup Condition="'$(DeployEnv)' == 'Prod'"&#62;
      &#60;AccountsDBServer&#62;accounts.company.com&#60;/AccountsDBServer&#62;
&#60;/PropertyGroup&#62;
&#60;PropertyGroup&#62;
  &#60;AccountsDBCS&#62;Data Source='$(AccountsDBServer)';Integrated Security=True;&#60;/AccountsDBCS&#62;
&#60;/PropertyGroup&#62;
 
</pre>
<p>The <strong>DeployTransforms.Properties</strong> file, also kept in MSBuild format defines how the golden values from the Deployment.Properties are applied to the [App&#124;Web].config file.</p>
<p>It might look like this: </p><pre>&#60;ItemGroup&#62;
  &#60;ConfigTransform Include="/configuration/connectionStrings/add[@name='AccountsConnectionString']/@connectionString"&#62;
    &#60;Value&#62;$(AccountsDBCS)&#60;/Value&#62;
  &#60;/ConfigTransform&#62;
&#60;/ItemGroup&#62;</pre>
<p>Of course you would add additional properties and config transforms as needed.</p>
<p>The final steps are to include into your project the DeployTransforms.Properties as well as a DeployTransforms target. That might look like this: </p><pre>&#60;Import Project="ConfigTransform.properties" /&#62;
&#60;Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /&#62;
&#60;Import Project="ConfigTransform.targets" /&#62;
&#60;Target Name="Publish" DependsOnTargets="$(NonClickOnceDependsOn)"&#62;
</pre>
<p>and <strong>ConfigTransform.targets</strong></p><pre>&#60;Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" /&#62;

&#60;PropertyGroup&#62;
  &#60;PublishDependsOn Condition="'$(PublishableProject)'=='true'"&#62;
    DeployTransform;
    $(PublishDependsOn);
    RollbackConfigFile;
  &#60;/PublishDependsOn&#62;

  &#60;NonClickOnceDependsOn&#62;
    NonClickOnceDeployTransform;
  &#60;/NonClickOnceDependsOn&#62;

  &#60;ConfigFile&#62;App.config&#60;/ConfigFile&#62;
  &#60;ConfigFileName&#62;$(ConfigFile)&#60;/ConfigFileName&#62;
&#60;/PropertyGroup&#62;

&#60;Target Name="DeployTransform" &#62;
  &#60;Copy SourceFiles="$(ConfigFile)" DestinationFolder="$(IntermediateOutputPath)" /&#62;
  &#60;Attrib Files="$(ConfigFile)" ReadOnly="false"/&#62;
  &#60;CallTarget Targets="UpdateConfigFile" /&#62;
&#60;/Target&#62;

&#60;Target Name="NonClickOnceDeployTransform" &#62;
  &#60;CallTarget Targets="SetConfigFileName" /&#62;
  &#60;CallTarget Targets="UpdateConfigFile" /&#62;
&#60;/Target&#62;

&#60;Target Name="SetConfigFileName"&#62;
  &#60;CreateProperty Value="@(AppConfigWithTargetPath-&#62;'$(OutDir)%(TargetPath)')"&#62;
    &#60;Output TaskParameter="Value" PropertyName="ConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
  &#60;CreateProperty Value="$(TeamBuildPublishDir)$(AssemblyName).exe.config" Condition="$(ConfigFileName)=='' and $(TeamBuildPublishDir) !=''"&#62;
    &#60;Output TaskParameter="Value" PropertyName="ConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
  &#60;CreateProperty Value="$(OutputPath)$(AssemblyName).exe.config" Condition="$(ConfigFileName)=='' and $(TeamBuildPublishDir) ==''"&#62;
    &#60;Output TaskParameter="Value" PropertyName="ConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
  &#60;CreateProperty Value="$(TeamBuildPublishDir)\_PublishedWebsites\$(MSBuildProjectName)\Web.config" Condition="$(WebApplication)=='true' and $(TeamBuildPublishDir) !=''"&#62;
    &#60;Output TaskParameter="Value" PropertyName="ConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
  &#60;CreateProperty Value="App.config"&#62;
    &#60;Output TaskParameter="Value" PropertyName="SrcConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
  &#60;CreateProperty Value="Web.config" Condition="$(WebApplication)=='true'"&#62;
    &#60;Output TaskParameter="Value" PropertyName="SrcConfigFileName" /&#62;
  &#60;/CreateProperty&#62;
&#60;/Target&#62;


&#60;Target Name="UpdateConfigFile"&#62;
  &#60;Error Text="%(ConfigTransform.Identity) has no value for DeployEnv: '$(DeployEnv)'" Condition="'%(ConfigTransform.Value)' == ''" /&#62;
  &#60;CallTarget Targets="CopyConfigFile"/&#62;
  &#60;XmlUpdate
        Prefix="n"
        Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
        XmlFileName="$(ConfigFileName)"
        Xpath="%(ConfigTransform.Identity)"
        Value="%(ConfigTransform.Value)" /&#62;
&#60;/Target&#62;

&#60;Target Name="CopyConfigFile"
        Outputs="$(ConfigFileName)"
        Inputs="ConfigTransform.properties;..\DeployEnv.properties"&#62;
  &#60;Copy SourceFiles="$(SrcConfigFileName)" DestinationFiles="$(ConfigFileName)" /&#62;
&#60;/Target&#62;
</pre><a href="http://11011.net/software/vspaste"></a>
<p>This approach is invoked via msbuild /t:Publish /p:deployEnv=(UAT&#124;Prod...)</p>
<p>The outputs of the build are now configured for the intended environment.&#160; </p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2fdealing-with-the-configuration-nightmare.aspx"><img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2fdealing-with-the-configuration-nightmare.aspx" border="0"></a><img src="http://weblogs.asp.net/aggbug.aspx?PostID=6561182" width="1" height="1"><div class="feedflare">
<a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=kIMSweFe"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=41" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=18ySDBCr"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=43" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=N9nyZ2PL"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=52" border="0"></img></a>
</div><img src="http://feeds2.feedburner.com/~r/ScottWeinstein/~4/dov3BGdhZ5o" height="1">]]></description>
			<content:encoded><![CDATA[<p>Typically during an overview of some new technology they will say &#8220;<strong>and it&#8217;s totally configurable</strong>&#8221; at which point my inner sarcastic voice pipes up in response &#8220;great, now we have two problems&#8221;. </p>
<p>Configuration files are getting bigger and more complex. In some ways this is a good thing as there&#8217;s a move towards a declarative approach, but in other ways it&#8217;s unneeded as the cost of compilation has gone down. The real problems with the move towards configurable everything is the configuration files often lack designer support, and do not get verified at compile time. </p>
<p>I can&#8217;t help with the lack of designers or verification, but I do have some solutions for managing the proliferation of config files and multiple environments.</p>
<p>In a typical enterprise development project, you may have as many as five environments to execute against.</p>
<ol>
<li>localDev &#8211; the PC you develop and unit test on
<li>sharedDev &#8211; a shared environment with minimal expectations around quality
<li>UAT/QA -&nbsp; a shared an environment with explicit quality expectations &#8211; typically this means that developers publish a list of known bugs to testers before the release and the new bugs discovered by testers will be addressed
<li>Production &#8211; this system that users use
<li>COB/DR &#8211; the backup system that users will use &#8220;in case of&#8221;</li>
</ol>
<p>&nbsp;</p>
<p>I&#8217;ve seen a number of approaches for dealing with configuration files and multiple environments</p>
<p>&nbsp;</p>
<blockquote><p>0.&nbsp; Edit the configuration file by hand right after you deploy.</p>
</blockquote>
<p>If this approach works for you, then god bless. You can stop reading this post. On the other hand, if this turns your hair gray, or if you&#8217;re deploying technology has has tamper protection (such as ClickOnce), then keep reading</p>
<blockquote><p>1. Put all environmental information into the single configuration file. Typically this approach it is combined with a UI addition allowing the user the option to select which environment to use.</p>
</blockquote>
<p>This approach has some merits. Everything is in a single location, so it&#8217;s easy to find and maintain.&nbsp; On the other hand this approach makes it way too easy to run un-tested development code against a production environment, and doesn&#8217;t completely solve the configuration problem &#8211; and that it doesn&#8217;t tell a server component what environment it&#8217;s currently running in.</p>
<p>&nbsp;</p>
<blockquote><p>2.&nbsp; Keep 5 separate configuration files, one for each environment.</p>
</blockquote>
<p>This approach looks the <a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">DRY principle</a> in the eye, and says &#8221; what are you going to do about it?&#8221; It&#8217;s fine if you&#8217;re feeling tough, and in the mood for a maintenance headache. For example if you add a new logging sink, you need to edit 5 files.</p>
<p>&nbsp;</p>
<blockquote><p>3. Treat your configuration files as code to be compiled</p>
</blockquote>
<p>Benefits: no duplication of effort, automated, safe, unambiguous, auditable. </p>
<p>Cons: Takes about 1 developer day to setup.</p>
<p>Here&#8217;s how it works. The configuration files for each assembly are always kept in <strong>localDev</strong> configuration. A new file, call it Deployment.Properties is the added of root of the solution.&nbsp; For each assembly, a new file is applied called DeployTransforms.Properties is added.</p>
<p>The <strong>Deployment.Properties</strong> file is kept in MSBuild format and represents the golden definition of environment settings. It might look something like this</p>
<pre>&lt;PropertyGroup Condition="'$(DeployEnv)' == 'sharedDev'"&gt;
  &lt;AccountsDBServer&gt;accountsdev.company.com&lt;/AccountsDBServer&gt;
&lt;/PropertyGroup&gt;
&lt;PropertyGroup Condition="'$(DeployEnv)' == 'UAT'"&gt;
      &lt;AccountsDBServer&gt;accountsUAT.company.com&lt;/AccountsDBServer&gt;
&lt;/PropertyGroup&gt;
&lt;PropertyGroup Condition="'$(DeployEnv)' == 'Prod'"&gt;
      &lt;AccountsDBServer&gt;accounts.company.com&lt;/AccountsDBServer&gt;
&lt;/PropertyGroup&gt;
&lt;PropertyGroup&gt;
  &lt;AccountsDBCS&gt;Data Source='$(AccountsDBServer)';Integrated Security=True;&lt;/AccountsDBCS&gt;
&lt;/PropertyGroup&gt;
</pre>
<p>The <strong>DeployTransforms.Properties</strong> file, also kept in MSBuild format defines how the golden values from the Deployment.Properties are applied to the [App|Web].config file.</p>
<p>It might look like this: </p>
<pre>&lt;ItemGroup&gt;
  &lt;ConfigTransform Include="/configuration/connectionStrings/add[@name='AccountsConnectionString']/@connectionString"&gt;
    &lt;Value&gt;$(AccountsDBCS)&lt;/Value&gt;
  &lt;/ConfigTransform&gt;
&lt;/ItemGroup&gt;</pre>
<p>Of course you would add additional properties and config transforms as needed.</p>
<p>The final steps are to include into your project the DeployTransforms.Properties as well as a DeployTransforms target. That might look like this: </p>
<pre>&lt;Import Project="ConfigTransform.properties" /&gt;
&lt;Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /&gt;
&lt;Import Project="ConfigTransform.targets" /&gt;
&lt;Target Name="Publish" DependsOnTargets="$(NonClickOnceDependsOn)"&gt;
</pre>
<p>and <strong>ConfigTransform.targets</strong></p>
<pre>&lt;Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" /&gt;

&lt;PropertyGroup&gt;
  &lt;PublishDependsOn Condition="'$(PublishableProject)'=='true'"&gt;
    DeployTransform;
    $(PublishDependsOn);
    RollbackConfigFile;
  &lt;/PublishDependsOn&gt;

  &lt;NonClickOnceDependsOn&gt;
    NonClickOnceDeployTransform;
  &lt;/NonClickOnceDependsOn&gt;

  &lt;ConfigFile&gt;App.config&lt;/ConfigFile&gt;
  &lt;ConfigFileName&gt;$(ConfigFile)&lt;/ConfigFileName&gt;
&lt;/PropertyGroup&gt;

&lt;Target Name="DeployTransform" &gt;
  &lt;Copy SourceFiles="$(ConfigFile)" DestinationFolder="$(IntermediateOutputPath)" /&gt;
  &lt;Attrib Files="$(ConfigFile)" ReadOnly="false"/&gt;
  &lt;CallTarget Targets="UpdateConfigFile" /&gt;
&lt;/Target&gt;

&lt;Target Name="NonClickOnceDeployTransform" &gt;
  &lt;CallTarget Targets="SetConfigFileName" /&gt;
  &lt;CallTarget Targets="UpdateConfigFile" /&gt;
&lt;/Target&gt;

&lt;Target Name="SetConfigFileName"&gt;
  &lt;CreateProperty Value="@(AppConfigWithTargetPath-&gt;'$(OutDir)%(TargetPath)')"&gt;
    &lt;Output TaskParameter="Value" PropertyName="ConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
  &lt;CreateProperty Value="$(TeamBuildPublishDir)$(AssemblyName).exe.config" Condition="$(ConfigFileName)=='' and $(TeamBuildPublishDir) !=''"&gt;
    &lt;Output TaskParameter="Value" PropertyName="ConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
  &lt;CreateProperty Value="$(OutputPath)$(AssemblyName).exe.config" Condition="$(ConfigFileName)=='' and $(TeamBuildPublishDir) ==''"&gt;
    &lt;Output TaskParameter="Value" PropertyName="ConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
  &lt;CreateProperty Value="$(TeamBuildPublishDir)\_PublishedWebsites\$(MSBuildProjectName)\Web.config" Condition="$(WebApplication)=='true' and $(TeamBuildPublishDir) !=''"&gt;
    &lt;Output TaskParameter="Value" PropertyName="ConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
  &lt;CreateProperty Value="App.config"&gt;
    &lt;Output TaskParameter="Value" PropertyName="SrcConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
  &lt;CreateProperty Value="Web.config" Condition="$(WebApplication)=='true'"&gt;
    &lt;Output TaskParameter="Value" PropertyName="SrcConfigFileName" /&gt;
  &lt;/CreateProperty&gt;
&lt;/Target&gt;

&lt;Target Name="UpdateConfigFile"&gt;
  &lt;Error Text="%(ConfigTransform.Identity) has no value for DeployEnv: '$(DeployEnv)'" Condition="'%(ConfigTransform.Value)' == ''" /&gt;
  &lt;CallTarget Targets="CopyConfigFile"/&gt;
  &lt;XmlUpdate
        Prefix="n"
        Namespace="http://schemas.microsoft.com/developer/msbuild/2003"
        XmlFileName="$(ConfigFileName)"
        Xpath="%(ConfigTransform.Identity)"
        Value="%(ConfigTransform.Value)" /&gt;
&lt;/Target&gt;

&lt;Target Name="CopyConfigFile"
        Outputs="$(ConfigFileName)"
        Inputs="ConfigTransform.properties;..\DeployEnv.properties"&gt;
  &lt;Copy SourceFiles="$(SrcConfigFileName)" DestinationFiles="$(ConfigFileName)" /&gt;
&lt;/Target&gt;
</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This approach is invoked via msbuild /t:Publish /p:deployEnv=(UAT|Prod&#8230;)</p>
<p>The outputs of the build are now configured for the intended environment.&nbsp; </p>
<p><a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2fdealing-with-the-configuration-nightmare.aspx"><img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fweblogs.asp.net%2fsweinstein%2farchive%2f2008%2f08%2f24%2fdealing-with-the-configuration-nightmare.aspx" border="0"></a><img src="http://weblogs.asp.net/aggbug.aspx?PostID=6561182" width="1" height="1">
<div class="feedflare">
<a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=kIMSweFe"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=41" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=18ySDBCr"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=43" border="0"></img></a> <a href="http://feeds2.feedburner.com/~f/ScottWeinstein?a=N9nyZ2PL"><img src="http://feeds2.feedburner.com/~f/ScottWeinstein?d=52" border="0"></img></a>
</div>
<p><img src="http://feeds2.feedburner.com/~r/ScottWeinstein/~4/dov3BGdhZ5o" height="1"></p>
]]></content:encoded>
			<wfw:commentRss>http://feedproxy.google.com/~r/ScottWeinstein/~3/dov3BGdhZ5o/dealing-with-the-configuration-nightmare.aspx/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SilverLight tests + PowerShell = Automated tests in MSBuild</title>
		<link>http://blog.lab49.com/archives/2292</link>
		<comments>http://blog.lab49.com/archives/2292#comments</comments>
		<pubDate>Mon, 26 May 2008 17:57:16 +0000</pubDate>
		<dc:creator>Sergey Kogan</dc:creator>
				<category><![CDATA[MSBuild]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Silverlight]]></category>

		<guid isPermaLink="false">http://blog.lab49.com/?p=2292</guid>
		<description><![CDATA[In this post I will only discuss the way to configure SilverLight unit tests to run completely automated from MSBuild, so that it can also be used as part of continuous integration setup. SilverLight tests must run within an actual SilverLight plugin rather then .NET Framework. Fortunately, Microsoft provided a test framework and source code [...]]]></description>
			<content:encoded><![CDATA[<p>In this post I will only discuss the way to configure SilverLight unit tests to run completely automated from MSBuild, so that it can also be used as part of continuous integration setup.</p>
<p>SilverLight tests must run within an actual SilverLight plugin rather then .NET Framework. Fortunately, Microsoft provided a test framework and source code for the both SilverLight controls and unit tests.</p>
<p>It is great to get support for TDD for such a new technology as SilverLight 2.0 right from the start. However, it is not possible to run SL tests from MSBuild out of the box. But do not panic, there is an easy solution to thid using PowerShell. PowerShell allows you to use COM objects and fortunately IE comes with a COM API that we can take advantage of. Below is a PowerShell script that creates and redirects IE browser to the SL test page, re-formats the test result output and saves it as an html file. It also returns an exit code &gt; 0 in case of test failures.</p>
<p>param($testURL, $outFile=&#8221;TestReport.html&#8221;, $visible=$true)</p>
<p>#Instanciate IE, set Visible mode (tests that interact with desktop will require $true to pass<br />
#then navigate to the test page<br />
$ie = new-object -com &#8220;InternetExplorer.Application&#8221; <br />
$ie.Visible = $visible <br />
$ie.Navigate($testURL)  </p>
<p>#poll the content until test execution is complete.<br />
#Note: using $ie.Busy won&#8217;t work, because it does not detect that SL application is still running.<br />
#Furthermore, the SL application is running until you close the browser, even after all the tests complete<br />
while (!($ie.Document.body.innerHTML -match &#8220;\[ Close \]&#8220;))<br />
{<br />
[System.Threading.Thread]::Sleep(100)<br />
}</p>
<p>#Once the execution is complete, we can check the results<br />
#and, thanks to artist rendition by D. Finke, display some shining colors!<br />
$failure = $ie.Document.title -match &#8220;(\d*) failures&#8221;<br />
if($failure) {Write-Host -NoNewline -ForegroundColor &#8220;red&#8221; &#8220;Failed $matches[1]&#8220;}<br />
else {Write-Host -NoNewline -ForegroundColor &#8220;green&#8221; &#8220;Pass   &#8220;}</p>
<p>#Heavy RegEx stuff to reformat the body HTML.<br />
#The test results in the SL test framework are displayed  as a narrow column on the right with some<br />
#error messages wrapped into context pop-up or limited in width.<br />
$regOpt = [System.Text.RegularExpressions.RegexOptions]<br />
[regex]::Replace($ie.Document.body.innerHTML, &#8216;^.*&lt;DIV style=&#8221;RIGHT.*?&lt;/DIV&gt;&lt;/DIV&gt;&#8217;, &#8220;&#8221;, $regOpt::Singleline) -ireplace &#8220;width: 324px&#8221;, &#8220;&#8221; -ireplace &#8216;&lt;SPAN title=\&#8221;(.*?)\&#8221;.*?\(Mouse over for assert trace\)&lt;/SPAN&gt;&#8217;, &#8216;$1&#8242; -ireplace &#8216;&lt;/DIV&gt;\w*$&#8217;, &#8220;&#8221; &gt; &#8220;$outFile&#8221;</p>
<p>#return error code in case of failure so that MSBuild and other tools can detect it. <br />
Write-Host $testUrl<br />
$ie.Quit()<br />
if ($failure) { exit 1 }</p>
<p>Now, after we have out PowerShell script we can call it from MSBuild and have a completely automated SL unit test setup.<br />
Note: If you will be running it from a service, i.e. TeamCity build agent, you should enable the service to interact with desktop.</p>
<p>Below is a snippet from MSBuild configuration file that calls the SL unit tests via PowerShell. Also, you will need to set PowerShell execution policy to allow the script to run using Set-ExecutionPolicy cmdlet. Check google for available policies.</p>
<p>&lt;Target Name=&#8221;RunUnitTests&#8221;&gt;  <br />
  <br />
  &lt;Exec IgnoreExitCode=&#8221;True&#8221; Command=&#8217;Powershell .\GetSLTestReport.ps1 &#8220;[Your Location]\TestPage.html&#8221; ControlTestResults.html&#8217;&gt;<br />
   &lt;Output TaskParameter=&#8221;ExitCode&#8221; PropertyName=&#8221;ControlTestExitCode&#8221;/&gt;<br />
  &lt;/Exec&gt;</p>
<p>  &lt;Error Condition=&#8221;$(ControlTestExitCode) &gt; &#8217;0&#8242;&#8221; Text=&#8221;SL Unit Tests run failed with code $(ControlTestExitCode)&#8221;/&gt;<br />
  &lt;/Target&gt;</p>
<p>Note: I am using IgnoreExitCode option to allow other tests/tasks to run in case ControlTestResults.html fails. If you do not need this, you can remove IgnoreExitCode and  &lt;Error&gt; tag.<br />
=&gt;</p>
<p>&lt;Target Name=&#8221;RunUnitTests&#8221;&gt;  <br />
  <br />
  &lt;Exec IgnoreExitCode=&#8221;True&#8221; Command=&#8217;Powershell .\GetSLTestReport.ps1 &#8220;[Your Location]\TestPage.html&#8221; ControlTestResults.html&#8217; /&gt;<br />
  &lt;/Target&gt;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.lab49.com/archives/2292/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MSBuild : Item Groups on the fly</title>
		<link>http://blog.lab49.com/archives/683</link>
		<comments>http://blog.lab49.com/archives/683#comments</comments>
		<pubDate>Sat, 28 Oct 2006 02:12:35 +0000</pubDate>
		<dc:creator>Doug Finke</dc:creator>
				<category><![CDATA[.Net]]></category>
		<category><![CDATA[MSBuild]]></category>

		<guid isPermaLink="false">http://blog.lab49.com/?p=683</guid>
		<description><![CDATA[MSBuild is part of our continuous integration process. Syncing to Subversion&#8217;s latest changes is done using the SvnCheckout Task (MSBuild Community Tasks Project). The revision number is then captured. Subsequent tasks create&#160;staging/deployment targets, based on the revision &#8211; C:\deployment\389. This approach allows us to quickly review a directory tree and locate releases by revision. &#160; [...]]]></description>
			<content:encoded><![CDATA[</p>
<p>MSBuild is part of our continuous integration process. Syncing to Subversion&#8217;s latest changes is done using the SvnCheckout Task (<a href="http://msbuildtasks.tigris.org/">MSBuild Community Tasks Project</a>). The revision number is then captured.
<p>Subsequent tasks create&nbsp;staging/deployment targets, based on the revision &#8211; C:\deployment\389. This approach allows us to quickly review a directory tree and locate releases by revision.
<p>&nbsp;<br />
<h2>Automation Challenge</h2>
<p>MSBuild Item Groups allow wild cards, for example **\*.*&nbsp;tells MSBuild to build a recursive list of all files in all subdirectories. The&nbsp;wrinkle&nbsp;is MSBuild resolves Item Groups when the script is loaded. </p>
<p>Our targets are created during the running of the script. So the directory contents cannot be resolved at load time. </p>
<p>The solution, create Item Groups on the fly with the <a href="http://msdn2.microsoft.com/en-us/library/s2y3e43x.aspx">CreateItem Task</a>.
<p><font face="Arial" size="1">&nbsp;&lt;CreateItem Include=&#8221;$(DistributionFolder)\$(Revision)\**\*.*&#8221;&gt;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&lt;Output TaskParameter=&#8221;Include&#8221; ItemName=&#8221;RevisionedDistributionFolderFiles&#8221;/&gt;<br />&nbsp;&lt;/CreateItem&gt;</font>
<p>This creates a named Item Group <em>RevisionedDistributionFolderFiles</em> and resolves the target directory structure for use in later tasks. </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.lab49.com/archives/683/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

