• Registry Cmdlets: First Steps with CDXML

    Summary: Richard Siddaway shows how to start creating CDXML cmdlets.

    Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the second post in a series. Yesterday, in Registry Cmdlets: Working with the Registry, I explained that there aren’t any cmdlets for working directly with the registry. I showed some examples of using cmdlets that I’ve created to access the registry. The cmdlets are created from the WMI registry class, StdRegProv, by using CDXML.

    In this post, I’ll show you how to get started with CDXML. Before that, though, I’d better explain CDXML...

    Cmdlet definition XML (CDXML) is a way to create a Windows PowerShell module from a WMI class by using the cmdlets-over-objects technology that was introduced in Windows PowerShell 3.0. As usual, it’s always easier to show you an example:

    <?xml version="1.0" encoding="utf-8"?>

    <PowerShellMetadata xmlns="http://schemas.microsoft.com/cmdlets-over-objects/2009/11">

     <Class ClassName="ROOT\cimv2\Win32_BIOS">

      <Version>1.0</Version>

      <DefaultNoun>Bios</DefaultNoun>

     

      <InstanceCmdlets>

       <GetCmdletParameters DefaultCmdletParameterSet="DefaultSet" >

          

       </GetCmdletParameters>

      </InstanceCmdlets>

     </Class>

     </PowerShellMetadata>

    The first two lines in the previous command are header information, and they have to be present in all CDXML files. You also need to state the WMI class you want to use:

    <Class ClassName="ROOT\cimv2\Win32_BIOS">

    The namespace and class have to be supplied. This is equivalent to:

    Get-CimInstance -Namespace root/CIMV2 -ClassName Win32_BIOS

    You can only use a single WMI class in a CDXML module. If you need to access several WMI classes in your module, create a CDXML file per class, and use a module manifest to perform the load.

    The version is arbitrary, and is up to you as to how it is changed as you change the module. Here is an example:

    <Version>1.0</Version>

    Windows PowerShell cmdlets have a noun-verb naming convention. CDXML allows you to set a default noun:

    <DefaultNoun>Bios</DefaultNoun>

    If you don’t supply a noun for the name of any cmdlet in your module, it will use the default noun.

    The following four lines define the standard Get cmdlet in the module:

    <InstanceCmdlets>

       <GetCmdletParameters DefaultCmdletParameterSet="DefaultSet" >

       </GetCmdletParameters>

    </InstanceCmdlets>

    The remaining XML closes the tags.

    Save the XML with a .cdxml extension. I’ve used Bios.cdxml. You treat the CDXML file as a Windows PowerShell module. It’s most closely analogous to a .psm1 file. Create a folder on your module path with the same name as your module and save the file in that folder. The module will be automatically loaded in the same way as any other Windows PowerShell module when you start a Windows PowerShell session.

    If you are working on the module in a series of stages, save it in a working folder that’s not on your module path. You can then load the module directly:

    Import-Module .\bios.cdxml

    If you are testing and debugging, you may need to load the module back into your Windows PowerShell session. You can ensure that you get the latest version of the module by using the –Force parameter:

    Import-Module .\bios.cdxml –Force

    £> Get-Module -Name Bios | Format-Table -AutoSize

    ModuleType Version Name ExportedCommands

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

    Cim    1.0   bios Get-Bios

    The module contains a single cmdlet: Get-Bios. The module type shows as CIM. If you use a module manifest, the module type will change to Manifest.

    If you use Get-Command on the cmdlet, for example:

    £> Get-Command Get-Bios -Syntax

    Get-Bios [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [<CommonParameters>]

    ...you’ll see that there are some parameters that you didn’t define: –CimSession, –ThrottleLimit, and –AsJob. These are always generated on a CDXML module. You get this functionality for free. You also get the standard common parameters without any effort on your part.

    You use the cmdlet in the same manner as any other cmdlet:

    £> Get-Bios

    SMBIOSBIOSVersion : 2.04.0950

    Manufacturer   : American Megatrends Inc.

    Name       : 2.04.0950

    SerialNumber   : 036685734653

    Version      : OEMA - 1072009

    This is the same data and format that you’d get by using the WMI class directly:

    £> Get-CimInstance -ClassName Win32_BIOS

    SMBIOSBIOSVersion : 2.04.0950

    Manufacturer   : American Megatrends Inc.

    Name       : 2.04.0950

    SerialNumber   : 036685734653

    Version      : OEMA - 1072009

    You can use Get-Help on the cmdlet. It will only show the basic syntax. If you want extensive help, you need to create an XML-based external Help file for the module. There is no way to provide inline, comment-based Help like you can for a Windows PowerShell module.

    Now that you’ve seen a basic CDXML module, let’s start putting together our Registry module. This code is the first step:

    <?xml version="1.0" encoding="utf-8"?>

    <PowerShellMetadata xmlns="http://schemas.microsoft.com/cmdlets-over-objects/2009/11">

     <Class ClassName="root\cimv2\StdRegProv">

      <Version>1.0.0.0</Version>

      <DefaultNoun>Registry</DefaultNoun>

     

      <StaticCmdlets>

       <Cmdlet>

        <CmdletMetadata Verb="Get" Noun="RegistrySTRING" ConfirmImpact="Low"/>

        <Method MethodName="GetSTRINGvalue">

         <ReturnValue>

          <Type PSType="System.UInt32"/>

          <CmdletOutputMetadata>

          </CmdletOutputMetadata>

         </ReturnValue>

        

         <Parameters>

          <Parameter ParameterName="hDefKey" >

           <Type PSType="System.UInt32" />

           <CmdletParameterMetadata PSName="Hive">

           </CmdletParameterMetadata>

          </Parameter>

          <Parameter ParameterName="sSubKeyName" >

           <Type PSType="System.String" />

           <CmdletParameterMetadata PSName="SubKey">

           </CmdletParameterMetadata>

          </Parameter>

          <Parameter ParameterName="sValueName" >

           <Type PSType="System.String" />

           <CmdletParameterMetadata PSName="ValueName">

           </CmdletParameterMetadata>

          </Parameter>

          <Parameter ParameterName="sValue">

           <Type PSType="System.String" />

           <CmdletOutputMetadata />

          </Parameter>

         </Parameters>

        </Method>

       </Cmdlet>  

      </StaticCmdlets>

      </Class>

    </PowerShellMetadata>

    The first few lines define the class and version, as you’ve seen previously. I used Registry as the default noun. The next line is very important. In our Registry module, it is:

    <StaticCmdlets>

    The corresponding line in the BIOS example was:

    <InstanceCmdlets>

    The difference is that with the BIOS example, you are working with an instance of the object that exists. When you start your computer, the BIOS loads, and that information is available to you. You can’t create another instance of the BIOS!

    Other classes where you are dealing with instances include Win32_LogicalDisk, Win32_ComputerSystem, and Win32_OperatingSystem. These are all objects that exist on your system, and therefore, they can have instances.

    The Registry class is a little different. As you saw yesterday, the StdRegProv class provides a set of static methods for working with the registry. You don’t have instances in this case. The <StaticCmdlets> element tells the system that you are dealing with static members of the class rather than instances of CIM objects.

    Eventually, the module will create a cmdlet for most methods in the StdRegProv class. To get a quick reminder of the methods available, you can use:

    Get-CimClass -ClassName StdRegProv | select -ExpandProperty CimClassMethods

    Many registry properties are strings, so I’m going to start with the GetStringValue method:

        <CmdletMetadata Verb="Get" Noun="RegistrySTRING" ConfirmImpact="Low"/>

        <Method MethodName="GetSTRINGvalue">

    The cmdlet metadata sets the verb as Get and the noun as RegistrySTRING (this is capitalized for demonstration purposes, but feel free to revert to normal Windows PowerShell case conventions if you desire). This cmdlet will be called Get-RegistrySTRING and it will use the GetSTRINGvalue method.

    The methods in CIM classes return data and a return value. If the return value is zero (0), it worked correctly. Any other value indicates failure. A return value is useful when troubleshooting registry issues, so you should include it in your cmdlet:

         <ReturnValue>

          <Type PSType="System.UInt32"/>

          <CmdletOutputMetadata>

          </CmdletOutputMetadata>

         </ReturnValue>

    The type is very important. It must be System.UInt32, that is, an unsigned 32-bit integer. CIM works with unsigned integers, which only have positive values:

    £> [uint32]::MinValue

    0

    £> [uint32]::MaxValue

    4294967295

    In contrast, the standard integer can take positive and negative values:

    £> [int32]::MinValue

    -2147483648

    £> [int32]::MaxValue

    2147483647

    The last step in defining the cmdlet is to supply the information that is required to define its parameters. Each parameter is defined in a separate parameter block. The GetStringValue method has four arguments—three input arguments and one output argument.

    Get-CimClass and the WMI documentation refer to method parameters rather than arguments. To avoid additional confusion, I'm using arguments when I am talking about the method and I'm using parameters when I am talking about the resultant cmdlet.

    The first input argument, hDefKey, defines the hive to be accessed. Remember that this is a number, for instance:

    HKEY_LOCAL_MACHINE = 2147483650 (0x80000002)

    It is supplied as an unsigned integer and the method argument name is overridden, so the cmdlet parameter name is Hive rather than hDefKey. This isn’t necessary, but it makes the cmdlet easier to use. The name of your cmdlet parameter is your choice—use whatever makes the cmdlet easier to use for you.

          <Parameter ParameterName="hDefKey" >

           <Type PSType="System.UInt32" />

           <CmdletParameterMetadata PSName="Hive">

           </CmdletParameterMetadata>

          </Parameter>

    The sSubKeyName and sValueName arguments for GetStringValue define the value you are reading from the registry. Both use "System.String" as their type, and I’ve overridden the argument names so the cmdlet’s parameters are SubKey and ValueName respectively:

          <Parameter ParameterName="sSubKeyName" >

           <Type PSType="System.String" />

           <CmdletParameterMetadata PSName="SubKey">

           </CmdletParameterMetadata>

          </Parameter>

     

          <Parameter ParameterName="sValueName" >

           <Type PSType="System.String" />

           <CmdletParameterMetadata PSName="ValueName">

           </CmdletParameterMetadata>

          </Parameter>

    The final argument for the GetStringValue method is sValue. This again is a string, but it is the property for the return object that holds the value read from the registry. <CmdletOutputMetadata /> is required to ensure the data is output:

          <Parameter ParameterName="sValue">

           <Type PSType="System.String" />

           <CmdletOutputMetadata />

          </Parameter>

       Note  Renaming output parameters doesn’t seem to be possible.

    Save the file with the CDXML definition as registry1.cdxml. You can load it into Windows PowerShell:

    £> Get-Command -Module registry1

    CommandType   Name                        ModuleName

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

    Function    Get-RegistrySTRING                 registry1

    £> Get-Command Get-RegistrySTRING -Syntax

    Get-RegistrySTRING [-Hive <uint32>] [-SubKey <string>] [-ValueName <string>] [-CimSession <CimSession[]>] [-ThrottleLimit <int>] [-AsJob] [-WhatIf] [-Confirm] [<CommonParameters>]

    You can use the following commands to use it.

    To define the hive to use (in this case, HKLM):

     [uint32]$hklm = 2147483650

    To define the subkey and value to read:

     $subkey = "SOFTWARE\Microsoft\Internet Explorer"

     $value = "Version"

    To use the cmdlet:

    Get-RegistrySTRING -Hive $hklm -SubKey $subkey -ValueName $value

    This gives the following result:

    sValue              ReturnValue

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

    9.11.9600.17498      0

    Now you know how to get started creating cmdlets for working with the registry. This series will continue tomorrow when I will show you how to change the code to enable you to use friendly names for the registry hives, make parameters mandatory, and perform validation on the values input to parameters.

    We invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, remember eat your cmdlets every day with a dash of creativity.

    Richard Siddaway, Windows PowerShell MVP and Honorary Scripting Guy

  • PowerTip: Use PoweShell to Search for CIM Classes

    Summary: Learn how to use Windows PowerShell to search for CIM classes.

    Hey, Scripting Guy! Question How can I use Windows PowerShell to find a CIM class on my machine?

    Hey, Scripting Guy! Answer Use Get-WmiObject in List mode. For instance, to find the class for working with the registry, use:

    Get-WmiObject -Namespace root -Class StdRegProv -List –Recurse

         Note If you don’t know where a class can be found, always start at the root.

    If you want to find the classes for working with disks:

    Get-WmiObject -Namespace root -Class *disk* -List -Recurse

    If you know the namespace, you can simplify the search:

    Get-WmiObject -Namespace root\cimv2 -Class *disk* -List –Recurse

         Note If you don’t provide a namespace, the default of root\cimv2 is used.

    When you find likely classes, use Get-CimClass to investigate details.

  • Registry Cmdlets: Working with the Registry

    Summary: Richard Siddaway investigates how to use CIM to manage the registry.

    Honorary Scripting Guy, Richard Siddaway, here filling in for my good friend The Scripting Guy. Today, I'm starting a series about registry cmdlets by investigating how to work with the registry.

    The bad news is that there aren’t any cmdlets for working with the registry. There’s a Registry provider, which means you can use the Item and ItemProperty cmdlets to manage the local registry—but there aren’t any specific registry cmdlets associated with the provider.

    The good news is that we can adopt the approach that many teams at Microsoft have taken and create our own by using cmdlet definition XML (CDXML). A Common Information Model (CIM) class is wrapped in some fairly simple XML and published as a Windows PowerShell module. If you look in the Modules folder on a computer running Windows 8.1, Windows 8, Windows Server 2012 R2, or Windows Server 2012, you will find many files with a CDXML extension:

    Get-ChildItem -Path C:\Windows\System32\WindowsPowerShell\v1.0\Modules -Filter *.CDXML -Recurse

    These files are a great resource for figuring out how CDXML should be constructed. Other useful resources include:

    By the end of this series, you will have a Registry module that will make working with the registry much easier, and you will have learned how to use CDXML so that you can create other modules based on your favorite CIM classes.

    First though, we should have a quick recap of using CIM to work with the registry.

    Note Although there is a technical difference between CIM and WMI, I will be using them interchangeably, which seems to be common practice.

    The registry is accessed through the StdRegProv class. You can examine the class like this:

    Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv

      NameSpace: ROOT/cimv2

    CimClassName            CimClassMethods   CimClassProperties

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

    StdRegProv             {CreateKey, Delet... {}

    The class is also available in the root\default namespace. This is the only option if you are running Windows XP or Windows Server 2003. These versions are no longer supported, so we’ll use the root\cimv2 namespace for the remainder of the series. If you want to check if the classes are the same, you can examine the root\default version like this:

    Get-CimClass -Namespace root\default -ClassName StdRegProv

    When you look at the StdRegProv class, the first thing you notice is that it has no properties. The class provides methods only. To drill into the methods:

    $class = Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv

    $class.CimClassMethods | select Name

    Name

    ----

    CreateKey

    DeleteKey

    EnumKey

    EnumValues

    DeleteValue

    SetDWORDValue

    SetQWORDValue

    GetDWORDValue

    GetQWORDValue

    SetStringValue

    GetStringValue

    SetMultiStringValue

    GetMultiStringValue

    SetExpandedStringValue

    GetExpandedStringValue

    SetBinaryValue

    GetBinaryValue

    CheckAccess

    SetSecurityDescriptor

    GetSecurityDescriptor

    As shown here, all the methods are static:

    £> $class.CimClassMethods["GetSTRINGvalue"] | Format-List

    Name    : GetStringValue

    ReturnType : UInt32

    Parameters : {hDefKey, sSubKeyName, sValueName, sValue}

    Qualifiers : {implemented, static}

    This means that you don’t need an instance of the class to work with—you can simply use the methods. The parameters for these methods are relatively straightforward:

    £> $class.CimClassMethods["GetSTRINGvalue"].Parameters

    Name               CimType Qualifiers

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

    hDefKey                  UInt32 {ID, IN}

    sSubKeyName        String {ID, IN}

    sValueName            String {ID, in}

    sValue                      String {ID, out}

    This is where we meet the first nasty bit. The hdefkey parameter defines the registry hive you want to work with. It’s an unsigned integer that can take the following values:

    HKEY_CLASSES_ROOT = 2147483648 (0x80000000)

    HKEY_CURRENT_USER = 2147483649 (0x80000001)

    HKEY_LOCAL_MACHINE = 2147483650 (0x80000002)

    HKEY_USERS = 2147483651 (0x80000003)

    HKEY_CURRENT_CONFIG = 2147483653 (0x80000005)

    HKEY_DYN_DATA = 2147483654 (0x80000006)

    This list shows the hive and the related unsigned integer as a decimal and hexadecimal number. I have found that it’s easiest to define these values as variables. For instance, to work with the HKEY_LOCAL_MACHINE hive, use:

    [uint32]$hklm = 2147483650

    Let’s look at a little Windows PowerShell history for a moment...

    When Windows PowerShell 1.0 launched, it immediately gave us a way to work with WMI (Get-WmiObject). That was a huge step forward from using VBScript. Unfortunately, we couldn’t use the StdRegProv class directly because of its static methods. The answer was to use the [wmiclass] type accelerator:

    $reg = [wmiclass]"\\.\root\cimv2:StdRegprov"

    Then you can use Get-Member like this:

    $reg | Get-Member

    Now you'll see an object from the System.Management.ManagementClass:

    System.Management.ManagementClass#ROOT\cimv2\StdRegProv

    I tend to use variables for things like registry keys and values because it makes my code reusable. A couple of relatively safe values to use are:

    $subkey = "SOFTWARE\Microsoft\Internet Explorer"

    $value = "Version"

    You can then call a method on the class:

    $reg.GetSTRINGvalue($hklm, $subkey, $value)

    The important parts of the results are:

    ReturnValue   : 0

    sValue      : 9.11.9600.17126

    ReturnValue is the return code. A value of 0 in the ReturnValue is good. Anything else is bad, and could be very bad. Finding what the return code means can be difficult, but some information is available if you search WMI return values.

    The sValue property holds the return value you want. You can access it directly by using standard techniques:

    $reg.GetSTRINGvalue($hklm, $subkey, $value) | select -ExpandProperty sValue

         –or–

    ($reg.GetSTRINGvalue($hklm, $subkey, $value)).sValue

    This technique is still valid and works with the latest versions of Windows PowerShell. However, working with the registry became much easier in Windows PowerShell 2.0 when Invoke-WmiMethod joined the game. Our registry access code became:

    Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name GetSTRINGvalue -ArgumentList $hklm, $subkey, $value

    However, life is never straightforward...a bit of a problem dropped into our laps. To explain, I need to create a registry key to work with. I can use the $reg object that I created earlier:

    $newkey = "SOFTWARE\HSGtest"

    $reg.CreateKey($hklm, $newkey)

    And let’s add a value:

    $newname = 'Date'

    $newvalue = 'June 2013'

    $reg.SetSTRINGvalue($hklm, $newkey, $newname, $newvalue)

    And finally, let's test that it’s created correctly:

    $reg.GetSTRINGvalue($hklm, $newkey, $newname)

    The truncated output shows:

    ReturnValue      : 0

    sValue           : June 2013

    Oops! This where I realize that I’ve used the wrong data, so now I need to modify the data:

    $newvalue = 'June 2014'

    And for a change, I’ll use Invoke-WmiMethod:

    Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name SetSTRINGvalue $hklm, $newkey, $newname, $newvalue

    A return code of 0 indicates that it worked. But let’s assume I’m a bit paranoid and I want to check the data:

    $reg.GetSTRINGvalue($hklm, $newkey, $newname)

    What I get is:

    ReturnValue   : 0

    sValue      : June 2013

    In reality, the value hasn’t been changed. This isn’t a Windows PowerShell issue. It's related to the underlying .NET classes. It turns out that when you are using Invoke-WmiMethod, the order of the arguments matters. The cmdlet expects them in alphabetical order, not the order the documentation shows (which is the order that you’d use if using the [wmiclass] approach).

    This problem will be most noticeable if the arguments are different data types. You get a nice big juicy error message that states you have a type mismatch: code 2147749893. The quick way to check the correct order of arguments is to use Get-CimClass:

    $class = Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv

    $class.CimClassMethods["SetSTRINGvalue"].Parameters

    Examining the Name of the parameters shows this:

    Name

    ----

    hDefKey

    sSubKeyName

    sValue

    sValueName

    Let’s retry it in that order:

    Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name SetSTRINGvalue $hklm, $newkey, $newvalue, $newname

    $reg.GetSTRINGvalue($hklm, $newkey, $newname)

    And it works.

    One way to avoid this confusion is to use the CIM cmdlets, which were introduced in Windows PowerShell 3.0. The problem goes away because you have to give argument name and value pairs:

    To set data:

    $newvalue = ‘June 2015’

    Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName SetSTRINGvalue -Arguments @{hDefKey=$hklm; sSubKeyName=$newkey; sValueName=$newname; sValue=$newvalue}

    And to read data:

    Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName GetSTRINGvalue -Arguments @{hDefKey=$hklm; sSubKeyName=$newkey; sValueName=$newname}

    This still leaves us with a lot of typing. Wouldn’t it be better if we could do this:

    Set-RegistrySTRING -Hive HKLM -SubKey $newkey -ValueName $newname -Value 'April 2015'

    Get-RegistrySTRING -Hive HKLM -SubKey $newkey -ValueName $newname

    These are cmdlets from the module that I’m going to show you how to create in the next few posts, but for now I need to clean up my registry:

    Remove-RegistryKey -Hive HKLM -SubKey $newkey

    Bye for now. Next time, we’ll start digging into how we can use CDXML to create registry cmdlets.

    Biography

    Richard Siddaway is based out of the UK. He spends his time automating anything and everything, for Kelway, Ltd. A 7-year Windows PowerShell MVP, Richard is a prolific blogger, mainly about Windows PowerShell (see Richard Siddaway's Blog). Richard has been a director at PowerShell.org since the inception of that organization, and he is a frequent speaker at Windows PowerShell user groups and conferences. He has written a number of books: PowerShell in Practice, PowerShell and WMI, PowerShell in Depth (co-author), PowerShell Dive (co-editor), and Learn Active Directory Management in a Month of Lunches, which features lots of Windows PowerShell. All of the books are available from Manning Publications.

    We invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, remember eat your cmdlets every day with a dash of creativity.

    Richard Siddaway, Windows PowerShell MVP and Honorary Scripting Guy

  • PowerTip: Back up GPOs with PowerShell

    Summary: Use Windows PowerShell to back up all GPOs in a domain.

    Hey, Scripting Guy! Question How can I use Windows PowerShell to generate a backup of all Group Policy Objects in a domain?

    Hey, Scripting Guy! Answer Use the Backup-GPO cmdlet from the Remote Server Administration Tools (RSAT).
               Specify a name, path, domain, and server, for example:

    Backup-GPO -All -Path \\share\backups -Comment "weekly Backup" -Domain nwtraders.com -Server dc1