Specify different path for provider iisApp when creating package with msdeploy
How I make the package
I make the msdeploy package like this:
msdeploy.exe -verb:sync -source:iisApp=c:\content\ -dest:package=c:\pkg.zip
The c:\content directory has a single index.html file.
Result
The output looks like this:
Info: Adding package (package).
Info: Adding child iisApp (c:\content\).
Info: Adding child createApp (c:\content\).
Info: Adding child contentPath (c:\content\).
Info: Adding child dirPath (c:\content\).
Info: Adding child filePath (c:\content\index.html).
Total changes: 6 (6 added, 0 deleted, 0 updated, 0 parameters changed, 0 bytes copied)
If I extract the content of c:\pkg.zip into directory c:\pkg it looks like this:
archive.xml
systemInfo.xml
Content\c_C
Content\c_C\content
Content\c_C\content\index.html
If I dump the package like this:
msdeploy.exe -verb:dump -source:package=c:\pkg.zip -xml
I get:
<output>
<MSDep开发者_如何学Cloy.iisApp>
<iisApp path="c:\content\">
<createApp
path="c:\content\"
isDest="False"
managedRuntimeVersion=""
enable32BitAppOnWin64=""
managedPipelineMode=""
applicationPool=""
appExists="True" />
<contentPath path="c:\content\">
<dirPath
path="c:\content\"
securityDescriptor="D:"
parentSecurityDescriptors=""
attributes="Directory">
<filePath
path="index.html"
size="0"
attributes="Archive"
lastWriteTime="07/07/2011 20:58:00"
securityDescriptor="D:" />
</dirPath>
</contentPath>
</iisApp>
</MSDeploy.iisApp>
</output>
How I want it to be
I don't want the package to depend upon the current location of the site files. I'm going to send the package to a customer, and I don't want any detailes about the packaging process to get shipped with the package. I want the content of the package c:\pkg.zip to be like this:
archive.xml
systemInfo.xml
Content\index.html
I want the package to be able to create an IIS application, so I need a virtual path. I also want to install the package into the default location. So the physical path also has to change. I want the dump to look something like this:
<output>
<MSDeploy.iisApp>
<iisApp path="Default Web Site\Site">
<createApp
path="Default Web Site\Site"
isDest="False"
managedRuntimeVersion=""
enable32BitAppOnWin64=""
managedPipelineMode=""
applicationPool=""
appExists="False" />
<contentPath path="c:\inetpub\wwwroot\site">
<dirPath
path="c:\inetpub\wwwroot\site"
securityDescriptor="D:"
parentSecurityDescriptors=""
attributes="Directory">
<filePath
path="index.html"
size="0"
attributes="Archive"
lastWriteTime="07/07/2011 20:58:00"
securityDescriptor="D:" />
</dirPath>
</contentPath>
</iisApp>
</MSDeploy.iisApp>
</output>
I have changed the iisApp and createApp provider path attributes to be Default Web Site\Site
. And I changed the contentPath and dirPath provider path attributes to be c:\inetpub\wwwroot\site
.
Questions
- How can I accomplish this?
You need to look at MS Deploy replace rules, a useful feature well hidden on the MS Deploy Team Blog.
In your case, you will need to extend your command line with a pile of replace expressions, something like this:
msdeploy.exe
-verb:sync
-source:iisApp=c:\content\
-dest:package=c:\pkg.zip
-replace:objectName=iisApp,targetAttributeName=path,
replace="Default Website\Site"
-replace:objectName=createApp,targetAttributeName=path,
replace="Default Website\Site"
-replace:objectName=contentPath,targetAttributeName=path,
replace="c:\inetpub\wwwroot\site"
-replace:objectName=dirPath,targetAttributeName=path,match="^c:\content",
replace="c:\inetpub\wwwroot\site"
Running this should produce your desired output.
In the above sample, the first 3 replace rules match by tag name (objectName
) and attribute name (targetAttributeName
), and overwrites with the specified replace string.
The last replace rule will match all path attributes of all dirPath
tags beginning with "c:\content" and will replace only that part of the attribute value with the replace string.
Finally, I haven't found a way to avoid having the package zip-file contain the original source folder names. The only workaround would be to package from a neutral, temporary location like "c:\site".
So the procedure is:
- Copy your stuff to a neutral, temporary location.
- Create your package from here.
- Use the verb:dump to see the generated xml.
- Create your package again with added replace rules for everything you want changed in the package.
- Take a headache pill ;-)
I had more or less the same problems.
First things first:
The long deployment-machine-specific paths
To that effect, I used a trick found at http://sedodream.com/2013/01/13/WebPackagingFixingTheLongPathIssue.aspx
As suggested in the post, one can modify the desired (you can have several) .pubxml file under the Properties/PublishProfiles folder in your project. This is the approach I followed since it allowed me to customize the behavior per publishing profile.
If I'm not mistaken though, I believe you can apply the same modification to the {project-name}.wpp.targets file (which probably doesn't exists yet) on the project root directory. Changes here though, affect the web publishing pipeline (wpp) and thus all publishing profiles found in the project.
However...
This approach is just about to spoil your deployment when it comes time to replace your connection strings with those provided by your publishing profile. The reason: the above trick doesn't affect connection strings since they are being created automatically by the wpp at build time. Buh-huh!
The solution I found for that problem was twofold:
1.) Created a parameters.xml file where I manually declared the connection strings. Ok, maybe I copied them from the parameters.xml file within my package's .zip file since I was deploying to a package. That helped.
They look somegthing like this:
<parameter name="myConnection-Web.config Connection String" defaultValue="" tags="SqlConnectionString"
description="myConnection Connection String used in web.config by the application to access the database.">
<parameterEntry kind="XmlFile" scope="DeploymentPackage\\Web\.config$" match="/configuration/connectionStrings/add[@name='myConnection']/@connectionString" />
</parameter>
2.) Included the following line at the top of the same .pubxml file we modified earlier
<AutoParameterizationWebConfigConnectionStrings>false</AutoParameterizationWebConfigConnectionStrings>
And... Voilà!
Create ISS App
With the above approach hopefully you declared several parameters, including the connection strings.
When you create a package, however, regardless of wheter you created a parameters.xml or not, a *.SetParameters.xml template file is created for you. Within it you will see as the very first parameter the "IIS Web Application Name", which will default to whatever you inserted in your publishing profile. You can change that; to whatever you want.
Remember I said template before? I meant it; it's just a template. You're suppose to take that *.SetParameters.xml file and make as many copies of it as needed. What are they for? Environment related parameters. You could have a:
- DEV.SetParameters.xml
- QA.SetParameters.xml
- Staging.SetParameters.xml
- Production.SetParameters.xml
- ... and so on and so forth
and then use the parameters file best suited for the job (or the environment) like so:
{yourProjectName}.deploy.cmd /Y /M:{targetServer} [...] -setParamFile:QA.SetParameters.xml
or its equivalent MsDeploy command line of course.
Now, by default, the manifest created for you at build time, and stashed within your package under the archive.xml file, will use an iisApp provider first and foremost. This is good, cause this provider, unlike the createApp provider, will actually create the directory for you if it doesn't exist. At least according to this note from TechNet:
"Unlike the iisApp provider, if the physical folder for the new application does not exist, the createApp provider does not create a physical folder underneath the folder of the parent site; it only creates a reference in configuration to such a folder. If you want a physical folder created, you will have to create it manually before or after using createApp. For this reason, you should normally use the iisApp provider instead. The iisApp provider is the more appropriate choice because it uses the createApp provider as an initial step in a series of steps that include the creation of the application in configuration, the creation of a physical folder for the application if the folder does not exist, and the copying of content files into the folder of the new application."
I would be happy to include the links... but since I don't have 10+ points I'm allowed only one per post. Go figure! :)
So, in short...
... by the work done on the first part, you probably won't need to do much in order to have the folder created at deploy time in the target server.
In case you do need to override that though, you can either define your own manifest file and deploy off of it (a separate topic)... or you can follow @peter_raven advice and override its value using the -Replace
rules from MsDeploy.
Either one would work as a charm.
The package prefix is removed by supplying the kind, scope, and match properties as shown below:
"msdeploy.exe" \
-verb:sync \
-source:iisApp="[Path to your website contents]" \
-declareParam:name="IIS Web Application Name",kind="ProviderPath",scope="IisApp",match="^C:\\path\\to\\your\\site\\folder",defaultValue="Default Web Site/SomeSite" \
-dest:package=[WebDeployPackageName].zip
I've managed to resolve a problem with manually defined connection strings while using the solution from @SkyFighter. Now one can use the auto-parameterization feature and have the connection string parameters with correct scopes.
Fortunately there is a place inside WPP to inject into. Unfortunately I had to use AfterTarget/BeforeTarget rather than SomeTargetDependsOn variables to narrow down the new target's placement.
And here is the target itself:
<Target Name="Replace_WebConfigsToAutoParmeterizeCS_TransformScope"
AfterTargets="PreAutoParameterizationWebConfigConnectionStrings"
BeforeTargets="AutoParameterizationWebConfigConnectionStringsCore"
Condition=" '$(EnableAddReplaceToUpdatePacakgePath)'=='true' ">
<ItemGroup>
<_WebConfigsToAutoParmeterizeCS>
<TransformScope>$([System.String]::Copy('%(TransformScope)').Replace('$([System.IO.Path]::GetFullPath($(WPPAllFilesInSingleFolder)))', '$(PackagePath)'))</TransformScope>
</_WebConfigsToAutoParmeterizeCS>
</ItemGroup>
</Target>
It is driven by the same variables as in the Sayed's sample for fixing long paths. So place this target anywhere those variables already available.
P.S. This trick/hack requires at least MSBuild v3.5 where metadata manipulation was first introduced.
精彩评论