Microsoft Web Deploy – Bad Application, or the Worst Application?

Background

I’m migrating a bunch of corporate websites hosted on Win2k8 and IIS7 to a new server running exactly the same. I’m sticking with the same environment because there are some things I really don’t want to risk breaking — we just needed faster hardware and more spindles.

I figured I’d use MS Web Deploy 3.5 to move all the IIS settings from one server to the other (a task that was gloriously simple in IIS6).

Web Deploy adds the following option to the IIS Manager context menus for the server and individual sites:

MicrosoftWebDeploy_IIS_manager_interface

Oh goodie, I can deploy!

It looks great! Simple and straightforward. Export or import.

Indeed it’s a simple interface. I wanted to export everything, so I chose to deploy from the server level.

But..

But it’s never really that simple, is it? (Well, except in IIS6.)

Here’s a redacted view of the package contents selector. It’s actually about 50 items at the top level, but annoyingly the box cannot be resized (though it does scroll horizontally by itself which is super annoying):

MicrosoftWebDeploy_export_server_package_contents_selector

OK, it looks like all the settings and crap that’s in IIS. What it doesn’t show, and hence what’s not selectable, is that the file system contents is included in the package. I had already robocopied the file system over, thinking that I’d just be exporting and importing the IIS settings. It generated a 25GB zip file.

It was a horrible waste of time, because it forces you to encrypt the contents of the package (even though I was exporting it to the local file system and would be copying it across a private LAN and there’s no sensitive customer data in there). I can copy the uncompressed files much faster than compressing and encrypting something like 40GB of content.

Whatever, I can deal with that. Except there’s no way to cancel the operation! The cancel button is greyed out. The “X” in the upper-right to close the dialog is clickable, and in fact does close it. Except that the operation keeps running in the background, which is not made clear anywhere in the UI. I didn’t realize that and went back to messing around with options, and eventually saw that a 25GB file had popped up in my temp dir!

Now for the import..

I temporarily moved some hardly-ever-accessed directories out of the web hierarchy so that I could run a faster export. A nice compact 2.7GB file was the result. I moved that over to the new server, and did the ol’ Deploy -> Import. It started, and then crashed right away.

The operation failed with the most user-unfriendly, borderline-useless, college-project error message possible: Object reference not set to an instance of an object.

MicrosoftWebDeploy_object_ref_not_set_instance_object_ss_20150224

Click for the full monty.

We’ve all seen that error message before, but on a mature product from a multi-billion dollar company? Phhht. What am I talking about, it’s Microsoft after all.

At least they included the stack trace! That at least gives a clue!

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.Web.Deployment.CertStoreSettingsProvider.Delete(Boolean whatIf)
   at Microsoft.Web.Deployment.DeploymentObject.DeleteOperation(DeploymentSyncContext syncContext)
   at Microsoft.Web.Deployment.DeploymentObject.Delete(DeploymentSyncContext syncContext)
   at Microsoft.Web.Deployment.DeploymentSyncContext.HandleDelete(DeploymentObject destObject, DeploymentObject sourceParentObject)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.ProcessSync(DeploymentObject destinationObject, DeploymentObject sourceObject)
   at Microsoft.Web.Deployment.DeploymentObject.SyncToInternal(DeploymentObject destObject, DeploymentSyncOptions syncOptions, PayloadTable payloadTable, ContentRootTable contentRootTable, Nullable`1 syncPassId, String syncSessionId)
   at Microsoft.Web.Deployment.DeploymentObject.SyncTo(DeploymentProviderOptions providerOptions, DeploymentBaseOptions baseOptions, DeploymentSyncOptions syncOptions)
   at Microsoft.Web.Deployment.DeploymentObject.SyncTo(DeploymentWellKnownProvider provider, String path, DeploymentBaseOptions baseOptions, DeploymentSyncOptions syncOptions)
   at Microsoft.Web.Deployment.UI.InstallProgressWizardPage.OnWorkerDoWork(Object sender, DoWorkEventArgs e)
   at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

As you can see from the second line, it’s trying to delete something from the Certificate Store (I suppose). A quick Googling found a German guy that was experiencing the same error. That lead to a red herring post about different .NET framework versions (I have the same versions of .NET installed on both machines, so that’s not my issue).

Unfortunately I had closed the error dialog, but I tried running the process again and I noticed this:

MicrosoftWebDeploy_HKLM_IIS_detail_ss_20150224

There’s the key that’s causing all the faff.

So it’s trying to delete HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/IIS/CentralCertProvider, which doesn’t exist on either the source or destination server. In fact, the entire IIS key doesn’t exist. The solution seemed obvious to me:

MicrosoftWebDeploy_HKLM_IIS_creation_ss_20150224

That’s right.

That’s right, I just created the key and added a nonsense string value for no reason. It then ran past the point of the NullReferenceException.

But wait, there’s more!

It crashed again, with an error message and stack trace even more useless than the first:

MicrosoftWebDeploy_count_non-neg_error_ss_20150224

Click for full size.

I can’t sigh enough at this one. I’m not asking for the world here, but at least say what file and line you were processing when the exception was thrown. This is Computer Programming 101 level stuff here. Not what you’d expect from version 3.5 of a utility meant for enterprise use. Even when I half-ass programs for my own personal use (which I do quite often), I have better error handling.

System.ArgumentOutOfRangeException: 'count' must be non-negative.
Parameter name: count
   at System.String.CtorCharCount(Char c, Int32 count)
   at Microsoft.Web.Deployment.XmlElementProvider.GetIndentWhitespace(XmlElement element, XmlWhitespace& before, XmlWhitespace& after)
   at Microsoft.Web.Deployment.XmlElementProvider.AddChild(DeploymentObject source, Int32 position, Boolean whatIf)
   at Microsoft.Web.Deployment.DeploymentObject.AddChild(DeploymentObject source, Int32 position, DeploymentSyncContext syncContext)
   at Microsoft.Web.Deployment.DeploymentSyncContext.HandleAddChild(DeploymentObject destParent, DeploymentObject sourceObject, Int32 position)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenNoOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenNoOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.HandleAddChild(DeploymentObject destParent, DeploymentObject sourceObject, Int32 position)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.ProcessSync(DeploymentObject destinationObject, DeploymentObject sourceObject)
   at Microsoft.Web.Deployment.DeploymentObject.SyncToInternal(DeploymentObject destObject, DeploymentSyncOptions syncOptions, PayloadTable payloadTable, ContentRootTable contentRootTable, Nullable`1 syncPassId, String syncSessionId)
   at Microsoft.Web.Deployment.DeploymentObject.SyncTo(DeploymentProviderOptions providerOptions, DeploymentBaseOptions baseOptions, DeploymentSyncOptions syncOptions)
   at Microsoft.Web.Deployment.DeploymentObject.SyncTo(DeploymentWellKnownProvider provider, String path, DeploymentBaseOptions baseOptions, DeploymentSyncOptions syncOptions)
   at Microsoft.Web.Deployment.UI.InstallProgressWizardPage.OnWorkerDoWork(Object sender, DoWorkEventArgs e)
   at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)

Moreover, is this even a recoverable error? It looks like it’s trying to count the characters using CtorCharCount, which is some kind of private or protected method (?) called in the String constructor (apparently):

CtorCharCount is one of those optimizing implementation details that can get kind of confusing if you try to expose it to users. It’s called as part of the string ctor in some cases. (Source: social.msdn.microsoft.com)

It’s constructing some string while adding a child to an XML node (?) during some kind of sync (?) process, or something. If this were based upon open source code I could figure it out pretty easily. But it’s most definitely not open source.

Work around?

Nope. I figured I’d start by trying to export and then import the sites one by one to see which one causes an error. Well, if you export a site you can’t import it unless that site exists on the target. It treats the site as a Web Application, even if you’re exporting the entire site.

I tried de-selecting all the locations except for one very simple website in the server export, but it still failed.

And now…

I don’t care anymore.

I’ve already wasted too much time on this. More time in fact than it will take me to manually recreate the handful of websites and web apps on the new server. So I’m done. I hope that if you were encountering the NullReferenceException this helped you.

Otherwise it was cathartic for me.

I wouldn’t be so hard on the developers of this tool if I were doing anything weird or unexpected. But I’m trying to migrate the settings from one instance of IIS to another, which is what the tool is alleged to do. The sites and web apps I have to migrate are few, and they’re not very complicated. This should have worked right off the bat.

If anyone has a solution, please let me know. Maybe I’m just being blinded by irritation and I’m missing something simple. But like I said, it’s time to go and do this the old-fashioned way.

Down load Microsoft Web Deploy to your toilet today!

About Scott

I'm a computer guy with a new house and a love of DIY projects. I like ranting, and long drives on your lawn. I don't post everything I do, but when I do, I post it here. Maybe.
Bookmark the permalink.

8 Comments

  1. I couldn’t agree more! Went through the same steps as you. How difficult can it be to port from once instance of IIS to another?! So frustrating!

  2. So frustrating!
    You never see these kind of problems in Linux.
    Bad Design , Bad Architect

  3. I had the same pathetic error message when attempting to do a full server export/import between two 2008 R2 machines, both with IIS 7.5. As it turned out, the servers had different versions of the Web Deploy tool (source=2.1,target=3.5); So, I had to downgrade the target server to version 2.1. Once I did that, the import ran successfully.

    • Glad you got it working, and good to know that’s a potential solution!

      I wish I could say that had been my problem, but IIRC I installed it on both servers from the same MSI.

  4. i’ve got this error too, i have created the key as you explain, was ok, and got wrong error with count… this come because the path is empty here :

    after that the importation was successfull but still not work in IIS, got error… so i have moved files from my other server :

    C:\Windows\System32\inetsrv\config\administration.config
    C:\Windows\System32\inetsrv\config\applicationHost.config

    and now everything works !

  5. i’ve got this error too, i have created the key as you explain, was ok, and got wrong error with count… this come because the path is empty in the pack – archive.xml :

    -rootWebConfig32 path=”/” MSDeploy.path=”2″ MSDeploy.MSDeployLinkName=”Child4″ …. … ..-
    -location path=”EMPTY !” MSDeploy.MSDeployKeyAttributeName=”path”-

    after that the importation was successfull but still not work in IIS, got error… so i have moved files from my other server :

    C:\Windows\System32\inetsrv\config\administration.config
    C:\Windows\System32\inetsrv\config\applicationHost.config

    and now everything works !

  6. to complete my message, for avoid error after migration, you must set before use ms deploy all your pool to CLASSIC and no managed code !

    if you still have issues, last way is to use appcmd for migration.

Leave a Reply