Building PowerShell with WMIv2

In this two-part blog post set, we are going to describe the design of the networking management system we use introduced in Windows 2012. Before Windows Server 2012 the managing of host settings (IP addresses, routes, firewall rules, etc.) as well as many networking services (DNS server, DHCP server, etc.) used a disparate set of tools.

Windows Server 2012 brought modernized management of these technologies with WMIv2 and PowerShell support. We built a whole bunch of WMIv2 providers for networking and then created cmdlets as wrappers for that WMI functionality.

This post isn’t really about networking, but really about how developers can build their own WMIv2 providers and then create “wrapper” PowerShell cmdlets, just like those we created for networking. There is no special sauce. Windows Defender (with their Defender PowerShell module) is another example of an app using this pattern.

In particular, we’re going to discuss:

1. The benefits of building a WMIv2 provider.

2. An overview of how the NetRoute WMI provider and supporting cmdlets function.

The 2nd blog post will discuss the usage of PowerShell advanced functions for some of our networking cmdlets, in particular Get-NetIPConfiguration and Test-NetConnection.

When you are building an app or service on Windows, there are numerous ways you can enable users to manage your product:

clip_image002

Windows Server 2012 made it easier than ever to build PowerShell cmdlets and WMI providers, with the PowerShell cmdlets serving as simple wrappers of the WMI functionality. This platform capability was leveraged for most networking cmdlets and is what we’ll be describing here.

Building a WMIv2 Provider

WMI is the Windows implementation of the DMTF CIM standard. CIM provides standard methods for querying and modifying the configuration of a computing device. WMI has shipped in Windows for a long time but writing WMI providers used to be a very difficult, error-prone task. That all changed with the new WMIV2 provider model which proved to be very simple and easy. The resultant provider looks the same to all existing management tools, it is just an easier way to implement a provider. This means that a WMIV2 provider can be used by rich ecosystem of standards-based management tools like the System Center suite of tools. The DMTF also specifies a web services protocol (WS-Management) for remote management.

Here is an example of retrieving the routes of a server, using CIM cmdlets available in Windows Server 2012. We’re going to use routes as the example through this blog.

PS C:\ > Get-CimInstance -Namespace root/StandardCimv2 –ClassName MSFT_NetRoute

Implementing management functionality in WMIv2 provides numerous benefits:

Interoperability

Because the CIM manageability interface is based on a standard; anyone can implement an app on any platform that leverages your WMI provider. For example, an app on a Linux machine can remotely manage a Windows device.

Broad API Support and Scale

On Windows, there are numerous APIs for accessing WMI providers. Developers can access your WMI provider from .NET or directly using native code.

Here is an example of querying all the routes of a device using the C# CIM API:

using System;

using System.Collections.Generic;

using Microsoft.Management.Infrastructure;

public partial class MI

{

    public static void EnumerateInstances()

    {

        try

        {

            CimSession cimSession = CimSession.Create("localhost");

            IEnumerable<CimInstance> enumeratedInstances =

                cimSession.EnumerateInstances(@"root/StandardCimv2", "MSFT_NetRoute");

            foreach (CimInstance cimInstance in enumeratedInstances)

            {

                Console.WriteLine("{0}",

cimInstance.CimInstanceProperties["Name"].Value.ToString());

       }

        }

        catch (CimException ex)

        {

            // Handle the exception as appropriate.

            // This example prints the message.

            Console.WriteLine(ex.Message);

        }

    }

}

You can can see a similar example for the C++ API here on MSDN, showing the Win32_Process class being enumerated.

Simple PowerShell

The WMIv2 providers you create can be easily transformed into PowerShell cmdlets. This is called cmdletization and allows you to hit two birds with one stone: build a low-level management API (for developers) and a simple to use console management experience (for IT Pros) for your app or service.

The mapping of WMI functionality to PowerShell uses a cmdlet definition XML file (CDXML).

image

Managing Routes

For more information on building a WMIv2 provider. [link]

Building a WMIv2 provider starts by defining the properties and methods of your management objects. You do this in a Managed Object Format (MOF) file.

The MOF file for core TCP and IP objects, including those for NetRoute, are located directly at

C:\Windows\System32\wbem\NetTCPIP.mof. There is also some online documentation. NetRoute isn’t a very complex class, with just a few properties. The relevant excerpt for NetRoute is below with some comments.

class MSFT_NetRoute : CIM_NextHopRoute 

{

// The basic properties we’re going to expose

[read : ToSubclass] string DestinationPrefix;

[read : ToSubclass] uint32 InterfaceIndex;

[read : ToSubclass] string InterfaceAlias;

[read : ToSubclass] string NextHop;

[read : ToSubclass,write : ToSubclass,ValueMap{"0", "1", "2"} : ToSubclass] uint8 Publish;

[read : ToSubclass,write : ToSubclass] datetime ValidLifetime;

[read : ToSubclass,write : ToSubclass] datetime PreferredLifetime;

[ValueMap{"0", "1"} : ToSubclass,read : ToSubclass] uint8 Store;

// The value maps are used to create friendly enumerations for data that is stored numerically

[read : ToSubclass,ValueMap{"2", "23"} : ToSubclass] uint16 AddressFamily;

[read : ToSubclass,ValueMap{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",

"12", "13", "14", "15", "16", "17", "18", "19"} : ToSubclass] uint16 Protocol;

// Static methods have to be declared in the class’ MOF file

[implemented,static : ToSubclass DisableOverride] uint32 Create([In] uint32

InterfaceIndex,[In] string InterfaceAlias,[In] string DestinationPrefix,[In] string

NextHop,[In] uint8 Publish,[In] uint16 RouteMetric,[In] uint16 Protocol,[In] datetime

ValidLifetime,[In] datetime PreferredLifetime,[In] string PolicyStore,[In] uint16

AddressFamily,[In] boolean PassThru,[Out,EmbeddedInstance("MSFT_NetRoute") :

ToSubclass] MSFT_NetRoute CmdletOutput[]);

[implemented,static : ToSubclass DisableOverride] uint32 Find([In] uint32

InterfaceIndex,[In] string LocalIPAddress,[In] string RemoteIPAddress,

[Out,EmbeddedInstance("CIM_ManagedElement") : ToSubclass] CIM_ManagedElement

CmdletOutput[]);

};

Our management functionality is going to have the following tasks, which map to WMI methods we have to expose. The WMI instance methods here are inherited by our class. The static methods are custom and are declared in the MOF file.

Task

Cmdlet

WMI Method

Retrieving routes

Get-NetRoute

Enumerate (Instance method)

Modifying routes

Set-NetRoute

Modify (Instance method)

Removing routes

Remove-NetRoute

Delete (Instance method)

Creating routes

New-NetRoute

Create (Static method)

Searching routes

Find-NetRoute

Find (Static method new in R2))

The SDK tool Convert-MofToProvider can take your MOF file and create a Visual Studio project containing the stub C++ code. There is a good example in MSDN for what the generated stub would look like. The meat of your provider is built within that stub.

Looking at the CDXML

Once you have the WMI provider finished, now you have to create a PowerShell wrapper using CDXML. Below is a folded snippet of the NetRoute CDXML, commonly available at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\MSFT_NetRoute.cdxml.

This shows how we defined the instance cmdlets. If you check out Get-Help Get-NetRoute, you’ll see how the parameters map via the CDXML.

<InstanceCmdlets>

<GetCmdletParameters DefaultCmdletParameterSet="ByName">

<QueryableProperties>

<Property PropertyName="DestinationPrefix">

<Property PropertyName="InterfaceIndex">

<Property PropertyName="InterfaceAlias">

<Property PropertyName="NextHop">

<Property PropertyName="AddressFamily">

<Property PropertyName="Publish">

<Property PropertyName="RouteMetric">

<Property PropertyName="Protocol">

<Property PropertyName="ValidLifetime">

<Property PropertyName="PreferredLifetime">

</QueryableProperties>

<QueryableAssociations>

<Association Association="MSFT_NetIPInterfaceRoute" ResultRole="Dependent" SourceRole="Antecedent">

</QueryableAssociations>

<QueryOptions>

<Option OptionName="PolicyStore">

<Option OptionName="IncludeAllCompartments">

</QueryOptions>

</GetCmdletParameters>

<Cmdlet>

<CmdletMetadata Verb="Set" ConfirmImpact="Medium"/>

<Method MethodName="cim:ModifyInstance" >

<GetCmdletParameters DefaultCmdletParameterSet="ByName">

</Cmdlet>

<Cmdlet>

<CmdletMetadata Verb="Remove" ConfirmImpact="High"/>

<Method MethodName="cim:DeleteInstance" />

</Cmdlet>

</InstanceCmdlets>

The static cmdlets are defined similarly:

<StaticCmdlets>

<Cmdlet>

<CmdletMetadata Verb="New" ConfirmImpact="Medium" DefaultCmdletParameterSet="ByInterfaceAlias"/>

<Method MethodName="Create" CmdletParameterSet="ByInterfaceAlias">

<Method MethodName="Create" CmdletParameterSet="ByInterfaceIndex">

</Cmdlet>

<Cmdlet>

<CmdletMetadata Verb="Find"/>

<Method MethodName="Find">

</Cmdlet>

</StaticCmdlets>

Building a Module and Formatting

In building your PowerShell cmdlets, it is important to keep in mind the cmdlet development guidelines. This is how you can ensure your cmdlets provide an experience consistent with the PowerShell environment.

PowerShell cmdlets can be built using .NET, directly in PowerShell as an advanced function, or with WMIv2 – as we are describing in this blog. In all cases, the cmdlets must be installed in a PowerShell module. The formatting and output types of cmdlets must also be described.

For the NetTCPIP module, which houses the NetRoute functionality, you can review this content directly:

  • Module Description
    • C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\NetTCPIP.psd1
  • Types definition, for formatting
    • C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Types.ps1xml
  • Formatting information
    • C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Format.ps1xml

There is nothing incredibly interesting or new about these files, so we’ll skip over them. More documentation about formatting and modules for PowerShell is available online.

The End Result

The end-result is a WMI provider and 5 cmdlets. We have fairly nice table formatting of routes, the “routing table” of the device.

PS C:\> Get-Command *NetRoute*

CommandType Name ModuleName

----------- ---- ----------

Function Find-NetRoute NetTCPIP

Function Get-NetRoute NetTCPIP

Function New-NetRoute NetTCPIP

Function Remove-NetRoute NetTCPIP

Function Set-NetRoute NetTCPIP

 

PS C:\> Get-NetRoute

ifIndex DestinationPrefix NextHop RouteMetric PolicyStore

------- ----------------- ------- ----------- -----------

3 255.255.255.255/32 0.0.0.0 256 ActiveStore

1 255.255.255.255/32 0.0.0.0 256 ActiveStore

3 224.0.0.0/4 0.0.0.0 256 ActiveStore

1 224.0.0.0/4 0.0.0.0 256 ActiveStore

3 192.168.1.255/32 0.0.0.0 256 ActiveStore

3 192.168.1.10/32 0.0.0.0 256 ActiveStore

3 192.168.1.0/24 0.0.0.0 256 ActiveStore

1 127.255.255.255/32 0.0.0.0 256 ActiveStore

1 127.0.0.1/32 0.0.0.0 256 ActiveStore

1 127.0.0.0/8 0.0.0.0 256 ActiveStore

3 0.0.0.0/0 192.168.1.1 0 ActiveStore

9 ff00::/8 :: 256 ActiveStore

3 ff00::/8 :: 256 ActiveStore

1 ff00::/8 :: 256 ActiveStore

When should you build WMIv2-Based Cmdlets?

As stated earlier, there are numerous benefits to building a PowerShell experience with WMIv2 and cmdletization. If the management task is relevant in a datacenter or large enterprise, the interoperability and broad API support are valuable features, and that’s why we used this design pattern for most networking manageability. The Windows management infrastructure abstracts remoting and security (since providers are automatically executed in the context of the invoker); this helps in building functionality that works at datacenter scale.

Overall, here are the set of requirements for building a PowerShell and WMIv2 management experience. We point again to our specific examples for NetRoute.

 

  1. Define your classes in MOF files.
    1. C:\Windows\System32\wbem\NetTCPIP.mof
  2. Build the WMI provider.
    1. This includes the task of registering the WMI provider so it’s installed, using Register-CimProvider.
  3. Cmdletize the WMI provider using CDXML.
    1. C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\MSFT_NetRoute.cdxml.
  4. “Package” and format the PowerShell functionality.
    1. Module Description
      1. C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\NetTCPIP.psd1
    2. Types definition, for formatting
      1. C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Types.ps1xml
    3. Formatting information
      1. C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Format.ps1xml

There is some overhead to building management functionality in this way. In particular, building a MOF file and WMI provider in native code can be more difficult than building a PowerShell cmdlet in .NET. Even easier is building a PowerShell advanced function, a cmdlet written directly in PowerShell.

There were a couple of places where we bypassed WMI. Resolve-DNSName is a .NET cmdlet. Get-NetIPConfiguration and Test-NetConnection are two cmdlets built as PowerShell advanced functions. These cmdlets can be used with PowerShell remoting – but there is no standards-based interface. We did this mainly because these are tools and aren’t used to actually change a system’s configuration, and there is little value in standardizing their interface, but it could save us lots of time. We’ll talk more about that in the next blog post.

For now, we hope you got an idea of how WMIv2 providers work, and how you can cmdletize their functionality to provide a great PowerShell experience on top!

Christopher Palmer, Program Manager