IIS snapin extensibility. Part 3.

Adding custom types to provider namespace

To add new entry into IIS namespace you will need to prepare your own XML file with name ending with 'namespace.xml' and copy it into the same folder, where NavigationTypes.namespace.xml is located. During startup provider will read all files using filter '*.namespace.xml' and combine their contents into common namespace tree. Let's take a specific example. We will add IIS global modules as children of namespace root and we will place all global modules under node 'Modules'. I will use PowerShell 'here string' to populate layout file:

$xml = @'
<?xml version="1.0" encoding="utf-8"?>
<navigationTypes>
  <Modules>
    <type>Microsoft.IIs.PowerShell.Framework.NodeCollection</type>
    <factoryType>Microsoft.IIs.PowerShell.Framework.NodeCollectionFactory</factoryType>
    <assembly>Microsoft.IIS.PowerShell.Framework, Version=7.1.0.0, Culture=neutral, PublicKeyToken=1a5b963c6f0fbeab, processorArchitecture=MSIL</assembly>
    <instanceXPath>/system.webServer/globalModules</instanceXPath>
    <filePath>MACHINE/WEBROOT/APPHOST</filePath>
    <configurationPath>%filePath%</configurationPath>
    <readonly>true</readonly>
    <parents>
      <parentInfo>
        <parentType>Server</parentType>
        <xPath>system.webServer/globalModules</xPath>
        <filePath>MACHINE/WEBROOT/APPHOST</filePath>
      </parentInfo>
    </parents>
  </Modules>
  <Module>
    <type>Microsoft.IIs.PowerShell.Framework.NamespaceNode</type>
    <factoryType>Microsoft.IIs.PowerShell.Framework.NamespaceNodeFactory</factoryType>
    <assembly>Microsoft.IIS.PowerShell.Framework, Version=7.1.0.0, Culture=neutral, PublicKeyToken=1a5b963c6f0fbeab, processorArchitecture=MSIL</assembly>
    <instanceXPath>system.webServer/globalModules/add[@name="%name%"]</instanceXPath>
    <filePath>MACHINE/WEBROOT/APPHOST</filePath>
    <newItemParameters>
      <Parameter Name="Image" Type="System.String" />
      <Parameter Name="PreCondition" Type="System.String" />
    </newItemParameters>
    <parents>
      <parentInfo>
        <parentType>Modules</parentType>
        <xPath>/system.webServer/globalModules/add</xPath>
        <filePath>MACHINE/WEBROOT/APPHOST</filePath>
      </parentInfo>
    </parents>
  </Module>
</navigationTypes>
'@
new-item $pshome\modules\webadministration\module.namespace.xml `

-type file -value $xml

If you installed snapin that is released outside of Windows this file should go to WebAdministration snapin folder, i.e. its name will be $env:programfiles\iis\powershellsnapin\module.namespace.xml.

Node 'Modules' defines 'Server' as parent, server type will execute query '/system.webServer/globalModules' to get instances of this child. It means we will have globalModules configuration section assigned to this namespace node.

Under the node 'Modules' we will have all global modules from collection, defined on configuration section '/globalModules'. Parent will select children using XPath query '/system.webServer/globalModules/add', i.e. will add all entries from default collection of section globalModules. Now, let's check, does it work or not. To reload namespace layout we have to restart provider. To do this under v1.0 you will need to execute remove-pssnapin then add-pssnapin, I will do the same with module:

remove-module WebAdministration
import-module WebAdministration
dir iis:\

Name
----
AppPools
Modules
Sites
SslBindings

OK, we have collection node. What's below?

dir iis:\modules

Name                     Image
----                     -----
AnonymousAuthenticationM %windir%\System32\inetsrv\authanon.dll
odule
ApplicationRequestRoutin %ProgramFiles%\IIS\Application Request Routing\requestRo
g                        uter.dll
CgiModule                %windir%\System32\inetsrv\cgi.dll
...
WSMan                    %windir%\system32\wsmsvc.dll

Excellent, we see all global modules there. With this new nodes you could use most of provider commands, you could remove nodes, edit nodes, but how could you create new module here? Provider needs to know, which parameters to use for new element, and since we are using generic classes NamespaceNode and NamespaceNodeFactory, we have to pass parameters to new-item API somehow. This is where we use entry <newItemParameters>. Provider reads this entry and returns new item parameters class with properties, defined there to PowerShell.

new-item iis:\modules\TestModule -image %windir%\foo\bar.dll

Name                     Image
----                     -----
TestModule               %windir%\foo\bar.dll

This command doesn't validate parameters, therefore for test we could pass whatever we want. New module it there:

PS E:\dev\powershell\Bin #> dir iis:\modules\test*

Name                     Image
----                     -----
TestModule               %windir%\foo\bar.dll

This is just a simple example. When you want to extend provider with configuration data that could be obtained through XPath queries, all you need is to add namespace layout for this data. You also could use this technique to extend existing namespace nodes with extra properties. In case if you want to add node that exposes data from some other source, not configuration, you have to use your managed types to back this node and its factory. Those types should implement interfaces INamespaceNode and INamespaceNodeFactory. You will use type names in the layout file for <type>, <factoryType> entries, <assembly> entry should have a fully qualified assembly name that contains those types. This assembly has to be placed into the GAC to make it discoverable. I will give you an example of this in one of following posts.

--Sergei

No Comments