I recently answered a question on StackOverflow about altering a WebDeploy package which spawned the idea for this post. Most developers consider WebDeploy to be nothing short of magic and have no real understanding of its inner workings. To be honest, I still don’t feel like I have a “full” understanding of WebDeploy but I have gained a deeper understanding than most developers through our use of WebDeploy/MSDeploy for my team’s continous delivery pipeline at EBSCO.
NOTE – My setup for this blog post is an empty web application in VS2015 and WebDeploy 3.5. I have been using this tech for 4+ years at this point and am not aware of any big changes to these capabilites since VS2010. My expectation is that this should all work back to VS2010. If you find that is not the case please let me know so I can update this post.
I wrote a quick post last year that described the relationship between MSDeploy/WebDeploy and how we extended MSDeploy for SQL database and non-web deployments. In summary, WebDeploy (the marketing term) includes MSDeploy binaries, MSBuild extensions (*.targets files), Visual Studio extensions and the Web Deployment Agent Service. Technically, the targets are part of the VS Web Project not WebDeploy but they are closely related.
Trigger WebDeploy to Create a Package
For a web project you can instruct the build to include the creation of a WebDeploy package by adding the “/t:Package” argument to the MSBuild call:
This produces several new folders in the [project_root]obj[configuration] folder:
WebDeploy supports config transformation by default and provides the TransformWebConfig folder with the original and transformed versions of the config file. Config transformations are provided in the default ASP.NET project templates. Its a great technology and served us well for a long time but there is a better way.
WebDeploy also includes support for Parameterization. The main difference between config transforms and Parameterization is when they perform the transformation. Config transforms occur at build-time while Parameterization occurs at deploy-time. WebDeploy not only supports custom Parameterization it also creates some parameters for you by default. Specifically for the ConnectionStrings in your config. The CSAutoParameterization folder provides the original config and any automatic parameterization transformations. Here is a comparison of the original and transformed configs:
In this case you can see the bottom line is the transformed connection which replaces the previous connection string with a token. I’m not sure if the token name/format has any significance. Regardless, this is now provided as a parameter that can be changed during the final package deployment. I have personally found this auto parameterization for connections strings to be more confusing than helpful for most developers. Thankfully you can disable this “feature”.
WebDeploy can also be configured to include database creation scripts (either custom or generated from a database). I know few developers who actually use this functionality. In the end I think there are better options out there for database change management and deployment so I won’t dig into this.
The Package Folder
The Package folder contains the actual package and supporting files.
The PackageTmp folder contains the files that were built and zipped into the package file. This includes the transformed config file, any static web files and the compiled binaries. In other words this is an exact copy of what will deployed by your package, excluding any parameterization changes that will occur at deploy-time.
The next file is the [projectName].deploy.cmd batch command that is really just a wrapper for calling msdeploy.exe. If you’re not familiar with batch files and open this file it will look very intimidating. In the end it makes it easier for almost anyone to deploy the package by providing arguments and flags. You can read the [projectName].deploy-readme.txt file for all the details of each option. If you dig down in the cmd file you will see the following line which calls msdeploy.exe with the source set to the package zip file and the destination set to a variable which is set from an argument passed to the batch file.
"%MSDeployPath%msdeploy.exe" -source:package='%RootPath%WebApplication7.zip' -dest:%_Destination% -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension %_MsDeployAdditionalFlags%
Next we have the [projectName].SetParameters.xml file which provides the default the Parameterization values. The cmd file uses this file for Parameterization by default. You can edit the file manually to edit it for the target server. We typically create and checkin a SetParameters file for each of our environments with the appropriate values. Here is the default set parameters file for our example project:
<xml version="1.0" encoding=&quot;utf-8&quot;> &lt;parameters&gt; &lt;setParameter name=&quot;IIS Web Application Name&quot; value=&quot;Default Web Site/WebApplication7_deploy&quot; /&gt; &lt;setParameter name=&quot;AppSetting-Setting1&quot; value=&quot;debug1&quot; /&gt; &lt;setParameter name=&quot;MyDatabaseConnection-Web.config Connection String&quot; value=&quot;Data Source=.sqlexpress;Initial Catalog=MyDatabase;Integrated Security=True;Pooling=false;&quot; /&gt; &lt;/parameters&gt;
Lastly in the Package folder is the [projectName].SourceManifest.xml file which defines what MSDeploy providers are in the package in simplified form. There are many MSDeploy Providers but a default WebDeploy package only includes the iisApp and setAcl providers. The first creates the IIS site and deploys the package to the site. The setAcl provider sets the directory permissions on the target server to match the source. Sayed Hashimi has a great post on extending the source manifest, check it out!
Note – you can exclude the ACLs from the package by adding the following MSBuild property to the project (csproj) file or the [projectName].wpp.targets file.
<PropertyGroup> <IncludeSetAclProviderOnDestination>False</IncludeSetAclProviderOnDestination> </PropertyGroup>
The MSDeploy package is just a zip file. If we open the [projectName].zip file you will see there is a Content folder which includes the files from the PackageTemp folder in a structure that mimics the absolute path of the folder.
The archive.xml file is an internal version of the source manifest file with additional internal details.
The parameters.xml file declares the Parameterization parameter entries that can be set via the SetParameters file. This is a combination of your custom parameters.xml file and the parameters WebDeploy creates automatically.
If you have enabled the database deployment feature you will also have any SQL scripts that are included in the package.
Lastly the systemInfo.xml file has system information about IIS on the system where the package was created. I believe this is informational only and not used during the deployment of a package. You can also pull this information using the following command on a target system and compare with the info in the package to ensure IIS compatibility. MSDeploy may do some of this checking automatically but I’ve never seen any documentation or errors to prove this.
msdeploy -verb:getsysteminfo -source:webServer
Manually Deploy a Package
We primarily use the cmd files during our deployments but there is no reason you can’t deploy the package using MSDeploy.exe. If you try to manually deploy the package directly to IIS it will fail with the following error:
>"C:Program Files (x86)IISMicrosoft Web Deploy V3msdeploy.exe" -verb:sync -source:package=WebApplication7.zip -dest:iisApp=Site1/Example Error: Source (sitemanifest) and destination (iisApp) are not compatible for the given operation. Error count: 1.
You receive the same error if you try to deploy the package to a directory as well. This is where most developers give up and move on. But its not hard to accomplish this. First you need to understand how the Manifest provider works. Simply put, a manifest is just a list of other providers that must have a matching manifest to deploy to. So WebDeploy is actually defining a list of providers (iisApp, setAcl, dbFullSql, depending on how its configured) and then packaging that manifest. You must define a destination manifest to deploy the package with.
The easiest way I’ve found to accomplish this is make a copy of the sourceManifest with file name [projectName].destManifest.xml and replace the iisApp provider line with a drPath
<?xml version="1.0" encoding="utf-8"?> <sitemanifest> <IisApp path="C:tempmanualPackageDeploymentDir" /> </sitemanifest>
Then call MSDeploy again passing the destination manifest:
>"C:Program Files (x86)IISMicrosoft Web Deploy V3msdeploy.exe" -verb:sync -source:package=WebApplication7.zip -dest:manifest=WebApplication7.DestManifest.xml Info: Adding virtual path (C:tempPackageTmp) Info: Adding directory (C:tempPackageTmp). Info: Adding directory (C:tempPackageTmpbin). Info: Adding file (C:tempPackageTmpbinMicrosoft.CodeDom.Providers.DotNetCompilerPlatform.dll). ... Total changes: 29 (29 added, 0 deleted, 0 updated, 0 parameters changed, 11389090 bytes copied)
Success! This can be helpful if you want to test a deployment to a local folder before updating a site. You could also use this to merge additional files into the package as described in the StackOverflow post mentioned at the beginning of this post.
WebDeploy/MSDeploy is a powerful tool that you can use to save time/effort, especially if you know how it works and bend it to your will. I challenge you to try it for your next deployment and really seek to understanding how its working. If you have issues, questions or other feedback please submit a comment on this post or on Twitter to continue this discussion.