Quantcast
Channel: Scripting Blog
Viewing all 2129 articles
Browse latest View live

Working with Fixed Number Sizes in PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about working with fixed number sizes in Windows PowerShell.

Hey, Scripting Guy! Question Hey, Scripting Guy! I have a script that returns a specific type of number, so I added a type constraint on the variable that holds the number. The problem is that sometimes when I run the script, I get errors. I cannot figure this out. Can you help me?

—BG

Hey, Scripting Guy! Answer Hello BG,

Microsoft Scripting Guy, Ed Wilson, is here. We have not had any snow this year here in Charlotte, North Carolina. This is good news, and of course, bad news. The good news is that because the city is rarely prepared for snow storms, any snow at all causes major problems. The bad news is that we do not get to see snow very often—maybe once every three or four years, if we are lucky. Personally, I enjoy looking at snow from the windows, but I really do not like it when I have to get outside in it. Snow causes lots of problems if you are not expecting it, and if you are not prepared for it.

We run into much the same issue when it comes to constraining number types in Windows PowerShell. Because Windows PowerShell has a greatly enhanced adaptive-type system, and it can change the types of the objects that lay under the covers, one rarely needs to deal with things (such as a type mismatch) that plague other object-oriented languages.

This, of course, has good and bad effects. At times, if I am not really paying attention, I can get tripped up if I am not expecting the object transformation. To be honest, this has only happened to me a couple of times, and it was usually because I was trying to do something pretty tricky in the first place. One way around this, is to place a type constraint on the object, and then force it to raise an error message when the change takes place. This can be helpful for spotting these sorts of changes.

Note  To be clear, most of the time (in fact, I would say over 90 percent of the time), it is best to allow Windows PowerShell to manage the types.

Changing number types

The default number type in Windows PowerShell is an Int, and that is also an Int32. If the number becomes too big, Windows PowerShell will change the number from an Int to a Double. I wrote the following script to illustrate the point:

function get-double

{

 Param($a)

  $b = $b + $a

  "now $($b.gettype())"

  if ($b -eq 'Infinity') {Return}

  get-double $b

}

I load the function and call it by passing the number 2. This is shown here:

Get-Double 2

When I run the script, it runs for a few seconds and then stops when it reaches Infinity. Part of the output is shown here:

Image of command output

It is clear from the output that when Windows PowerShell reaches the maximum value of Int32, it changes the type of number that is contained inside the variable $b to a Double. Most of the time, this is the behavior I want.

Constraining the type of the number in a variable

But suppose that the value of $b should never become greater than a certain value, and that number is an Int. The script appears to work, but it takes a long time to complete. As I troubleshoot the script, I decide to constrain the value of $b to a number type that makes sense for my script. I'm going to try an Int16.

In the following script, I use the type constraint [int16] to ensure that the variable $b only holds an Int16.

function get-int

{

 Param($a)

 $erroractionpreference = "stop"

  [int16]$b = $b + $a

  "now $b"

  get-int $b

}

If the value in $b becomes greater than 32768, an error arises. Because the error that arises is not a terminating error, the script would run and run and run, but continue to display errors. That is not the behavior I want, so I changed the value of $errorActionPreference from "continue" to "stop". The output is shown here:

Image of command output

By constraining the type of number that I store in the $b variable, it becomes obvious that the problem with my script is that I am, somehow, getting a number that is too big. In reality, a problem such as that is usually traceable to one of the following:

  • A loop that runs too long (an upper limit is not properly controlling the loop execution)
  • Variables that are not being properly initialized prior to reuse

Because Windows PowerShell has an adaptive-type system, such problems are often difficult to see. They show themselves as weird and inconsistent output, or you may have a script that appears to work, but it takes a really long time to complete. Constraining my types can make troubleshooting easier and enable me to more readily spot such problems.

BG, that is all there is to using numeric type constraints. Numbers Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Use Parentheses to Override Operator Precedence

$
0
0

Summary: Use parentheses to override operator precedence in Windows PowerShell.

Hey, Scripting Guy! Question How can I ensure that a math expression that keeps returning incorrect information will work properly in
           Windows PowerShell?                                               

Hey, Scripting Guy! Answer In Windows PowerShell (as in other languages), math operators have a precedence. Therefore, division
           comes before addition. If you want to override this behavior, group with parentheses, for example:

PS C:\> 2 + 9 / 3

(2 + 9)/3

5

3.66666666666667

Formatting PowerShell Decimal Places

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using strings to format the display of decimal places in Windows PowerShell.

Hey, Scripting Guy! Question Hey, Scripting Guy! One of the things I have a problem with is when I get numbers that have a lot of decimal places. I mean, how do I really get rid of them—at least properly? I have done things like convert them to integers, but that loses me all of the decimal places. I have also tried using Trimor other string techniques with mixed success. I would love to see how a real pro does it. Can you help me?

—PA

Hey, Scripting Guy! Answer Hello PA,

Microsoft Scripting Guy, Ed Wilson, is here. Like a lot of people, I like to work with a plan. I need to know what I am going to be doing a week from now, a month from now, and even a year from now. But when I have a plan in place, I do not give it a lot of extra thought. I mean, that is what the planning stage is for, right? To plan?

Well, PA, a similar thing happens when I am working with Windows PowerShell. I like to know where I am going, but then I hold off until just when I need to do something before I actually do it. What am I talking about?

Well numbers, for one thing.

Using a custom format string to control decimal places

When I use a custom format string to control the number of decimal places, I change the underlying type from a number type to a string. In the following example, I convert from a Double to a String:

PS C:\> $d = 1.11

PS C:\> $d.GetType()

IsPublic IsSerial Name                         BaseType                

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

True     True     Double                         System.ValueType        

PS C:\> $s = (1.11).tostring("#.#")

PS C:\> $s.GetType()

IsPublic IsSerial Name                          BaseType                

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

True     True     String                           System.Object

This change might be OK if I am sending the output to its final destination—for example, displaying it to the Windows PowerShell console or printing it on a printer. But if I am storing the data, perhaps in a database, it is probably best to store the actual number. This is because I can easily create a report from the database that displays only one decimal place, but I have no idea what the second decimal place might be.

Additionally, I could run into other problems trying to perform math operations on strings instead of numbers. So as a best practice, put it off and make these types of conversions the last thing you do to your data.

However, when I am trying to read data, I have a problem keeping track of more than two or three decimal places. The numbers begin to run together. So if I am trying to gain a quick impression, I like to easily change to one or two decimal places. To do this, I use a custom number format string.

The ToStringmethod is everywhere in Windows PowerShell—even number types contain a ToStringmethod. The great thing is that I can easily override the method with my own custom format string. Here is an example of changing a number that has two decimal places to a number that contains a single decimal place:

PS C:\> (1.11).tostring("#.#")

1.1

All I need to do is use the pound sing ( # ) where I want a digit to appear. So in the example, I had two decimal places, but I only wanted one.

If I have numbers on the right side of the decimal, I need only a single pound sign. On the left side of the decimal, everything becomes rounded up if there are additional numbers, or nothing happens if there are no numbers. The following example illustrates how I limit my output to two decimal places:

PS C:\> $a = 1.1, 2.22, 3.333, 4.4444, 5.55555

PS C:\> $a | % {$_.ToString("#.##")}

1.1

2.22

3.33

4.44

5.56

In the previous output, 5.55555 rounds up to 5.56, but 4.4444 rounds down to 4.44. The number 1.1 displays as 1.1, even though my format string of “#.##” contains two pound signs.

If I am working with output from a Windows PowerShell cmdlet, I can use the same technique by using a customized property. I can do this with Select-Object, Format-Table, or Format-List (and even with Out-GridView).

In the following image, I see the CPU time of several process reports as 0.03, and 0.02.

Image of command output

As shown here, if I format the output so that it only displays a single decimal place, I lose much of the CPU output:

Get-Process |

Where CPU |

Sort CPU -Descending |

Select-Object ProcessName,

@{l='cpu';e={($_.cpu).tostring("#.#")}}

The script and its associated output are shown here:

Image of command output

PA, that is all there is to using custom format strings to control the display of decimal places in Windows PowerShell. Numbers Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Format Decimal Numbers in PowerShell

$
0
0

Summary: Learn how to display a specific number of decimal points in Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to display only one decimal place on a number that currently has two?

Hey, Scripting Guy! Answer Use a custom number format string with the ToString method from the number, for example:

PS C:\> (1.11).tostring("#.#")

1.1

PowerTip: Round a Number with PowerShell

$
0
0

Summary: Use Windows PowerShell to round numbers to a specific decimal place.

Hey, Scripting Guy! Question How can I use Windows PowerShell to round a number to a specific number of decimal places, and continue to
           have a number instead of converting it to a string.

Hey, Scripting Guy! Answer Use the Round static method from the System.Math class. By default, it does not display decimal places, but
           you can easily change that behavior. The following code illustrates this procedure:

PS C:\> $a = 111.2226

PS C:\> [math]::Round($a)

111

PS C:\> [math]::Round($a,2)

111.22

PS C:\> [math]::Round($a,3)

111.223

Using PowerShell and Standard Number Formats

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using standard number formats.

Hey, Scripting Guy! Question Hey, Scripting Guy! I love using Windows PowerShell. I use it for everything. My problem is that it does not do everything automatically. For example, I use the Get-Volume cmdlet a lot to check on disk space, but I need to do a manual calculation to see the percentage of free space. This involves basically writing a script and dividing the numbers, multiplying by a hundred, and then trimming all the excess decimal places. Is there anything you can do to help me?

—AV

Hey, Scripting Guy! Answer Hello AV,

Microsoft Scripting Guy, Ed Wilson, is here. This morning I am sitting here sipping a beautiful cup of Darjeeling tea. I added a cinnamon stick to it, and that is it. It is robust, complex, and deeply relaxing. I am also munching on a freshly made bagel, with just a bit of cream cheese on it. I am checking my email on my Surface Pro 3, and looking outside wondering where the snow is. No snow here in Charlotte. Nope, not a flake.

Anyway, on cool, crisp mornings such as this, it is nice to open the Windows PowerShell ISE and curl up with a nice script. However, AV, your problem doesn’t need a scripted solution. You may want to write a quick script, but it is not totally necessary. As someone once said, I can solve that problem in one line.

Display formatted percentages

I can use the Windows PowerShell command Get-Volume (it is technically a function and not a cmdlet) to display a nice output that contains information about my drives. This command and its output are shown here:

Image of command output

One of the properties that appears to be missing is percentage of free space. I like to create a custom property for an object by using the Select-Object cmdlet. To do this, I use a hash table. I specify a label (a name for the property) and an expression (a script block that creates a value for that property). I can use the aliases l for label and e for expression. The hash table looks like this:

@{l='percent free';e={($_.sizeremaining/$_.size)}}

When I do this, I can use the special number format code, “P”, to create my percentage. Basically, it multiplies my value by 100 and returns the first two places after the decimal point. It also rounds up or down as necessary. The number format code becomes an override for the ToStringmethod. In the following example, I show how to call the ToStringmethod. Note that the “P” goes inside the parentheses that are required for all method calls.

PS C:\> (5/10).ToString("P")

50.00 %

To compute the percent free for each of my volumes, I pipe the Get-Volume function to the Select-Object cmdlet, and I specify the volume letter and my hash table as properties. Here is the command I use:

Get-Volume | Select driveletter, @{l='percentfree';e={($_.sizeremaining/$_.size).tostring("P")}}

The command and the output from the command are shown here:

Image of command output

AV, that is all there is to using Windows PowerShell to display a formatted percentage. Numbers  Week will continue tomorrow when I will talk about more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Express Percentage in PowerShell

$
0
0

Summary: Learn an easy method to express a percentage in Windows PowerShell.

Hey, Scripting Guy! Question How can I use Windows PowerShell to express a two-decimal place percentage? For example, I have multiple
           calculations in a Windows PowerShell script, and I am tired of always dividing two numbers, multiplying
           by a hundred, and trimming the results to two decimal places.

Hey, Scripting Guy! Answer Use the special “P” format specifier as an overload to the ToString method, for example:

PS C:\> (5/21).tostring("P")

23.81 %

Rounding Numbers—PowerShell Style

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to round numbers.

Hey, Scripting Guy! Question Hey, Scripting Guy! I think your series about formatting numbers has been pretty interesting. However, everything you have shown has a major problem—and that is that you convert numbers into strings. I would love to be able to display a Windows PowerShell number with a specific number of decimal places, but not have it be a string. Instead, I want it to be a number type. Can you do this? Or am I stuck with storing numbers and then converting them into strings before I display the results?

—GS

Hey, Scripting Guy! Answer Hello GS,

Microsoft Scripting Guy, Ed Wilson, is here. This morning, I decided to make a cup of Gunpowder green tea. I put a bit of lemon in it, and that is it. Gunpowder green tea seems to go well with kiwis—the flavors complement each other nicely. It is very refreshing. Normally, I drink a black tea in the morning, and then maybe have a cup of some sort of green tea in the afternoon. But I decided I wanted to start drinking more green tea.

I am sitting at my table, checking email sent to scripter@microsoft.com, and I ran across your email. GS, you are right. I have been focusing on methods that in the end convert the number to a string. So lets correct that right now.

Rounding a number with Windows PowerShell

To round a number in Windows PowerShell, all I need to do is to call the Roundstatic method from the System.Math class. A static method just means that it is directly available from the class, and that I do not need to create an instance of it first. Because the Math class is in the System .NET Framework namespace, I can drop the initial System when I call the static method. I can therefore call the method like this:

PS C:\> [math]::Round(2.25)

2

Notice that by default, the Roundmethod does not include any decimal places. The second parameter for the Roundmethod permits me to specify how many places I wanted to include. This technique is shown here:

PS C:\> [math]::Round(2.25, 1)

2.2

Notice that here the decimal, .25, rounded down to .2. This is because the number (2) is even. In the following example, the .35 rounds up because the number (3) is odd.

PS C:\> [math]::Round(2.35, 1)

2.4

A practical example of rounding

File sizes in Windows PowerShell appear as bytes. This can make the numbers a bit difficult to read. Luckily, Windows PowerShell includes a number of admin constants to make the conversion from bytes to kilobytes or megabytes easier to accomplish. The problem is nearly as bad when it comes to file sizes because the conversion usually produces a long string of decimal places. The answer is to use the Roundmethod and to select a number that is more easily digested.

In the following example, I use the Get-Item cmdlet to return information about a file that happens to reside on my hard drive. I then choose the Lengthproperty. That tells me the size of the file in bytes. I then convert the file size to megabytes and display the results. Following that, I use the Roundmethod choose the default, and choose two decimal places. This is shown in the following output:

PS C:\> Get-Item C:\fso\epcisar.txt

    Directory: C:\fso

Mode             LastWriteTime     Length        Name                                                          

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

-a---         7/17/2014   3:33 PM    6531842 epcisar.txt                                                    

PS C:\> $a = Get-Item C:\fso\epcisar.txt

PS C:\> $a.Length / 1MB

6.22924995422363

PS C:\> [math]::round($a.Length / 1MB)

6

PS C:\> [math]::round($a.Length / 1MB, 2)

6.23

Now that I know what I want to do, I am going to display the name of the file and the size of the file in megabytes—but I am going to do it in a single line. To do this, I will use the Select-Object cmdlet and create a new property. I use a hash table to create the new property. For the label (l is my property alias), I use ‘size in MB’. The expression picks the length property from the pipeline.

In the following example, I first divide the length by 1 MB, and then I call the [math]::Round static method and tell it that I want two decimal places:

Get-Item C:\fso\epcisar.txt | select name, @{l='size in MB'; e={[math]::Round(($_.length /1MB),2)}}

The command and the output from the command are shown in the following image:

Image of command output

GS, that is all there is to using Windows PowerShell to round numbers. This also concludes Numbers Week. Join me tomorrow for PowerShell Spotlight when I will have guest post by Windows PowerShell MVP, Teresa Wilson (The Scripting Wife).

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Use PowerShell to Round to Specific Decimal Place

$
0
0

Summary: Use Windows PowerShell to round numbers to a specific decimal place.

Hey, Scripting Guy! Question How can I use Windows PowerShell to round a number to a specific number of decimal places, and
            continue to have a number instead of converting it to a string.

Hey, Scripting Guy! Answer Use the Roundstatic method from the System.Math class. By default, it does not display decimal places,
           but you can easily change that behavior. The following code illustrates this procedure:

PS C:\> $a = 111.2226

PS C:\> [math]::Round($a)

111

PS C:\> [math]::Round($a,2)

111.22

PS C:\> [math]::Round($a,3)

111.223

PowerTip: Use PowerShell to Display Network Adapter Power Settings

$
0
0

Summary: Use Windows PowerShell to display network adapter power settings.

Hey, Scripting Guy! Question How can I use Windows PowerShell to see if my laptop is going to sleep—it keeps dropping off of the
            wireless network.

Hey, Scripting Guy! Answer Use the Get-NetAdapterPowerManagement Windows PowerShell command to display the power
           management settings for your network adapter.

Weekend Scripter: Use PowerShell to Troubleshoot Group Policy Part 2

$
0
0

Summary: Guest blogger, Alex Verboon, continues his series about using Windows PowerShell to troubleshoot Group Policy.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Alex Verboon. Today Alex adds to his previous blog about using Windows PowerShell to troubleshoot Group Policy. He provides a script that automates this process. You can read his blog at Anything about IT.

Take it away Alex…

In my previous post, Use PowerShell to Troubleshoot Group Policy, I shared a script that retrieves the Group Policy processing time. As shown here, when specifying the optional –ShowDetails switch, the Get-GPProcessingTime script output also displays the Correlation:ActivityID that represents one instance of Group Policy processing.

To get all the details of what happened during that Group Policy processing cycle, we simply retrieve all events that have the corresponding ActivityID:

Get-GPEventByCorrelationID -Computer TestClient1 -CorrelationID f7cb68e1-f6da-4d23-8fca-c4cb85158de2

Here is the script output:

Image of command output

Following is the full script. You can download this script from the Script Center Repository at Get-GPEventByCo​rrelationID.

function Get-GPEventByCorrelationID

{

<#

.Synopsis

   Get Group Policy Eventlog entries by Correlation ID

.DESCRIPTION

   This function retrieves Group Policy event log entries filtered by Correlation ID from the specified computer

.EXAMPLE

   Get-GPEventByCorrelationID -Computer TestClient -CorrelationID A2A621EC-44B4-4C56-9BA3-169B88032EFD

 

TimeCreated                     Id LevelDisplayName Message                                                         

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

7/28/2014 5:31:31 PM          5315 Information      Next policy processing for CORP\CHR59104$ will be attempted in...

7/28/2014 5:31:31 PM          8002 Information      Completed policy processing due to network state change for co...

7/28/2014 5:31:31 PM          5016 Information      Completed Audit Policy Configuration Extension Processing in 0...

.......

 

#>

    [CmdletBinding()]

    Param

    (

        [Parameter(Mandatory=$true,

        ValueFromPipelineByPropertyName=$true,

        HelpMessage="Enter Computername(s)",

        Position=0)]

        [String]$Computer = "localhost",

        # CorrelationID

        [Parameter(Mandatory=$true,

        ValueFromPipelineByPropertyName=$true,

        HelpMessage="Enter CorrelationID",

        Position=0)]

        [string]$CorrelationID

        )

 

    Begin

    {

        $Query = '<QueryList><Query Id="0" Path="Application"><Select Path="Microsoft-Windows-GroupPolicy/Operational">*[System/Correlation/@ActivityID="{CorrelationID}"]</Select></Query></QueryList>'

        $FilterXML = $Query.Replace("CorrelationID",$CorrelationID)

    }

    Process

    {

        $orgCulture = Get-Culture

        [System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"

        $gpevents = Get-WinEvent -FilterXml $FilterXML -ComputerName $Computer

        [System.Threading.Thread]::CurrentThread.CurrentCulture = $orgCulture

    }

    End

    {

        [System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"

        $gpevents | Format-Table -Wrap -AutoSize -Property TimeCreated, Id, LevelDisplayName, Message

        [System.Threading.Thread]::CurrentThread.CurrentCulture = $orgCulture

     }

}

In addition to what I have posted here and in the Script Center Repository, I found the following script written by Thomas Bouchereau (PFE), which is similar: Group Policy processing events collection with PowerShell.

~Alex

Thank you, Alex, for sharing your time and knowledge. This is an awesome script and I wanted to make sure we shared it with the community. Join me tomorrow when Windows PowerShell MVP, Richard Siddaway, begins a great series about working with the registry. You will not want to miss it.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Back up GPOs with PowerShell

$
0
0

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

Registry Cmdlets: Working with the Registry

$
0
0

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: Use PoweShell to Search for CIM Classes

$
0
0

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: First Steps with CDXML

$
0
0

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 PowerShell to Find Parameters for CIM Class Method

$
0
0

Summary: Learn how to discover the parameters for a method in a CIM class.

Hey, Scripting Guy! Question How can I find the parameters of a CIM class method, such as GetStringValue in the StdRegprov class?

Hey, Scripting Guy! Answer Use Get-CimClass to get the class:

$class = Get-CimClass -ClassName StdRegProv

To view the methods:

$class.CimClassMethods

To view the parameters of a particular method:

$class.CimClassMethods["GetStringValue"].Parameters

You will see the parameter name and data type. The qualifiers will tell you if it’s an input or output parameter.

Registry Cmdlets: Advanced CDXML

$
0
0

Summary: Richard Siddaway shows some advanced features in CDXML.

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the third post in a series. To catch up, read:

Yesterday, you saw that you can create a Windows PowerShell cmdlet from the StdRegProv CIM class by using CDXML, which enables you to read a string value from the registry. Today, I want to show you some of the advanced features of CDXML. These enable you to make your CDXML cmdlet act like any other Windows PowerShell cmdlet. For instance, you can make parameters mandatory and validate the values that are input to parameters.

Before that though, I want to show something that makes working with the registry so much easier. One of the biggest pain points when working with the CIM Registry provider, StdRegProv, is those awful numbers you have to remember—the numbers that define the hives:

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)

They are required when using the cmdlet we created yesterday:

[uint32]$hklm = 2147483650

$subkey = "SOFTWARE\Microsoft\Internet Explorer"

$value = "Version"

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

But wouldn’t it be great if you could provide a friendly value for the hive—something like this:

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

There is a simple way to achieve this. The first step is to change the end of your CDXML file from this:

  </Class>

</PowerShellMetadata>

...to this:

  </Class>

  <Enums>

    <Enum EnumName="RSPSNA.Registry.Hive" UnderlyingType="System.UInt32">

      <Value Name="HKCR" Value="2147483648" />

      <Value Name="HKCU" Value="2147483649" />

      <Value Name="HKLM" Value="2147483650" />

      <Value Name="HKUS" Value="2147483651" />

      <Value Name="HKLocalMachine" Value="2147483650" />

      <Value Name="HKCC" Value="2147483653" />

    </Enum>

  </Enums>

</PowerShellMetadata>

You are adding an enumeration definition to the CDXML file. It has a name (in this case, EnumName="RSPSNA.Registry.Hive") and an underlying type (- UnderlyingType="System.UInt32"). You then need to define the values in the enumeration:

<Value Name="HKCR" Value="2147483648" />

Each value has a name-value pair as shown. It is possible to define multiple names for the same value:

      <Value Name="HKLM" Value="2147483650" />

      <Value Name="HKLocalMachine" Value="2147483650" />

This means that you can provide your users with a number of different ways of defining the hive key, and they can use whichever fits their style. I prefer the 4-letter acronyms—it’s less typing!

You also have to change the parameter definitions. Yesterday, you saw this command to define the hive parameter:

            <Parameter ParameterName="hDefKey" >

              <Type PSType="System.UInt32" />

              <CmdletParameterMetadata PSName="Hive">

              </CmdletParameterMetadata>

            </Parameter>

You need to change it to use the enumeration:

            <Parameter ParameterName="hDefKey" >

              <Type PSType="RSPSNA.Registry.Hive" />

              <CmdletParameterMetadata PSName="Hive">

              </CmdletParameterMetadata>

            </Parameter>

The only change is on the Type line where the type is changed to match the name of the enumeration. The name of the type, and therefore the enumeration, is arbitrary and under your control. I recommend that you create a naming scheme for enumerations in your organization.

In my testing, I named this version of the module registry2.cdxml. You import the module and set the values for the subkey and value you want to read:

Import-Module .\registry2.cdxml -Force

$subkey = "SOFTWARE\Microsoft\Internet Explorer"

$value = "Version"

And then use the cmdlet like this:

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

The act of defining an enumeration automatically forces validation of the input:

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

Get-RegistrySTRING : Cannot process argument transformation on parameter 'Hive'. Cannot convert value "HKLL" to type

"Microsoft.PowerShell.Cmdletization.GeneratedTypes.RSPSNA.Registry.Hive". Error: "Unable to match the identifier name

HKLL to a valid enumerator name.  Specify one of the following enumerator names and try again: HKCR, HKCU, HKLM,HKLocalMachine, HKUS, HKCC"

At line:1 char:26

+ Get-RegistrySTRING -Hive HKLL -SubKey $subkey -ValueName $value

+                          ~~~~

    + CategoryInfo          : InvalidData: (:) [Get-RegistrySTRING], ParameterBindingArgumentTransformationException

    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Get-RegistrySTRING

As with other Windows PowerShell cmdlets, you even get a helpful hint as to the accepted values. As a side note, Tab completion works on the enumeration, so type:

Get-RegistrySTRING –Hive

...and then use the Tab key to cycle through the allowed values.

The hive defaults to HKLM, so you can do this:

Get-RegistrySTRING -SubKey $subkey -ValueName $value

This is not a practice I recommend, though. If you explicitly state the registry hive you want to access, it makes troubleshooting easier.

You can add other validation to the cmdlet parameters in a similar manner to advanced functions. The simplest change is to make a parameter mandatory. Add the IsMandatory="true" element to the CmdletParameterMetadata node, and you now have a mandatory parameter:

            <Parameter ParameterName="hDefKey" >

              <Type PSType="RSPSNA.Registry.Hive" />

              <CmdletParameterMetadata IsMandatory="true" PSName="Hive">

              </CmdletParameterMetadata>

            </Parameter>

Like with an existing cmdlet or an advanced function, if you don’t specify the value of a mandatory parameter you will be prompted.

This works as you would expect:

Import-Module .\registry3.cdxml -Force

Get-RegistrySTRING -Hive HKLM -SubKey "SOFTWARE\Microsoft\Internet Explorer" -ValueName Version

Registry3.cdxml is the version where I added the mandatory parameter. If you don’t specify the –Hive parameter, you will be prompted:

£> Get-RegistrySTRING -SubKey "SOFTWARE\Microsoft\Internet Explorer" -ValueName Version

cmdlet Get-RegistrySTRING at command pipeline position 1

Now, supply values for the parameters—you won’t be allowed to specify the parameter without a value:

£> Get-RegistrySTRING -Hive  -SubKey "SOFTWARE\Microsoft\Internet Explorer" -ValueName Version

Get-RegistrySTRING : Missing an argument for parameter 'Hive'. Specify a parameter of type

'Microsoft.PowerShell.Cmdletization.GeneratedTypes.RSPSNA.Registry.Hive' and try again.

At line:1 char:20

+ Get-RegistrySTRING -Hive  -SubKey "SOFTWARE\Microsoft\Internet Explorer" -ValueN ...

+                    ~~~~~

    + CategoryInfo          : InvalidArgument: (:) [Get-RegistrySTRING], ParameterBindingException

    + FullyQualifiedErrorId : MissingArgument,Get-RegistrySTRING

There are a set of tags for setting validation on parameter input; for instance, to validate that the input isn’t null or empty:

            <Parameter ParameterName="sValueName" >

              <Type PSType="System.String" />

              <CmdletParameterMetadata PSName="ValueName">

                <ValidateNotNullOrEmpty />

              </CmdletParameterMetadata>

            </Parameter>

After importing the changed module, you can use it as before:

Get-RegistrySTRING -Hive HKLM -SubKey "SOFTWARE\Microsoft\Internet Explorer" -ValueName Version

If you don’t specify the Subkey value, you will receive an error message:

£> Get-RegistrySTRING -Hive HKLM -SubKey -ValueName Version

Get-RegistrySTRING : Missing an argument for parameter 'SubKey'. Specify a parameter of type 'System.String' and try again.

At line:1 char:31

+ Get-RegistrySTRING -Hive HKLM -SubKey -ValueName Version

+                               ~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [Get-RegistrySTRING], ParameterBindingException

    + FullyQualifiedErrorId : MissingArgument,Get-RegistrySTRING

If you don’t specify the parameter at all, you get a different error message:

£> Get-RegistrySTRING -Hive HKLM  -ValueName Version

Get-RegistrySTRING : Invalid parameter

At line:1 char:1

+ Get-RegistrySTRING -Hive HKLM  -ValueName Version

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (StdRegProv:root\cimv2\StdRegProv) [Get-RegistrySTRING], CimException

    + FullyQualifiedErrorId : HRESULT 0x80041008,Get-RegistrySTRING

If you specify the SubKey but not the value you’ll get an error:

Get-RegistrySTRING -Hive HKLM  -SubKey "SOFTWARE\Microsoft\Internet Explorer"

sValue ReturnValue

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

                        1

Remember that a non-zero return code from a CIM method means that there has been an error.

A good way to discover examples of validation in CDXML modules is to view the files on a system running Windows 8 or Windows Server 2012 (or later versions). You’ll find the examples in the subfolders of C:\Windows\System32\WindowsPowerShell\v1.0\modules.

There are a lot of modules in these systems, and over 60% are created using CDXML. Here is a quick way to search for interesting examples:

Get-ChildItem -Path $pshome\modules -Filter *.cdxml -File -Recurse | Select-String -Pattern '*validate*' -SimpleMatch 

Here are some examples of using validation in CDXML that I discovered in this way:

<ValidateNotNull /> is similar to <ValidateNotNullOrEmpty />, which  you’ve seen already. I prefer to use <ValidateNotNullOrEmpty />.

To validate on a set of values:

                                <ValidateSet>

                                    <AllowedValue>Both</AllowedValue>

                                    <AllowedValue>Incoming</AllowedValue>

                                    <AllowedValue>Outcoming</AllowedValue>

                                </ValidateSet>

This is taken from C:\Windows\System32\WindowsPowerShell\v1.0\modules\Defender\MSFT_MpPreference.cdxml.

You can also validate on a range as shown in C:\Windows\System32\WindowsPowerShell\v1.0\modules\MMAgent\ps_mmagent_v1.0.cdxml:

                <ValidateRange Min="1" Max="8192" />

You might also find examples of other validation options, such as:

                <ValidateCount Min="1" Max="10" />

                <ValidateLength Min="1" Max="10" />

ValidatePattern and ValidateScript from advanced functions are not allowed in CDXML. You can have multiple validations on the same parameter, but remember that once one fails, that’s it—your input is rejected.

If you require them, you also have some options to allow specific scenarios:

                <AllowEmptyCollection />

                <AllowEmptyString />

                <AllowNull />

That’s it for today. Tomorrow I’ll finish this short series with a look at some of the issues that arise when you start working with registry data types other than strings.

Bye for now.

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: Override Modules in PowerShell Session

$
0
0

Summary: Learn how to override the modules you have imported into your Windows PowerShell session.

Hey, Scripting Guy! Question How can I re-load the Windows PowerShell module that I am developing so I can test the changes at each
           stage of development?

Hey, Scripting Guy! Answer By default, if you have a module loaded into to your Windows PowerShell session and you attempt to re-load it,
           nothing happens because Import-Module discovers it is already loaded. Use the –Force parameter to ensure
           that a fresh copy of the module is loaded and you get your latest features:

Import-Module <mymodule> -Force

Registry Cmdlets: Complete the Registry CDXML Module

$
0
0

Summary: Richard Siddaway shows how to complete the registry CDXML module.

Honorary Scripting Guy, Richard Siddaway, here today filling in for my good friend, The Scripting Guy. This is the final post in a series. To catch up, read:

So far, you’ve seen how to create a CDXML module with a single cmdlet based on the GetStringValue method of the StdRegProv CIM class. In this post, I’ll show you how to add the other methods that are available in the class to create a complete module of easy-to-use cmdlets for working with the registry. I’ll also show you how to work with some of the more awkward registry data types.

The complete module consists of these cmdlets:

Get-RegistryBinary

Get-RegistryDWORD

Get-RegistryExpandSTRING

Get-RegistryKey

Get-RegistryMultiSTRING

Get-RegistryQWORD

Get-RegistrySTRING

Get-RegistryValue

New-RegistryKey

Remove-RegistryKey

Remove-RegistryValue

Set-RegistryBinary

Set-RegistryDWORD

Set-RegistryExpandSTRING

Set-RegistryMultiSTRING

Set-RegistryQWORD

Set-RegistrySTRING

These cmdlets provide the ability to get and set values for each of the registry data types, get lists of registry keys and values, create new keys, and delete keys and values.

Reading data from the registry is very useful, but you also need to be able to write to the registry. You can use the SetStringValue method as an example:

      <Cmdlet>

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

        <Method MethodName="SetSTRINGvalue">

          <ReturnValue>

            <Type PSType="System.UInt32"/>

            <CmdletOutputMetadata>

            </CmdletOutputMetadata>

          </ReturnValue>

         

          <Parameters>

            <Parameter ParameterName="hDefKey" >

              <Type PSType="RSPSNA.Registry.Hive" />

              <CmdletParameterMetadata IsMandatory="true" PSName="Hive">

              </CmdletParameterMetadata>

            </Parameter>

            <Parameter ParameterName="sSubKeyName" >

              <Type PSType="System.String" />

              <CmdletParameterMetadata PSName="SubKey">

                <ValidateNotNullOrEmpty />

              </CmdletParameterMetadata>

            </Parameter>

            <Parameter ParameterName="sValueName" >

              <Type PSType="System.String" />

              <CmdletParameterMetadata PSName="ValueName">

                <ValidateNotNullOrEmpty />

              </CmdletParameterMetadata>

            </Parameter>

            <Parameter ParameterName="sValue">

              <Type PSType="System.String"  />

              <CmdletParameterMetadata PSName="Value">

                <ValidateNotNullOrEmpty />

              </CmdletParameterMetadata>

            </Parameter>

          </Parameters>

        </Method>

      </Cmdlet>

In this case, you have to provide the hive, subkey, and value with the data to insert into that value. I’ve used ValueName and Value for the registry value and data respectively:

Set-RegistryString -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName svalue -Value 'Summit2014'

The cmdlet will create the registry value if it doesn’t exist but the key needs to be present already. You can create registry keys as follows:

New-RegistryKey -Hive HKLM -SubKey software\moduletest

Other registry data types include:

  • Binary
  • DWORD – 32-bit number
  • QWORD – 64-bit number
  • Multistting = array of strings
  • Expanding string (usually used with environment variables)

When you look at registry values by using the EnumValues method, the output isn’t very easy to work with:

£> [uint32]$hklm = 2147483650

£> $key = 'software\moduletest'

£> Invoke-CimMethod -ClassName StdRegProv -MethodName EnumValues -Arguments @{hDefKey = $hklm; sSubKeyName = $key}

ReturnValue    : 0

sNames         : {dvalue, qvalue, svalue, mstring...}

Types          : {4, 11, 1, 7...}

PSComputerName :

You get an array of value names in sNames plus an array of data types in Types. The data types (as occurs so often in CIM) are coded as integer values. Ideally, you would want to see the data types output in a friendlier manner.

You can apply a similar technique to the way the numeric values for registry hives were dealt with. Start by creating an enumeration:

    <Enum EnumName="RSPSNA.Registry.Type" UnderlyingType="System.UInt32">

      <Value Name="String" Value="1" />

      <Value Name="ExpandingString" Value="2" />

      <Value Name="Binary" Value="3" />

      <Value Name="Dword" Value="4" />

      <Value Name="MultiString" Value="7" />

      <Value Name="Qword" Value="11" />

    </Enum>

Remember that the name part of the enumeration cannot contain spaces. If you need to break words, use capital letters to designate the start of words or use a delimiter (such as a hyphen).

The CDXML definition for the cmdlet created from the EnumValues method looks like this:

      <Cmdlet>

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

        <Method MethodName="EnumValues">

          <ReturnValue>

            <Type PSType="System.UInt32"/>

            <CmdletOutputMetadata>

            </CmdletOutputMetadata>

          </ReturnValue>

         

          <Parameters>

            <Parameter ParameterName="hDefKey" >

              <Type PSType="RSPSNA.Registry.Hive" />

              <CmdletParameterMetadata IsMandatory="true" PSName="Hive">

              </CmdletParameterMetadata>

            </Parameter>

            <Parameter ParameterName="sSubKeyName" >

              <Type PSType="System.String" />

              <CmdletParameterMetadata PSName="SubKey">

                <ValidateNotNullOrEmpty />

              </CmdletParameterMetadata>

            </Parameter>

            <!-- Two output arrays -->

            <!--                   -->

            <Parameter ParameterName="sNames">

              <Type PSType="System.String[]"  />

              <CmdletOutputMetadata />

            </Parameter>

            <Parameter ParameterName="Types">

              <Type PSType="RSPSNA.Registry.Type[]" />

              <CmdletOutputMetadata />

            </Parameter>

          </Parameters>

        </Method>

      </Cmdlet>

You have two output arguments. The first is sNames, which is an array of strings that contain the value names. The second is an array that is created with the RSPSNA.Registry.Type enumeration supplying the values. You still have the issue of two output arrays, when ideally, you want one set of output objects. Unfortunately, CDXML can’t solve this issue, but a little bit of Windows PowerShell magic gives you the answer.

Using the cmdlet as-is results in this output:

£> Get-RegistryValue -Hive HKLM -SubKey software\moduletest

sNames      : {dvalue, qvalue, svalue, mstring...}

Types       : {Dword, Qword, String, MultiString...}

ReturnValue : 0

You can see that Types now contains the data type names rather than the numeric values. This would be usable for a registry key with only a handful of values, but ideally, you want something easier to read:

£> $values = Get-RegistryValue -Hive HKLM -SubKey software\moduletest

£> 0..$($values.sNames.Length-1) | foreach {New-Object -TypeName psobject -Property @{'Name'=$values.sNames[$psitem]; 'Type'=$values.Types[$psitem]}} | ft -a

Name               Type

----               ----

dvalue            Dword

qvalue            Qword

svalue           String

mstring     MultiString

SZvalue ExpandingString

Bvalue           Binary

If you store the output from Get-RegistryValue in a variable, you can access each array individually. Pipe numeric values that match the array indices – 0 to $values.sNames.Length-1 into Foreach-Object. Within the Foreach, use New-Object to create a PSObject that combines the matching name and data type. Output and use Format-Table to display, if you prefer.

By the way, don’t go looking for a Software\ModuleTest key in the HKLM hive on your system. It’s something I created for demonstration purposes.

Working with the integer data types, DWORD and QWORD, is similar to working with strings. The only change is that the output (or value input) property becomes a System.UInt32 or System.UInt64 respectively, and the name changes to uValue.

The MultiString data type holds an array of strings rather than a single string. The parameters working with the data are defined as an array, for instance:

            <Parameter ParameterName="sValue">

              <Type PSType="System.String[]"  />

              <CmdletOutputMetadata />

            </Parameter>

The remaining CDXML code is very similar to that for dealing with single string values. You can use the standard Windows PowerShell array handling techniques to work with the data.

To read the data:

$data = Get-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring  | select -ExpandProperty svalue

To remove a value from the array and write back:

$data = $data | where {$psitem -ne 'Value2'}

Set-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring  -Value $data

Get-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring

To add another value into the array and write back:

$data += 'Value6'

Set-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring  -Value $data

Get-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring

To reset the whole array:

$data = 'Value1', 'Value2', 'Value3'

Set-RegistryMultiSTRING -Hive HKLM -SubKey SOFTWARE\ModuleTest -ValueName mstring  -Value $data

Environment variables are some of those background pieces of functionality that no one really notices until they go wrong. They can be used in the registry. The data type is known as an expanding string (sounds like something from astrophysics), and you can use the cmdlets from your registry module like this:

Get-RegistryExpandSTRING -Hive HKLM -SubKey software\moduletest -ValueName SZvalue

Set-RegistryExpandSTRING -Hive HKLM -SubKey software\moduletest -ValueName SZvalue -Value '%TEMP%\mynewfolder'

Reading is just like reading a simple string value, but the result looks like this:

sValue

------

C:\windows\TEMP\myfolder

Notice that the result is automatically expanded for you. If you want to modify or create a registry value by using an environment variable, you code it like this:

'%TEMP%\mynewfolder'

The registry stores the data in that format until it’s accessed, at which point, the accessing routine should perform the expansion.

The last registry data type is the Binary type. I don’t like modifying these because bad things can happen if you get it wrong. But if you have to make changes, the safest way is to use the Set-RegistryBinary cmdlet from your module.  

CIM treats the binary data as an array of unsigned 8-bit integers. You need to convert this to a byte array for Windows PowerShell (.NET). The output argument when working with the GetBinaryValue method is coded like this:

            </Parameter>

            <Parameter ParameterName="uValue">

              <Type PSType="System.Byte[]"  />

              <CmdletOutputMetadata />

            </Parameter>

Reading data from binary registry values can be accomplished like this:

£> $values = Get-RegistryBinary -Hive HKLM -SubKey software\moduletest -ValueName Bvalue  | select -ExpandProperty uvalu

e

£> $values

0

1

2

3

4

5

6

12

Modifying binary data can be accomplished by changing the array and writing back:

£> $values += [byte]14

£> Set-RegistryBinary -Hive HKLM -SubKey software\moduletest -ValueName Bvalue -Value $values

0

A return code of 0 indicates success.

Using CDXML isn’t a technique for everyone. One issue is that there aren’t any good tools to make it easier to work with. The XML is relatively simple, but you need to be careful and precise in your coding. There are many modules in the later versions of Windows that are created by using CDXML.

You can use the technique to create similar modules on earlier versions of Windows by using existing CIM classes. For instance, the Win32_NetworkAdapter and Win32_NetworkAdapterConfiguration classes could be used to duplicate the NetAdapter and NetTCPIP modules respectively. If you have those modules installed on your down-level systems, and you use the same cmdlet names, you could manage all of your systems from what appears to be the same set of cmdlets.

This concludes my series about registry cmdlets. If you are interested in learning more about CDXML, my talk on this topic is available. Please see the PowerShell Summit North America 2014 site on YouTube. To download the slides, demo scripts, and code for each stage of module development (including the final version of the module), see my Dropbox site.

Bye for now.

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 atscripter@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 PowerShell to Install Modules on Your System

$
0
0

Summary: Learn how to use Windows PowerShell to install modules on your system.

Hey, Scripting Guy! Question How can I use Windows PowerShell to roll out a new set of modules that are used extensively in my organization?

Hey, Scripting Guy! Answer When Windows PowerShell 5.0 is available, you can use PowerShellGet against an internal repository
           to install your modules.

  For systems running Windows PowerShell 4.0 or earlier, create a central repository of modules on a network share
  and use a scheduled task to check for updated modules. Then copy any new or updated modules to your system.

Viewing all 2129 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>