I've got a TFS build set up to build and deploy a web application. I'm passing in the MSDeploy parameters via the TFS build definition's MSBuild arguments. First time round this is working fine. When someone accesses the web app, one of the controls (Microsoft charting control) generates a couple of files in an empty directory I've added to the solution.
When I go to rebuild (or continuous integration is triggered) the next build will usually fail because it can't delete one of the generated files. When I try and manually delete the file it tells me that IIS worker process is using it and it can't be deleted.
Now to get the build building I'd have to manually restart IIS every time, which is not desirable with CI in mind. I've taken a look through Microsoft.Web.Publishing.Tasks.dll and there's nothing there to restart IIS using MSDeploy.
At the moment I'm thinking that adding stubs of the temporary files in the solution might be a resolution (maybe MSDeploy will be able to close the process if the file is a permanent part of the deployment) or I could do some unpleasantness with Exec in the solution file to get an IIS reset.
It's probably a long shot but has anyone come up against this and found a nice solution?
You could use MSBuild Extension Pack to stop the application pool automatically before deployment. There are several tasks in the MSBuild.ExtensionPack.Web namespace to manage IIS, such as stopping and starting an application pool, deleting an application, etc.
Related
ASP.NET Core 2.1
We have a shared dll, that is used in a few projects (via project reference)
We have class in that was named Proofer, and is now named ProofReader.
This was done through mass find and replace.
Nowhere in our source files locally exists any files containing the text Proofer.
VSTS builds the application without issue, then we send it via a release pipeline to Azure. All succeed.
Despite this we attempt to run the web app and get the following:
An unhandled exception occurred while processing the request.
TypeLoadException: Could not load type
'MainShared.Models.Proofer' from assembly 'MainShared,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule
pModule, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob,
Int32* pcNamedArgs)
We are having trouble determining if this is in the build, maybe something not being rebuilt when it should be, or something still cached on Azure that isn't being overwritten.
A few places I can suggest you look into -
If you are using app service web apps in azure , we can use the kudu (web app extension to remove/clean the files) to make sure you have a clean destination.
Check the VSTS Release logs if you have access to , it shows the files which are overwritten and the ones which could not be. Once i had a similar issue and found that the logs were already warning me that a few could not be overwritten.
If you are using VM's for your app deployment if has access to it , remove/backup the files under your site folder.
I had a similar problem after some redesign for a Web App using ASP.NET Core 2.2 (hosted on Azure), that some old not even anymore existing references causing the Load type exceptions to happen and the Web App simply refused to come up. I tried to clean everything up in Visual Studio, even deleted the bin and object directories, but the old reference was still looked for. In Kudu on the Azure portal I saw the *.PrecompiledViews.dll and *.PrecompiledViews.pdb files, which were very old. I simply removed them directly on the Azure server and could then simply start up my app and everything came up again. This fault should be fixed from the source. If I do a complete rebuild, I expect this old garbage to be taken care of during the publishing to the website. This is something that Microsoft should fix and as I can see this happened to others over half a year ago. Thanks Mark Johnson for your very useful hint about those files!
When we configure a web application to run in IIS Express there are certain things VS does, like:
Creating the application host configuration file in the IISExpress subfolder of the user documents folder.
Creating a dedicated site section for each web application in the solution, including ours.
Maybe more things are done, which I am unaware of.
I would like to replicate the same process from a script, so that running the web application from the script would be equivalent to running it from VS. Including for the very first time.
Right now I start IISExpress with the /port and /path flags, because this is how I used to run Cassini. However, Cassini supported an additional flag - /vpath. They removed it from IISExpress, meaning I have to use another set of flags - /config, /site, /siteid. But I suspect it must be done in conjunction with the Appcmd.exe utility.
This second approach is still something I haven't managed to master. So, my question is this - suppose I am given the port, path and vpath of a web application (i.e. no need to read them from the web application's csproj file, like VS does). What command sets up the right application host configuration file and how do I run IISExpress to take advantage of it?
I have the following build target set to do my deploys, as mentioned by Hanselman in his Tiny Happy Features #3 and as also noted in many other places as what I consider to be the recommended approach:
msbuild my_web_application.csproj /p:Configuration=Production;
DeployOnBuild=true;
PublishProfile=Production;
VisualStudioVersion=11.0;
AllowUntrustedCertificate=true;
AuthType=NTLM
This does the job, and replaces the deployment step I previously had via invoking ms deploy on the command line:
msdeploy.exe" -source:package="c:\source_to_my_web_application.zip" -dest:auto,my_server_name,includeAcls="False" -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParamFile:"c:\source_to_my_web_application\Package.SetParameters.xml"
The biggest difference I can see in the two methods is that the command line call will only push over the files that have modifications, whereas the msbuild call sends over the entire web application every time.
Is there a way to make the msbuild version do the "sync" behavior, like the direct call to msdeploy did for me?
From what I've been able to establish, syncing from a package (like the one created for this particular MSBuild command) will find differences related to the local working copy it's trying to deploy from.
If I do a fresh checkout, then build and deploy, it'll publish the entire web application.
If I do a clean and rebuild on that working copy, it'll only publish the dlls that got rebuilt.
If I do a build on that, it'll only publish the web.config files that got transformed and some other random dlls that I can't make rhyme or reason out of.
The bottom line, I guess, is that with our CI server setup, it should be assumed that all files will be published to the server, and if it happens to publish fewer than that, it's a bandwidth bonus.
As a side note, I've also encountered random, unreliable 404 errors while doing otherwise normal ms deploy commands. They seem to be intermittent, and can vary between my own workstation, my colleagues, and the CI server so that the MS Deploy service will return a 404 or execute just fine within seconds of each successive call. Solutions I've found for this type of behavior range from restarting Visual Studio to uninstalling and reinstalling various components, and making sure you do it in the right order and only the second Tuesday of the third week of any month ending in "ary"...
The takeaway for me is that this tool is sketchy, and that if you can get a setup to work with any amount of reliability it's a miracle and you should not touch it ever again and pray to the silicon gods that it stays that way.
Before we used TFS, we were building our packages locally using visual studio. There was a lot of projects organized into solutions. When we wanted to build the package, we simply located the ccproj project, right click on it and hit “package”.
There is couple of specific in our solution:
We use web roles with multiple web sites and virtual applications and we have them as project dependencies in VS2012 solution
We use config transformations for both web and worker roles. Worker roles transforms was achieved by adding transform target manually to the project file.
We have some additional class library projects – their outputs needs to go in a subfolder of the worker role along with correct configuration files, it is kind of plug-in architecture. We used some xcopy commands to include these non-referenced libraries in our worker roles.
Everything worked smoothly when building in VS 2012 locally.
When migrated to TFS we quickly learned that we won’t be able to replicate the same build process on build server
It turned out that TFS is not preserving solutions structure, more
details here:
http://social.msdn.microsoft.com/Forums/en-US/tfsbuild/thread/9ac815c8-5961-4670-a6d0-660a9b66da9c
The project dependency that was solving multiple web sites and
virtual applications in a single role did not work on build server,
probably because of different output directory. We had to add some
hacks in our ccproj and csproj files to get these published and
correctly included in the resulting package.
xcopy commands failed because of different directory structures on
TFS build sever.
We had to force run cspack on TFS build server by explicitly adding
/t:Publish parameter to msbuild command line.
Config transforms for worker roles did not worked, we had to force to
occur using another hacks in the ccproj and csporj files.
There was more issues but those are too detailed. I would keep it on
high level just to illustrate the whole issue. The build somehow
works now, but we have now a lot of hacks in place now.
I have two questions:
Is it possible to configure TFS build server to have exact same
behavior as the local build in VS2012?
Is there any official solution for building azure packages with multiple web sites and virtual applications in a single web role?
I haven't yet tried this on a TFS build server, but the approach outlined in my blog at http://michaelcollier.wordpress.com/2013/01/14/multiple-sites-in-a-web-role/ has been working well. The "trick" is basically to modify the .ccproj file to tap into the CoreBuildDependsOn target, adding logic that will execute MSBuild against the secondary sites. This should also allow config transforms to work.
I have a web application project in VS2012 which I'm publishing using a "Web Deploy Package". I want this package to include app-pool settings, specifically creating an IIS app-pool and assigning the newly created application to it.
I'm familiar with the option "Include application pool settings used by this Web project" available when the project is configured to use an IIS instance (not IIS Express), but IIS configuration is not part of the project file, and thus not source controlled. What happens when somebody builds a deployment package on a machine that hasn't had IIS meticulously configured? Not ideal.
How else then, can I go about getting AppPool settings into my web deploy package? I understand that the appPoolConfig provider is IIS7+ only, I'm fine with that limitation. I've banged my head against this issue in the past and never found a solution. 18 months later, we've got a new VisualStudio version, and a new web-publishing-pipeline, are there new options to address this? Or maybe something I missed when I first tackled this problem?
Edit
OK, I'm seeing the following as options:
Configure my project to sync settings from an IIS instance. As mentioned, I'm not a fan of this given that it puts settings outside of the project, meaning the environment has to be meticulously configured to build + publish. Plus it drags along other IIS settings I don't want included.
Inject something into the web-publishing-pipeline (WPP) to modify the archive.xml. I've toyed with this in the past and had limited success. One problem is the pipeline isn't exactly co-operative with working directly on the archive.xml file, another problem is some of the more cryptic attributes involved, like MSDeploy.MSDeployProviderOptions which appears to have some Base64 encoded binary? No idea what to put in there.
Find an existing "provider" that can do what I want. I might be out of luck here, the appPoolConfig provider only seems to want to read / write IIS, not, say, an XML file of settings. Does anybody know otherwise?
Write my own "provider" to produce manifest output entries. I'm not sure, is it possible to write a custom provider that writes to a manifest using the name of an existing provider? As in, MyCustomPoolProvider writes appPoolConfig sections into a manifest? This sounds like a potentially painful exercise that may or may not work. Would I still need to figure out the encoding of whatever is going into MSDeploy.MSDeployProviderOptions?
I get the feeling that the fundamental obstacle with Web Deploy for what I'm trying to accomplish, is how strictly it leans on "providers". The pre-existing providers are largely designed for IIS synchronisation, not primary development and publication. It so happens that some of these providers can be relatively easily hooked into via MSBuild, but the majority insist on pulling data from IIS, and that's that.
You are correct in your understanding of the appPoolConfig provider, in that it can only sync between App Pools and can't be provided with the configuration directly. What you could potentially do is keep a copy of the appPool in question in package form (ie. msdeploy -verb:sync -source:appPoolConfig=PoolName -dest:package=apppool.zip) and attempt to hijack the pipeline so that the MSDeploy call adds the application content into the package, leaving the existing content there.
Alternatively, you could always keep the packages separate and deploy them with different calls to MSDeploy.
FYI, MSDeploy.MSDeployProviderOptions is simply an encoded version of the parameters supplied to the provider when it was packaged. For example, -source:dirPath=c:\,ignoreErrors=0x10293847 -dest:package=package.zip would package the ignoreErrors value.