This blog is used as a memory dump of random thoughts and interesting facts about different things in the world of IT. If anyone finds it useful, the author will be just happy! :-)

Showing posts with label IIS extension. Show all posts
Showing posts with label IIS extension. Show all posts

Tuesday, August 18, 2009

WebDirProperties: AnonymousUser attribute is not obligatory

When you specify a WebDirProperties element to be used by the sites you install (configure) with WiX, you might also want to allow anonymous access to this site. Fortunately, there’s an attribute AnonymousAccess, which being set to ‘yes’ allows anonymous access to IIS web site.

NOTE: If you don’t address any property of “authorization” group (AnonymousAccess, BasicAuthentication, DigestAuthentication, PassportAuthentication or WindowsAuthentication) in your WebDirProperties, the site inherits those from w3svc root. If you set at least one explicitly, you need to set others the way you wish, because WiX defaults might not work for you.

The wix.chm states that “When setting this (AnonymousAccess) to 'yes' you should also provide the user account using the AnonymousUser attribute, and determine what setting to use for the IIsControlledPassword attribute.” But it turns out you are not forced to provide the AnonymousUser attribute and I suppose you never wanted to – you should provide a password as well, but who knows the password of IUSR on a target machine?

Instead, just omit the AnonymousUser attribute and this part of IIS metabase will stay untouched. The username/password will inherit from higher node (again, w3svc). And yes, don’t forget IIsControlledPassword=”yes”.

Hope this helps you tuning the website during the installation.

Tuesday, January 20, 2009

IIS extension: WebAppPool

Another challenge - another piece of fun with WiX. Imagine the following requirement: the installation program must install an application pool on IIS6+ environments; the multiple installed instances should use the same application pool. In other words, the application pool must be created with the first instance installation, and must be removed with the last instance uninstallation. 

A special element for maintaining IIS AppPools in IIS extension is called WebAppPool. As usual, we'll wrap it into a separate component, so that it is created on install. Later, we'll create a special custom action to deceive the standard removing mechanism on uninstall:

      <Component DiskId="1" Id="CreateIISAppPool" Guid="{YOURGUID-6C5B-4980-AD0B-E32FA2DBC1F4}" Directory="WebsiteFolder">
         <Condition>IISMAJORVERSION <> "#5"</Condition>
         <iis:WebAppPool Id="IISSiteAppPool6" Name="[IISAPPPOOL_NAME]" MaxWorkerProcesses="1" Identity="networkService" />
         <RegistryKey Root="HKLM" Key="$(var.ParentKey)">
            <RegistryValue Name="IISAppPoolName" Type="string" Value="[IISAPPPOOL_NAME]"/>
         </RegistryKey>
      </Component>

As you can see, the component is installed once the target system has IIS 6+. It creates a WebAppPool with the name provided in IISAPPPOOL_NAME public property. It also writes this name into a registry value, which resides under the instance-specific registry key. 
With this component included into the MSI package, the app pool is created when the first instance is installed, and nothing happens for second and subsequent instances. 

Let's examine the uninstall behavior. The MSI behaves natural - when it meets the component to uninstall, it removes the WebAppPool specified in it. But the IIS extension which performs the actual deletion of app pool, needs the name to be passed in it. So, the only thing we should do is to supply this action with a fake app pool name each time, except for the last instance uninstall.

Here is the algorithm:
  1. search the registry for the app pool name as usual
  2. schedule a special action on unistall after AppSearch, which detects if this is the last instance being uninstalled, and if not, "breaks" the app pool name into something non-existent
The first point is quite straight-forward:

      <Property Id="IISAPPPOOL_NAME">
         <RegistrySearch Id="IISAppPoolName" Root="HKLM" Key="$(var.ParentKey)" Name="IISAppPoolName" Type="raw" />
      </Property>

The second one is not natural, like any hack:

      [CustomAction]
      public static ActionResult ChangeWebAppPoolNameToDeceiveUninstall(Session session)
      {
         int numberOfInstalled = 1;
         foreach (ProductInstallation product in ProductInstallation.GetRelatedProducts(session["UpgradeCode"]))
         {
            if ((session["ProductCode"] != product.ProductCode) && product.IsInstalled)
            {
               numberOfInstalled++;
               break;
            }
         }

         if (numberOfInstalled > 1)
         {
            session["IISAPPPOOL_NAME"] += string.Format("|{0}", DateTime.Now.ToLongTimeString());
         }

         return ActionResult.Success;
      }

It iterates the related products (those sharing the UpgradeCode), and if it finds others installed, except for itself, it changes the app pool name we retrieved from registry into something unique, for instance, appends a unique string.

Thus, the IIS custom action which is going to delete the app pool fails to find the one with the provided name, and does nothing. When, otherwise, it is the last instance being uninstalled, the retrieved app pool name remains unchanged, and the app pool is successfully removed.

Note that the mentioned action should be immediate, should occur after AppSearch on uninstall.

That's it! I would appreciate any comments as usual.

Monday, January 19, 2009

IIS extension: WebSite

Ok, it's time for another portion of the installation fun, now about the IIS web sites.

The IIS extension in WiX is probably the most tricky and unobvious. That's my personal impression, of course. But, anyway, it gives you an option to tweak any property of a website, virtual directory or web directory. 

When installing a web application on Windows XP and thus IIS 5.1, it is natural to create an "ad hoc" virtual directory during install and remove it on uninstall. That's basically quite common case, but what if the application requires to reside under the site root directly, not virtual directory? 

In this case the root of the Default Web Site should just be switched to the installation directory - nothing is created on install and nothing is removed on uninstall. Let's see how this can be done with WiX IIS extension.

The iis:WebSite element has two "modes": if it resides under Component element, it is created during install, otherwise it is there just for reference from other elements. Fortunately, it has a special attribute ConfigureIfExists. Setting it to 'yes' avoids an attempt to create a new site, configuring the existent one instead:

      <Component DiskId="1" Id="ModifyIISSite5" Guid="{YOURGUID-2023-4D19-90D2-EE9101C71E44}" Directory="WebsiteFolder" Permanent="yes">
         <Condition>IISMAJORVERSION = "#5"</Condition>
         <iis:WebSite Id="IISSite5" Description="[IISSITE_NAME]" Directory="WebsiteFolder" ConfigureIfExists="yes">
            <iis:WebAddress Id="IISSiteAddress5" Port="[IISSITE_PORT]"/>
         </iis:WebSite>
      </Component>

Note, that in this case you should make sure you specified the existent website data. The website is uniquely identified by the description, port and header. The first is an attribute of a WebSite element itself, others belong to the child mandatory element WebAddress. 

The previous snippet highlights another attribute as bold - Permanent="yes". It makes the hosting component permanent, thus preventing it from being deleted on uninstall. Internally, the Windows Installer engine just keeps an extra reference to this component forever, thus it reference count is never equal to 0.

One last thing I'd like to point your attention to is a component condition. It uses the property called IISMAJORVERSION. This property, as well as another one called IISMINORVERSION, is brought by the IIS extension. They are populated from the target system registry during the AppSearch action. Before using them in your authoring make sure you add a couple of references:

    <PropertyRef Id="IISMAJORVERSION"/>
    <PropertyRef Id="IISMINORVERSION"/>

That's it! As usual, any comments are highly appreciated.