20 Mar 2018
Reading time ~6 minutes
Configure HTTPS for IIS website by WiX setup
- Introduction
- Detailed requirements
- The solution
3.1. Introspection
3.2. Configuration of the “Default Web Site” of IIS
3.3. Install an external certificate
3.4. Optional component - Summary
Introduction
Due to cybersecurity considerations, I recently had to implement a user story, using an existing MSI installer for an intranet web application at the customer’s site, to enable SSL encryption for the Internet Information Server’s Default Web Site
(hereinafter: IIS) when the corresponding PFX file for the server certificate is found at a predefined location. However, the configuration should remain after uninstalling the MSI package.
Detailed requirements
- The MSI installer (WiX) installs the web application in IIS under the existing
Default Web Site
(port 80). - If a valid certificate is found during the installation in a predefined directory (PFX file with defined password), the HTTPS binding (port 443) - if not already existing - is added to the
Default Web Site
. - The previously provided certificate is used for the added HTTPS binding.
- If the web application is uninstalled via MSI, the web application should be removed from the
Default Web Site
of IIS, but not theDefault Web Site
itself. Their server certificate and the HTTPS binding should also be preserved. - Reinstallation should not overwrite an existing HTTPS binding.
The solution
Introspection
If you are building an MSI installer for a web application using the Windows Installer Toolkit (WiX) that you want to install on an existing IIS Web site, then you should not create it as a component in the WiX code, but define the existing website (hereinafter referred to as Default Web Site
) outside of components so that you can reference it in other components of the installer. This prevents the IIS website from being removed during uninstallation:
So you write something like this:
<?xml version="1.0" encoding="Windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product ...>
...
<iis:WebSite Id="DefaultWebSite"
Description="Default Web Site"
Directory="INSTALLDIR">
<!--Following line just needed for compilation-->
<iis:WebAddress Id="AllUnassigned"
Port="80"
IP="*" />
</iis:WebSite>
...
<ComponentGroup Id="IIS">
<Component Directory="MyWebDir"
Id="MyWebComponent"
Guid="..."
KeyPath="yes">
<!--"DefaultWebSite" referenced in next line-->
<iis:WebVirtualDir WebSite="DefaultWebSite"
Id="MyWebApp"
Alias="MyWebApp"
Directory="MyWebDir" >
<iis:WebApplication Id="MyWebApplication"
Name="MyWeb"
WebAppPool="MyAppPool" />
</iis:WebVirtualDir>
<iis:WebAppPool Id="MyAppPool"
Name="MyAppPool"
MaxCpuUsage="75"
ManagedRuntimeVersion="v4.0"
ManagedPipelineMode="Integrated">
</iis:WebAppPool>
</Component>
</ComponentGroup>
...
</Product>
</Wix>
However, if you do not just want to reference the Default Web Site
but also configure it, you have to do it another way!
Configuration of the “Default Web Site” of IIS
To configure them, you now have to pack them into a component. You also need to know where the IIS root folder is located (usually c:\inetpub\wwwwroot
):
<Property Id="IIS_ROOT">
<RegistrySearch Id="FindInetPubFolder"
Root="HKLM"
Key="SOFTWARE\Microsoft\InetStp"
Name="PathWWWRoot"
Type="directory" />
</Property>
<Directory Id="TARGETDIR"
Name="SourceDir">
<Directory Id="WWWROOT"
Name="wwwroot" />
</Directory>
<CustomAction Id="SetWWRootDirFromIIS"
Return="check"
Property="WWWROOT"
Value="[IIS_ROOT]"
Execute="firstSequence" />
<InstallUISequence>
<Custom Action="SetWWRootDirFromIIS"
After="AppSearch">
(MaintenanceMode="Modify" OR NOT INSTALLED) AND IIS_ROOT
</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetWWRootDirFromIIS"
After="AppSearch">
(MaintenanceMode="Modify" OR NOT INSTALLED) AND IIS_ROOT
</Custom>
</InstallExecuteSequence>
If you would like to deliver a static certificate (does not meet the requirements!), you could simply declare it as binary, e.g.:
<Binary Id="certBinary" SourceFile="MyServer.cert.pfx"/>
Thus, the following component could be put together:
<!-- ATTENTION: This component configures
the existing IIS "Default Web Site" with ssl. -->
<Component Id="InstallHttps"
Guid="..."
KeyPath="yes"
Directory="WWWROOT" >
<iis:Certificate Id="cert"
BinaryKey="certBinary"
Name="MyServerCert"
StoreLocation="localMachine"
StoreName="personal"
PFXPassword="password123"
Request="no"
Overwrite="yes" />
<iis:WebSite Id="DefaultWebSite"
Description="Default Web Site"
ConfigureIfExists="yes"
Directory="WWWROOT" >
<iis:WebAddress Id="AllUnassignedHttps"
Port="443"
IP="*"
Secure="yes" />
<iis:CertificateRef Id='cert' />
</iis:WebSite>
</Component>
So that the Default Web Site
is not deleted during uninstallation, you have to extend the component by the following tags:
Permanent="yes"
undNeverOverwrite="yes"
<!-- ATTENTION: This component configures
the existing IIS "Default Web Site" with ssl.
It must be marked with Permanent="yes"!
Otherwise the IIS "Default Web Site"
will be removed on uninstall. -->
<Component Id="InstallHttps"
Guid="..."
KeyPath="yes"
Directory="WWWROOT"
NeverOverwrite="yes"
Permanent="yes" >
<iis:Certificate Id="cert"
BinaryKey="certBinary"
Name="MyServerCert"
StoreLocation="localMachine"
StoreName="personal"
PFXPassword="password123"
Request="no"
Overwrite="yes" />
<iis:WebSite Id="DefaultWebSite"
Description="Default Web Site"
ConfigureIfExists="yes"
Directory="WWWROOT" >
<iis:WebAddress Id="AllUnassignedHttps"
Port="443"
IP="*"
Secure="yes" />
<iis:CertificateRef Id='cert' />
</iis:WebSite>
</Component>
Install an external certificate
In the previous step, we provided a static server certificate, which is not particularly useful for web applications running in popular browsers, since such a static certificate does not have the quality that these web browsers trust the connection (certificate must contain verifiable information about the server).
Let’s assume that a special certificate named MyServerCert.pfx
was created for this web browser with a default password and stored in the following agreed directory:
c:\ProgramData\MyCompany\Certificates\
So we need to modify our WiX component code as follows:
- remove the following line again:
<Binary Id="certBinary" SourceFile="MyServer.cert.pfx"/>
- Change the component definition (iis:Certificate):
<!-- ATTENTION: This component configures
the existing IIS "Default Web Site" with ssl.
It must be marked with Permanent="yes"!
Otherwise the IIS "Default Web Site"
will be removed on uninstall. -->
<Component Id="InstallHttps"
Guid="..."
KeyPath="yes"
Directory="WWWROOT"
NeverOverwrite="yes"
Permanent="yes" >
<iis:Certificate Id="cert"
CertificatePath="[CommonAppDataFolder]\MyCompany\Certificates\MyServerCert.pfx"
Name="MyServerCert"
StoreLocation="localMachine"
StoreName="personal"
PFXPassword="password123"
Request="no"
Overwrite="no" />
<iis:WebSite Id="DefaultWebSite"
Description="Default Web Site"
ConfigureIfExists="yes"
Directory="WWWROOT" >
<iis:WebAddress Id="AllUnassignedHttps"
Port="443"
IP="*"
Secure="yes" />
<iis:CertificateRef Id='cert' />
</iis:WebSite>
</Component>
Instead of the binary key, the certificate path must now be specified!
Attention: You also have to set Overwrite
to no
, otherwise the installation will fail.
Optional component
Finally, the requirement must now be fulfilled that the configuration only takes place if a certificate was found. For this we first look for the file MyServerCert.pfx
and set a property SERVERCERT
.:
<Property Id="SERVERCERT">
<DirectorySearch Id="FindServerCertificate"
Path="[CommonAppDataFolder]\MyCompany\Certificates"
Depth ="1">
<FileSearch Name="MyServerCert.pfx" />
</DirectorySearch>
</Property>
Then we set a condition for the installation of the component:
<Condition><![CDATA[SERVERCERT <> ""]]></Condition>
The configuration of the Default Web Site
will only be executed if the file was found and the property was filled!
Summary
The following pseudocode summarizes the presented steps and fragments again:
<?xml version="1.0" encoding="Windows-1252"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">
<Product ...>
...
<Property Id="SERVERCERT">
<DirectorySearch Id="FindServerCertificate"
Path="[CommonAppDataFolder]\MyCompany\Certificates"
Depth ="1">
<FileSearch Name="MyServerCert.pfx" />
</DirectorySearch>
</Property>
...
<Property Id="IIS_ROOT">
<RegistrySearch Id="FindInetPubFolder"
Root="HKLM"
Key="SOFTWARE\Microsoft\InetStp"
Name="PathWWWRoot"
Type="directory" />
</Property>
...
<CustomAction Id="SetWWRootDirFromIIS"
Return="check"
Property="WWWROOT"
Value="[IIS_ROOT]"
Execute="firstSequence" />
...
<Directory Id="TARGETDIR"
Name="SourceDir">
<Directory Id="WWWROOT"
Name="wwwroot" />
</Directory>
...
<ComponentGroup Id="IIS">
<!-- ATTENTION: This component configures
the existing IIS "Default Web Site" with ssl.
It must be marked with Permanent="yes"!
Otherwise the IIS "Default Web Site"
will be removed on uninstall. -->
<Component Id="InstallHttps"
Guid="..."
KeyPath="yes"
Directory="WWWROOT"
NeverOverwrite="yes"
Permanent="yes" >
<Condition><![CDATA[SERVERCERT <> ""]]></Condition>
<iis:Certificate Id="cert"
CertificatePath="[CommonAppDataFolder]\MyCompany\Certificates\MyServerCert.pfx"
Name="MyServerCert"
StoreLocation="localMachine"
StoreName="personal"
PFXPassword="password123"
Request="no"
Overwrite="no" />
<iis:WebSite Id="DefaultWebSite"
Description="Default Web Site"
ConfigureIfExists="yes"
Directory="WWWROOT" >
<iis:WebAddress Id="AllUnassignedHttps"
Port="443"
IP="*"
Secure="yes" />
<iis:CertificateRef Id='cert' />
</iis:WebSite>
</Component>
...
<Component Directory="MyWebDir"
Id="MyWebComponent"
Guid="..."
KeyPath="yes">
<!--"DefaultWebSite" referenced in next line-->
<iis:WebVirtualDir WebSite="DefaultWebSite"
Id="MyWebApp"
Alias="MyWebApp"
Directory="MyWebDir" >
<iis:WebApplication Id="MyWebApplication"
Name="MyWeb"
WebAppPool="MyAppPool" />
</iis:WebVirtualDir>
<iis:WebAppPool Id="MyAppPool"
Name="MyAppPool"
MaxCpuUsage="75"
ManagedRuntimeVersion="v4.0"
ManagedPipelineMode="Integrated">
</iis:WebAppPool>
</Component>
</ComponentGroup>
...
<InstallUISequence>
<Custom Action="SetWWRootDirFromIIS"
After="AppSearch">
(MaintenanceMode="Modify" OR NOT INSTALLED) AND IIS_ROOT
</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetWWRootDirFromIIS"
After="AppSearch">
(MaintenanceMode="Modify" OR NOT INSTALLED) AND IIS_ROOT
</Custom>
</InstallExecuteSequence>
...
</Product>
</Wix>
- Tags:
- development
- msi
- wix
- iis
- https
- ssl
- certificates