Configuring SSL using Javascript in IIS 7.0

In IIS 7.0 the configuration system has a nice feature that lets you extend it using what we sometimes refer as dynamic properties. This properties rather than being hard-coded in XML in some .config file they are implemented by COM interface that whenever a tool queries its value, the configuration system will create an instance of such a COM object and will query its value (through the IAppHostPropertyExtension interface). In our configuration system we used this capability to surface some runtime features such as the state of an Application Pool. We also used this feature to expose the SSL Certificate configured in a site binding. Furthermore, this extensibility also allows us to expose methods that provide functionality in a very similar way (through the IAppHostMethodExtension interface). We used this capability to provide functionality such as recycling an Application Pool, restarting a Site, or adding a certificate to a binding.

So in this post I just want to show how using this capability from Javascript you can easily provision an HTTPS web site by leveraging our configuration system and the extensions we provide for HTTP.SYS (HttpQueryServiceConfiguration/HttpSetServiceConfiguration) configuration below. Note that functionality we expose as properties include reading the certificate hash, reading the certificate store, reading the DS Mapper flag. And also as Methods we expose the ability to configure a certificate, enable and disable the DS Mapper functionality.

try {
    // Setup SSL in default web site, port 443, certificate hash
    SetupSsl("Default Web Site", "*:443:", "3efb3448636941cde7dae47c377f14188cbeb740");
}
catch(e) {
    WScript.Echo(e.description);
}

function SetupSsl(siteName, bindingInformation, certificateHash) {
    var adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager');

    var sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST"); 

    //-------------------------------------------------------------
    // Find the Site
    var siteElementPos = FindElement(sitesSection.Collection, "site", ["name", siteName]);
    if (siteElementPos == -1) throw new Error( "Site not found!");
    var siteElement = sitesSection.Collection.Item(siteElementPos);

    //-------------------------------------------------------------
    // Verify if the binding already exists...
    var bindingsCollection = siteElement.ChildElements.Item("bindings").Collection;
    var bindingElementPos = FindElement(bindingsCollection, "binding", ["protocol", "https", "bindingInformation", bindingInformation]);
    if (bindingElementPos != -1) throw new Error( "Binding Already Exists!!!");

    //-------------------------------------------------------------
    // Create the binding in the IIS configuration system
    var bindingElement = bindingsCollection.CreateNewElement("binding");
    bindingElement.Properties.Item("protocol").Value = "https";
    bindingElement.Properties.Item("bindingInformation").Value = bindingInformation;
    bindingsCollection.AddElement(bindingElement, -1);

    //-------------------------------------------------------------
    // Configure HTTP.Sys SSL certificate using the method extension
    var methodInstance = bindingElement.Methods.Item("AddSslCertificate").CreateInstance();
    methodInstance.Input.Properties.Item("certificateHash").Value = certificateHash;
    methodInstance.Input.Properties.Item("certificateStoreName").Value = "MY";
    methodInstance.Execute();

    //-------------------------------------------------------------
    // You can also use the certificateHash extension property to read the certificate Hash
    WScript.Echo(bindingElement.Properties.Item("certificateHash").Value);

    //-------------------------------------------------------------
    // You could also Configure HTTP.Sys DS Mapper functionality if needed:
    WScript.Echo(bindingElement.Properties.Item("isDsMapperEnabled").Value);
    //bindingElement.Methods.Item("EnableDsMapper").CreateInstance().Execute();
    //bindingElement.Methods.Item("DisableDsMapper").CreateInstance().Execute();

    adminManager.CommitChanges();
}

    //-------------------------------------------------------------
    // Helper method to find an element in a collection based on valuesToMatch
function FindElement(collection, elementTagName, valuesToMatch) {
    for (var i = 0; i < collection.Count; i++) {
        var element = collection.Item(i);

        if (element.Name == elementTagName) {
            var matches = true;
            for (var iVal = 0; iVal < valuesToMatch.length; iVal += 2) {
                var property = element.GetPropertyByName(valuesToMatch[iVal]);
                var value = property.Value;
                if (value != null) {
                    value = value.toString();
                }
                if (value != valuesToMatch[iVal + 1]) {
                    matches = false;
                    break;
                }
            }
            if (matches) {
                return i;
            }
        }
    }

    return -1;
}

Some of the nice side effects of exposing this as extensions in the IIS Configuration system is that it is now possible to call this method from a remote machine as well through the DCOM support of AHADMIN.