Nuget and Powershell WebAdministration Module Issues / by Matt Wrock

I’ve been working on getting an authentication library  packaged using nuget. The library uses an X509 certificate to encrypt and decrypt auth ticket data stored in a cookie. So to make it easy to get the library up and running, I wanted to create an install.ps1 script that would install the x509 cert in the correct location of the cert store. In order for the library to access the certificate, the account under which the hosting web application runs needs to have read permissions on the cert. I need to figure out which account that is and then use winhttpcertcfg.exe to grant that account the appropriate permissions.

I had assumed that this would simply involve loading the WebAdministration powershell module and then accessing its cmdlets to query which website has a physical path matching the one of the project that is referencing  my package. Then find its application pool’s process model to determine which account the application runs under.

When I began testing this script from within the nuget package manager console, I started getting an unexpected error on the first call into any cmdlet of the WebAdministration module:

Get-Website : Retrieving the COM class factory for component with CLSID {688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed d
ue to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
At line:1 char:12
    + CategoryInfo          : NotSpecified: (:) [Get-Website], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.IIs.PowerShell.Provider.GetWebsite

After spending some time researching this, I discovered that nuget uses the 32 bit powershell.exe shell and that the module is looking for com classes not registered in the wow64 registry on a 64 bit system. This proved to be very frustrating and I was wondering if discovering the IIS user account would not be possible in my nuget install.

After more research, I discovered that I could get to what I needed using the .net Microsoft.Web.Administration assembly. While probing this assembly is not quite as friendly and terse as the WebAdministration module, it met my needs perfectly. Here is the powershell script that determines the application pool identity:


param($installPath, $toolsPath, $package, $project)

function GetAppPoolAccount([string] $webDirectory){    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")    $iis = new-object Microsoft.Web.Administration.ServerManager    $account = "NetworkService"    $site = $iis.sites | where-object {$_.state -eq "Started"}|         foreach-object {$_.applications} | where-object {            $_.virtualdirectories -contains (                foreach-object {$_.virtualdirectories} | where-object {                    $_.physicalpath -eq $webDirectory                }            )        }    if($site)    {        $poolName = $site.applicationPoolName        $pool = $iis.applicationpools | where-object{$ -eq $site.ApplicationPoolName}        if($pool.processModel.identityType -eq "SpecificUser"){            $account = $pool.processModel.userName        }        elseif($pool.processModel.identityType -eq "ApplicationPoolIdentity"){            $account = "IIS APPPOOL\$poolName"        }        else{            $account = $pool.processModel.identityType        }    }    return $account}

. GetAppPoolAccount -webDirectory (Get-Item $project.FullName).Directory.$toolsPath\InstallAuthCerts $toolsPath "INT" $account

Essentially the Microsoft.Web.Administration assembly exposes the IIS applicationHost.config data in the same format that it exists in xml. My script loads the assembly, instantiates the ServerManager class and iterates its members to get the information I need. I then call into my InstallAuthCerts script with the path of the cert, the environment, and the account name which will install the cert and grant the appropriate permissions to $account.

Now if someone can point out how to use the WebAdministration module from a nuget console, I’d be grateful for that information. I dug around quite a bit and from what I can tell, it can’t be done. At least not on  a 64 bit machine.