Web Application Installer in WiX

If all you need to do is install your web application into Default Web Site, life is easy. Especially since Windows Installer XML (WiX) has all that support right in the box. Where things get nasty is if you need an installer that lets the user choose the web site, set the web application name, and the application pool. If you’re newish to WiX, that can be a daunting task. You might be tempted to skip WiX and use a Web Setup Project built into Visual Studio because those do offer a solution to the requirements. Sadly, Web Setup Projects are a giant world of hurt because of their other limitations. Add in the fun fact that Microsoft is dropping support for them in future releases of Visual Studio and it’s pretty obvious you need to avoid them.

Since this seems like a very common request, I thought I’d create an example WiX project that does what the now dying Web Setup Projects so there’s a path to a supported installation technology. While pieces of this type of installer have been covered before, especially the excellent article by Jon Torresdal, but there wasn’t a complete example. As always, if you’ve got any questions, feel free to email me or ask them in the comments. Grab the code for this installer here.

To mimic the Web Setup Project, I needed to do the following tasks:

  • Enumerate the web sites on the server and put them into a combo box.
  • Have an edit control that lets the user set the name of the application and does not allow a blank entry. This name is also used for the virtual directory name as well.
  • Installs the web application under the default directory for the web site.
  • Optionally let the user decide if they want to choose a different application pool for the web application. If they chose the default, the application pool for the web application will be set to the one in use by the web site.
  • Enumerate the application pools on the server and put them into a combo box.
  • Full support for IIS7 and higher. Note that the example does not work with IIS6, but it wouldn’t be hard to add that support.
  • Properly uninstalls the web application, virtual directory, and all files. As anyone who’s used the Web Setup Project knows, it does not correctly uninstall the web application from IIS (at least on IIS7).

As they say, a picture is worth a thousand words, so here’s a dialog that meets the requirements.

image

As the user interface in any MSI-based installer is the hard part, I wanted to get the UI out of the way first. Since my installer is very much like a standard WiXUI_InstallDir, which allows the user to choose the installation path, I downloaded the WiX source code and pulled that file as the basis of my UI. As the file for my UI is a decent size XML file, I didn’t want to just drop it into this article, so you should download the sample project and open.InstallerHelloWorldInstaller.SLN and look at WixUI_SimpleWebAppInstall.wxs. I’ll discus the highlights here.

If you’ve never done a custom WiX UI project before it can appear pretty daunting. The basic idea is that you reference existing dialogs, DialogRef elements, and add your own custom ones with the Dialog elements. The key to creating dialogs is to use the dialog editor in the great WiXEdit project. Being able to add controls and see their layouts visually drastically speeds up your development. Towards the bottom of WixUI_SimpleWebAppInstall.wxs, you’ll see the XML for two dialogs, the WebAppInstallDlg, which is the dialog shown above, and an error dialog, InvalidWebAliasAliasDlg, that is shown at the bottom of the file.

Defining dialogs isn’t too hard, but the fun work is getting them properly hooked into your installers UI flow. That’s done with the Publish elements in the middle of my file. Once I realized that Publish elements are essentially like your event handlers in normal .NET development, the Windows Installer approach started making a lot more sense to me. Let’s take a look at the button processing for the WebAppInstallDlg.

<!– Handle the back button–>
<
Publish Dialog=”WebAppInstallDlg”

         Control=”Back”

         Event=”NewDialog”

         Value=”LicenseAgreementDlg”>1</Publish>

<!– Check to see the web app name has something in it. –>
<
Publish Dialog=”WebAppInstallDlg”

         Control=”Next”

         Event=”SpawnDialog”

         Value=”InvalidWebAliasAliasDlg”

         Order=”1″>WEB_APP_NAME=””</Publish>
<!–
Set the INSTALLDIR property based on the selected web site’s physical path using

     my custom action. –>
<
Publish Dialog=”WebAppInstallDlg”

         Control=”Next”

         Event=”DoAction”

         Value=”SetInstallDirBasedOnSelectedWebSite”

         Order=”2″>1</Publish>
<!–
Set the APP_POOL_NAME to the web site’s default if that’s what the

     user wants.–>
<
Publish Dialog=”WebAppInstallDlg”

         Control=”Next”

         Event=”DoAction”

         Value=”SetAppPoolNameToWebSiteDefault”

         Order=”3″><![CDATA[USE_CUSTOM_APP_POOL <> 1]]></Publish>
<!–
Finally move to the VerifyReadyDlg if all values are looking good.
–>
<
Publish Dialog=”WebAppInstallDlg”

         Control=”Next”

         Event=”NewDialog”

         Value=”VerifyReadyDlg”

         Order=”4″><![CDATA[(WEB_APP_NAME<>””)]]></Publish>

At a glance you figured out the Back button processing but it’s the Next button that’s a little more interesting. The first act is to check that the WEB_APP_NAME property is empty. If it’s a null value, I want the InvalidWebAliasDlg to be shown. The second action is to call a custom action I wrote to set the INSTALLDIR property based on the chosen web site. As that element value is 1, that action is always executed. The third action is if the user did not check the use custom app pool checkbox, I want to run another custom action to set the app pool name for the web application to the same that the web site is using. Lastly, if we get to the last Publish element, I’ll move to the VerifyReadyDlg if the WEB_APP_NAME is not null.

One trick I found helpful when developing the UI was to initially insert my dialog and have the next button just go to the VerifyReadDlg. That way I could ensure the dialog was properly being shown and could build up the custom actions and get them hooked up to the real UI.

The final part of WixUI_SimpleWebAppInstall.wxs I want to mention is near the top of the XML and shown below.

<!– This is very important. As I am filling in the web site and app

     pool combo boxes dynamically, I need to force create the ComboBox

     table.

     This nice little fellow gets it into the output .MSI. However,

     as there’s nothing in the table, you’re going to get an ICE17

     warning that the combo box associated with WEBSITE_NAME does not

     exist. It’s safe to turn off ICE17 as the enumerate custom action

     will take care of doing the filling so it exists before being

     needed. –>

<EnsureTable Id=ComboBox/>

 

<!– The custom action DLL itself.–>

<Binary Id=WebAppCA

    SourceFile=$(var.CAFileLocation) />

 

<!– The custom action to enumerate the web sites and app pools into

     the appropriate combo boxes.–>

<CustomAction Id=EnumerateIISWebSitesAndAppPools

              BinaryKey=WebAppCA

              DllEntry=EnumerateIISWebSitesAndAppPools

              Execute=immediate

              Return=check />

 

<!– Make sure the enumerate web sites and app pools custom action

     gets called, but only called if we are doing and install. –>

<InstallUISequence>

  <Custom Action=EnumerateIISWebSitesAndAppPools

          After=CostFinalize

          Overridable=yes>NOT Installed</Custom>

</InstallUISequence>

As I have the requirement to let the user chose the web site and optionally the application pool, I need to get those items filled in. As there’s no built in support for enumerating those items in WiX or Windows Installer, I needed to write a custom action, EnumerateIISWebSitesAndAppPools, to do the work for me. The above section of code shows incorporating my custom action and getting it scheduled. What EnumerateIISWebSitesAndAppPools does is add the items to the ComboBox table in the in-memory MSI file that is where all combo boxes go for their data. Once gotcha I ran into is that just declaring a ComboBox in your UI does not create the table so your custom action will fail. The WiX trick to work around that is to use the EnsureTable element to have WiX put the table in the output MSI.

While I had bumped into discussions of custom actions through my reading on WiX and Windows Installer, I’d never had to write one before. Conventional wisdom dictates that you should use native C++ to write your custom actions. As I’m not afraid of C++, I took a look at the IIS 7 documentation for their C++ interfaces for administration. Just as I was afraid of, it’s all PCOM. You know, Painful COM, whose motto is “all the pain of COM and none of the benefits.” Because I didn’t want to spend two weeks in COM hell, I thought it time to take a good look at the Deployment Tools Framework (DTF) which we can use to write out custom actions in any managed language you so desire.

In all, using DTF made writing my custom actions nearly painless especially since the IIS7 managed administration API is very clean. As an example, here’s the code to my custom action that sets the installation directory based on the selected web site.

[CustomAction]
public static ActionResult SetInstallDirBasedOnSelectedWebSite(

                                                     Session session)

{

    if (null == session)

    {

        throw new ArgumentNullException(“session”);

    }

    try

    {

        // Debugger.Break();

        session.Log(“SetInstallDir: Begin”);

 

        // Let’s get the selected website.

        String webSite = session[“WEBSITE_NAME”];

        session.Log(“SetInstallDir: Working with the web site: {0}”,

                    webSite);

        // Grab that web sites based physical directory and get it’s

        // “/”

        // (base path physical directory).

        String basePath;

        using (ServerManager iisManager = new ServerManager())

        {

            Site site = iisManager.Sites[webSite];

            basePath = site.Applications[“/”].

                                VirtualDirectories[“/”].PhysicalPath;

        }

 

        session.Log(“SetInstallDir: Physical path : {0}”, basePath);

 

         // Environment variables are used in IIS7 so expand them.

        basePath = Environment.ExpandEnvironmentVariables(basePath);

 

        // Get the web application name and poke that onto the end.

        String webAppName = session[“WEB_APP_NAME”];

        String finalPath = Path.Combine(basePath, webAppName);

 

        // Set INSTALLDIR to the calculate path.

        session.Log(“SetInstallDir: Setting INSTALLDIR to {0}”,

                    finalPath);

        session[“INSTALLDIR”] = finalPath;

    }

    catch (Exception ex)

    {

        session.Log(“SetInstallDir: exception: {0}”, ex.Message);

        throw;

    }

    return ActionResult.Success;
}

Over half of the code there is logging but you can get the idea pretty quickly. There were a few things that I stumbled with developing my custom actions. The first was since I was installing on Server 2008 R2, which is a 64-bit operating system, I was concerned that my custom actions had to be 64-bit as well. It made sense to me, but after experimenting, that’s not the case at all. When the 64-bit MSIEXEC.EXE is executing your custom actions, it’s doing so out of process and takes into account the “bitness” of the DLL you’re executing. That was a pleasant surprise so if you are looking to use custom actions written by others, you don’t have to worry if they are 32-bit only. Obviously, the Windows Installer team hasn’t figured out how to run 64-bit custom actions on 32-bit windows.

Debugging managed custom actions is a bit interesting. There’s the MMSIBREAK environment variable, which breaks into the managed debugger when your custom action is called. What I found even easier was to just put in a call to Debugger.Break at the top of my method. Since your custom actions can be run in various processes, getting the environment variable set system wide can be a pain. You can find a nice overview of debugging managed custom actions at Jon Torresdal’s web site.

The last thing that threw me for a loop debugging custom actions was that I couldn’t seem to get the diagnostic logging for those custom actions I invoked as part of a button click DoAction control event. It was so weird that the custom action worked, but I never saw the logging. After much hair pulling, I realized that it’s a limitation of Windows Installer. It’s sad that Windows Installer is nasty enough without surprises like this floating around. What I ended up doing was adding a bunch of property sets, which do get logged instead of calling Session.Log.

As prompting the user for web sites, app pools, and virtual directories seems to be a very common question on the WiX-Users mailing list, I hope others can use my code to get past that hurdle. Once last thing I need to mention is that accessing the IIS 7 APIs requires full administrator rights. As my code needs to enumerate the web sites and web pool as soon as the MSI loads, if you double click on it from Explorer and are not running elevated, the install will immediately fail. To be a full installer you need to wrap the MSI in a boot strapper EXE that requests elevation before executing the MSI. As WiX 3.6 is all about the Burn, you may want to start there, but it’s still early in the development cycle.

Atmosera is an Oregon-based company who architects, deploys, and operates public, private, and hybrid Microsoft Cloud Platform and Azure environments for customers across the globe and diverse industries. Since 2011, Atmosera has been a trusted cloud partner to the State of Oregon. State agencies including the Oregon State Hospital (OSH), Oregon Health Authority (OHA), the Department of Justice, the Department of Human Services, and the Department of Treasury host their applications with Atmosera. This includes a partnership with Enterprise Technology Services (ETS) and the State of Oregon Data Center.

The Right Solution for Your Needs.

We deliver a hybrid spectrum of Microsoft Cloud Platform and Azure solutions to government agencies and application developers who demand a modern, open and flexible cloud service platform. We offer trusted, transparent, and secure Infrastructure as a Service (IaaS) and Platform as a Service (PaaS) solutions for production business applications, Business Intelligence (BI), continuous data protection, application availability, test/development, and Software as a Service (SaaS)

Architected to meet your needs.

Deployed flawlessly.

Operated reliably 24x7x365.

We build solutions to address your individual business objectives with an eye to sustained technology innovation.

We manage the entire lifecycle from 
start to finish to eliminate surprises 
and allow you to focus on your services.

We deploy environments which 
can be managed and maintained for you by our team of experts 24x7x365.

20+ Years of Experience Makes Us a Trusted Partner You Can Count On.

Atmosera backs technology with real humans who deliver experience and unparalleled dedication, resulting in smarter cloud computing investments. We developed a core methodology to ensure we accurately capture your needs and translate them into the best solution possible. This process gives you the peace of mind that your cloud investment will be aligned with the return you seek. We can be counted on to bring our industry experience and real-world best practices to operate Microsoft Cloud Platform and Azure environments.

Assess:

Migrate:

Re-platform:

Operate:

Rely on our team to map your existing environment to a corresponding 
Azure cloud.

Easily move from your existing environment to a public or private Microsoft cloud.

Understand how to transform your applications to better take advantage 
of cloud capabilities.

Our team actively manages all maintenance & optimization to keep your environment running at its best.

Information Security: Protect Your IT with Industry Best Practices.

We provide managed Information Security (InfoSec) and compliance options across the entire computing stack, from connectivity to applications, with stringent physical and logical security controls.

We take on the security and infrastructure concerns by becoming an extension to our customer’s team or their application development vendor. In that partnership, Atmosera shares the responsibility and liability associated with maintaining a secure environment, and stands by that commitment with a guarantee based on the defined lines of responsibility. All Atmosera customers benefit from more than technology by also getting processes, policies, training, and 24x7x365 technical resources. Customers have the peace of mind of knowing an industry expert has performed a thorough risk assessment, identified a remediation plan and provided ongoing audit support to ensure customers stay secure. Best of all, Atmosera understands this level of service is, and will continue to be, required year after year.

Managed Compliance
Stay compliant with IRS-1075, PCI-DSS, HIPAA/HITECH, and HITRUST.
Disaster Recovery (DR) 
& Data Protection
Safeguard your applications and data and stay operational event when disasters strike.
Secure Networks including Firewalls & DDOS Mitigation
Implement networks which deliver better security and usability.

Why Microsoft Cloud Platform and Azure?

Microsoft has made major strides in the public cloud space over the past few years. They are gaining market momentum with companies and also the analysts community who recognize the more than USD15B investment made. At this point Azure has more data centers than AWS and Google Cloud combined. It spans an unparalleled global reach with 36 regions available and more being added. Azure offers companies wanting to put workload in the a public cloud with a secure, scalable and competitive option.

The Power of Microsoft’s Hybrid Cloud Platform.

Microsoft is the only hyperscaler to offer a true hybrid cloud solution with the flexibility to run applications using private or public clouds — and the ability to move between them. Atmosera has the skills and expertise necessary to help architect, deploy, and operate integrated hybrid cloud solutions based on the Microsoft Cloud Platform and microservices. This offers our customers a unique set of capabilities and flexibility when planning out their cloud journey.

Azure Certified for Hybrid Cloud.

Azure Certified for Hybrid Cloud.

Atmosera has developed core competencies and intellectual property to take full advantage of all Azure has to offer. All Atmosera Azure services receive the “Azure Certified for Hybrid Cloud” designation which confirms it passed Microsoft’s rigorous validation process under the Cloud OS Network (COSN) program. Customers benefit from knowing that their solution was carefully planned and executed using best practices and proven methods.

Microsoft Azure Government.

Starting in June of 2017, Microsoft opened up their Microsoft Azure government regions to partners. Atmosera was one of the first Microsoft Cloud Solution Provider (CSP) to be approved to deploy qualified government customers in these dedicated regions.
Azure Government offers world-class security and compliance for US federal, state, local, and tribal government agencies and their partners. It provides an environment for government-only workloads which is operated by screened US citizens. Customers can choose from six data center regions with DoD Impact Level 5 Provisional Authority (PA) including two dedicated regions for US Department of Defense workloads. Environments can leverage hybrid flexibility by maintaining some data and functionality on-premises. Azure Government also offers the most certifications of any cloud provider to simplify critical government compliance requirements.

Azure Government regions where Atmosera can deploy customers include the following six locations:

  • US Gov Virginia
  • US Gov Indiana
  • US Gov Arizona
  • US Gov Texas
  • US DOD East
  • US DOD Central
Atmosera has seen a growing desire to use Azure by government agencies and the availability of Azure Government provides a highly secure and trustworthy cloud where workloads can be deployed and managed. The flexibility of hybrid environments makes it possible to transition workloads to the cloud as slowly or fast as desired by customers. Every deployment includes financially-backed Service Level Agreements or SLAs.

We deliver solutions that accelerate the value of Azure.

Ready to experience the full power of Microsoft Azure?

Start Today

Blog Home

Stay Connected

Upcoming Events

All Events