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