Microsoft Web Administration DLL hell
I faced an extremely annoying issue recently when trying to manage IIS from a locally running website using functionality in the Microsoft.Web.Administration dll.
My setup was seemingly simple. I had an ASP.NET MVC website running locally on my dev computer under IIS Express, which is the default runtime environment when running and debugging web projects locally using VS2013. A class library in my solution was referencing Microsoft.Web.Administration.dll v126.96.36.199 via a nuget package include. Code inside the class library was responsible for creating IIS Application Pools and Websites. Essentially, the website is an admin portal for managing deployments of another web application, whereby an admin user can log in and provision a new instance of a web application.
To test IIS management I also had LINQPad open, with a reference to the same Microsoft.Web.Administration.dll nuget package. My LINQPad script worked flawlessly and was creating App Pools and Websites with no issue. Additionally, I could then navigate to the newly created local websites and they would load up fine in the browser. Note: I’m using subdomains against localhost via a modification to my hosts file. The sites were also using SSL via the IIS Express Development Certificate, allowing me to create a new website, for example, at https://test.localhost and access it locally.
I was also configuring my app pools to run under a specific user account, not one of the local IIS built-in accounts, so my script was assigning an identity to the app pool during creation.
I first noticed an issue after my admin website had successfully created a new app pool and website. Everything was created correctly and when I opened up IIS Administration on my local machine I could see the website and app pool running. But as soon as I tried to access the website the app pool would stop. The Windows event logs showed that the Windows Activation Service was unable to start the website due to invalid login credentials for the app pool. How could this be? I was using the exact identity credentials that my LINQPad script was using, and I knew those credentials worked.
Since I knew the credentials were sound, the problem had to be something else. Out of pure desperation I eventually changed the app pool identity to run under the built-in account ApplicationPoolIdentity. I tried to access the website and it worked! So then I thought, I’ll just set the identity back to the account I want to run under, and lo and behold that worked too!! In case you haven’t quite grasped what I’m saying, simply switching the identity to something else and then back to the account I wanted to run under completely fixed the issue.
Additionally, I knew that using LINQPad to script the creation worked fine. So there had to be something different about using a local website rather than LINQPad to script the creation. Aha I thought, it’s go to be using the wrong version of the Microsoft.Web.Administration DLL. But how could that be? I found an SO question talking about issues getting a website to work when moving from a local website to Azure. The solution for them was to add a binding redirect to the web.config file to force the application to load the specific version of the DLL. IIS Express has its own version of the DLL v188.8.131.52, whereas full IIS has v184.108.40.206. I added the binding redirect to v220.127.116.11 thinking “yeah, this will fix it”, but still no luck
Then I started looking at fusion logs and seeing exactly what DLLs were loaded by my admin website during start-up. It was clear that v18.104.22.168 was being loaded, and not v22.214.171.124, despite the binding redirect. How could this be? Nowhere in my solution was I referencing v126.96.36.199 of the DLL. And then I found this SO question, specifically the answer provided by Lars Udengaard. It was now clear where the binding to v188.8.131.52 was coming from. Because my locally running admin website was running against IIS Express, the binding redirect in the IIS Express asp.net config file was taking precedence and always loading v184.108.40.206 despite my protestations otherwise. Man, that is really annoying!
So the solution is actually very simple. If your website needs to perform admin tasks on IIS, don’t run this website inside IIS Express. Instead, run and debug on a full local install of IIS. Otherwise you’ll always load v220.127.116.11 of the management DLL and management tasks will appear to have succeed when in fact they have not been performed correctly.
I can only guess that v18.104.22.168 implements functionality around creating app pools in a slightly different way than the v22.214.171.124 DLL, which is incompatible with full IIS. This was frustrating beyond belief. Clearly v126.96.36.199 of the DLL was not designed to be used against full IIS, so you’d think they could throw an exception when you try to create an instance of ServerManager against full IIS using v188.8.131.52 of the DLL. That would have saved me a LOT of time.