Quantcast
Channel: Hey, Scripting Guy! Blog
Viewing all 3333 articles
Browse latest View live

Use PowerShell to Identify Your Real Network Adapter

$
0
0

Summary: Learn how to use Windows PowerShell to identify easily the real network adapter.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have a problem that perhaps you can assist with. I know about WMI, and I know there is a class that represents a network adapter. The problem is that when I run the command, I get back lots of stuff that is not a real network adapter. I am sure I am not the first person to see this problem, so I want to know how other people solve this problem.

—BP

 

Hey, Scripting Guy! AnswerHello BP,

Microsoft Scripting Guy Ed Wilson here. This is one of the problems with technology—it is always changing. In the old days, it was relatively simple to use WMI to work with network adapters. All one needed to do was to choose the adapter that had the ipenabled property set to true. An example of a script that uses this technique is in the How Can I Change the IP Address Assigned to a Computer? blog post.

On my laptop today, that approach still works. The command to find a network adapter that is IP enabled is shown here along with the associated output:

PS C:\> Get-WmiObject win32_networkadapterconfiguration -Filter 'ipenabled = "true"'

 

DHCPEnabled      : True

IPAddress        : {198.134.88.47, fe80::fd99:3ef6:799f:dc0b}

DefaultIPGateway : {198.134.88.1}

DNSDomain        : portseattle.org

ServiceName      : netw5v64

Description      : Intel(R) Wireless WiFi Link 4965AGN

Index            : 13

Cool. However, there is a problem. This command retrieves the configuration of the network adapter; it does not get the network adapter itself. For example, if I want to change the IP address or the DNS server address, I use the Win32_networkadapterconfiguration class. If I want to enable or disable a network card, I use the win32_networkadapter class. The bad thing is that the Win32_Networkadapter class does not have an ipenabled property. When I attempt to use such a command, an error is displayed such as the one shown here:

PS C:\> Get-WmiObject win32_networkadapter -Filter 'ipenabled = "true"'

 

Get-WmiObject : Invalid query

At line:1 char:14

+ Get-WmiObject <<<<  win32_networkadapter -Filter 'ipenabled = "true"'

    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], ManagementException

    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObject Command

The good thing is that both the Win32_networkadapterconfiguration class and the win32_NetworkAdapter WMI class both share the index property. This means I can use the result of one query to feed into a query for the other class. In the following example, I find the network adapter that is ipenabled, get the index number of that adapter, and return the network adapter itself:

PS C:\> $a = Get-WmiObject win32_networkadapterconfiguration -Filter 'ipenabled = "true"'

PS C:\> $a.Index

13

PS C:\> Get-WmiObject win32_networkadapter -Filter 'index = 13'

ServiceName      : netw5v64

MACAddress       : 00:1F:3B:AD:FF:6D

AdapterType      : Ethernet 802.3

DeviceID         : 13

Name             : Intel(R) Wireless WiFi Link 4965AGN

NetworkAddresses :

Speed            : 36000000

Another way to find a specific network adapter is to look at the maker of the adapter or the description of the adapter. For example, on my laptop the same company makes both the wired Ethernet connection and the wireless network adapter. You cannot rely upon using only the network adapter that is connected, because more than one network adapter could be connected at the same time. Fortunately, both win32_networkadapter and win32_networkadapterconfiguration WMI classes contain a description property. On my laptop, the two outputs are identical:

PS C:\> Get-WmiObject win32_networkadapterconfiguration -Filter 'index = 13' | select description

 

description

Intel(R) Wireless WiFi Link 4965AGN

 

PS C:\> gwmi win32_networkadapter -filter 'index = 13' | select description

 

description

Intel(R) Wireless WiFi Link 4965AGN

 

One of the tricks I use so that I can always find the correct network adapter is assign specific names to the network adapters. Using WMI and the Win32_networkadapter class, I can create a new name for the netconnectionID property. This technique is shown here. Note   This command requires administrator rights.

$adapter = Gwmi win32_networkadapter -Filter 'index = 13'

$adapter.NetConnectionID = "ScriptingGuys"

$adapter.Put()

The commands and their associated output are shown in the following figure.

Image of commands and associated output

The following figure shows that the command was successful.

Image showing command was successful

After I have an easy-to-use network adapter name, I can use it in queries directly:

PS C:\> gwmi win32_networkadapter -Filter 'netconnectionid = "scriptingguys"'

 

ServiceName      : netw5v64

MACAddress       : 00:1F:3B:AD:FF:6D

AdapterType      : Ethernet 802.3

DeviceID         : 13

Name             : Intel(R) Wireless WiFi Link 4965AGN

NetworkAddresses :

Speed            : 48000000

I have always made it a practice to name my network adapters. In the past, it required a pretty extensive VBScript to accomplish, or it required manual intervention (or it required using netsh). Now, a very short Windows PowerShell command accomplishes the task.

Well, BP, that is all there is to working with the network adapter, index names, and NetConnectionIDs. I invite you to join me tomorrow for more Windows PowerShell cool tricks.

 

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

 

 


Easily Compare Two Folders by Using PowerShell

$
0
0

Summary: Microsoft Scripting Guy Ed Wilson illustrates how to compare two folders by using Windows PowerShell.

 

Microsoft Scripting Guy Ed Wilson here. It is an absolutely beautiful day in Charlotte, North Carolina. The early morning rain gave way to a colorful rainbow.

Photo of a rainbow

My manager bought me a new laptop, and I have been busy working on it; installing software, copying files, and migrating settings. The old laptop will be paved, so it is important to ensure I get everything copied to the new laptop.

Most of the major things are automatically transferred, but there are always a few folders that seem to get left behind. This is especially true because I am still working while doing the migration, and there is always a danger of missing something.

In the past, I might write a script to compare two folders to ensure they are identical. However, with Windows PowerShell, I do not need to write a script. I can type a simple command to compare two folders.

For example, I use a folder named fso that is located directly off of the root of the C: drive as my scratch directory. I leave all kinds of stuff in that directory, including files that contains important sample scripts and sample text files, spreadsheets, and databases. I use these files when writing articles, teaching, or making presentations. This folder is shown in the following figure.

Image of Ed's fso folder

There is nothing vital in the folder, but it is useful to have those files, so I want to ensure I have a good copy of the folder. Also, I am not capable of quickly reading through a folder with 144 files to ensure nothing is missing. To compare two folders I perform the following steps:

  1. Use the Get-ChildItem cmdlet with the recurse switched parameter and the path parameter (points to the folder to use for reference) to obtain a collection of fileinfo objects. Store these objects in a variable.
  2. Use the Get-ChildItem cmdlet with the recurse switched parameter and the path parameter (points to the folder to use for comparison) to obtain a collection of fileinfo objects. Store these objects in a different variable.
  3. Use the Compare-Object cmdlet and specify the objects stored in the first variable to the ReferenceObject parameter. Supply the objects stored in the second variable to the DifferenceObject parameter.

Note   Do not get hung up on whether the first folder should be the reference object or the difference object. The position of the folder in the two parameters determines the direction of the comparison arrows, but as long as you know which folder is difference or reference, you will be fine.

The code I type on my laptop is shown here:

$fso = Get-ChildItem -Recurse -path C:\fso

$fsoBU = Get-ChildItem -Recurse -path C:\fso_BackUp

Compare-Object -ReferenceObject $fso -DifferenceObject $fsoBU

The code and associated output are shown in the following figure. The output tells me that inputobject (this is the difference object parameter) is missing three files: a.txt, b.txt, and c.txt. I need to copy these three files to the c:\fso_backup folder.

Image of code and associated output

Well, it is the weekend, and as you can see, it is a beautiful day. I am going to get back to work on my laptop. Hope you have an awesome weekend. See you tomorrow.

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

 

 

Use a PowerShell Cmdlet to Count Files, Words, and Lines

$
0
0

Summary: Learn how to use a powerful Windows PowerShell cmdlet to count words and lines in files, or to count files.

 

Microsoft Scripting Guy Ed Wilson here. The weekend is halfway over in Charlotte, North Carolina. For my friends in Australia, the weekend is already over, and they are on their way to work. Of course they get to start their weekend earlier than I do. The ideal thing to do is to be in Australia to start the weekend, and then pop back to Charlotte to conclude the weekend. Yes, I have strange thoughts on the weekend. For example, I was on the treadmill earlier, and I was thinking about my favorite Windows PowerShell cmdlet. Anyway, I called the Scripting Wife while I was cooling down. She was downstairs and it is easier to call her than to go down there. Cell phones make great intercoms.

“What are you doing?” she asked as she answered her Windows 7 phone.

“I just finished running on the treadmill, and I am now cooling down. I was wondering what your favorite Windows PowerShell cmdlet is.”

“You have got to be kidding. Why would I have a favorite Windows PowerShell cmdlet?” she asked.

“Well, I was thinking about my favorite Windows PowerShell cmdlet while I was running, and I realized I did not know what yours was,” I said.

“Get-Real,” she said as she hung up.

At times, I think that the Scripting Wife seems to believe I am a nerd. I am not positive of this and am somewhat afraid to ask, but she seems to give off the “nerd alert” vibe when I enter a room or when I call her on her cell phone from upstairs and ask her about her favorite Windows PowerShell cmdlet.

Anyway, I will share my favorite cmdlet—it is the Measure-Object cmdlet. If I did not have the Measure-Object cmdlet, I would need to count the files in a folder manually. This is shown here:

$i=0

Get-ChildItem -Path c:\fso -Recurse -Force |

foreach-object { $i++ }

$i

 

Using the Measure-Object cmdlet, it is easy to count the files. I merely need to use the following steps.

  1. Use the Get-Childitem cmdlet to return a listing of fileinfo objects. Use the recurse switch to cause the cmdlet to work through subfolders. The force switch is used to return any hidden or system files. Pass the path to count to the path parameter.
  2. Pipe the fileinfo objects from step one to the Measure-Object cmdlet

An example of using this command to count the files in the c:\fso folder is shown here:

Get-ChildItem -Recurse -force | Measure-Object

The command and associated output are shown in the following figure. Note that I ran the command twice: the first time without the force switched parameter, and the second time using it.

Image of command and associated output

But the Measure-Object cmdlet does more than just count the number of files in a folder. It can also tell me information about a text file. A sample file is shown in the following figure.

Image of sample file

If I want to know how many lines are contained in the file, I use the Measure-Object cmdlet with the line switch. This command is shown here:

Get-Content C:\fso\a.txt | Measure-Object –Line

If I need to know the number of characters, I use the character switch:

Get-Content C:\fso\a.txt | Measure-Object -Character

There is also a words switched parameter that will return the number of words in the text file. It is used similarly to the character or line switched parameter. The command is shown here:

Get-Content C:\fso\a.txt | Measure-Object –Word

In the following figure, I use the Measure-Object cmdlet to count lines; then lines and characters; and finally lines, characters, and words. These commands illustrate combining the switches to return specific information.

Image of using Measure-Object to count

One really cool thing I can do with the Measure-Object cmdlet is to measure specific properties of the piped objects. For example, I can use the Get-ChildItem cmdlet to return fileinfo objects for all the text files in the folder. I can examine the length property and find out the minimum length of the files in the folder, the maximum length, the average size, and the total length of all files in the folder. This command and associated output are shown here:

PS C:\fso> Get-ChildItem -Filter *.txt | Measure-Object -Property length -Maximum -Minimum -Average -Sum

 

Count    : 66

Average  : 305903.833333333

Sum      : 20189653

Maximum  : 12534760

Minimum  : 0

Property : Length

 

If I want to, I can pipe the output to a table and create my own custom headings and output. In the following example, I display the average size of the files in kilobytes. I also define the format to omit decimal places:

PS C:\fso> Get-ChildItem -Filter *.txt | Measure-Object -Property length -Maximum -Minimum -Average -Sum | ft count, @{"Label"="average size(KB)";"Expression"={($_.average/1KB).tostring(0)}}

 

                                                      Count                      Average size(KB)

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

                                                         66                        299

 

Well, that is about all there to say for now. The Measure-Object cmdlet is one of my favorite cmdlets because it is easy to use and extremely flexible—an unbeatable combination in my book. What is your favorite cmdlet? Add a comment below and let me know. Until tomorrow, see ya.

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

 

 

Cool PowerShell Game Teaches Cmdlet Names

$
0
0

Summary: Learn Windows PowerShell cmdlet names by taking the Dr. Scripto Challenge.
 
Microsoft Scripting Guy Ed Wilson here. The Scripting Wife and I are on a road trip in Canada. We had a wonderful time in Toronto with Windows PowerShell MVP Sean Kearney. Now we are heading to Ottawa where we plan to meet with Windows PowerShell MVP Kirk Munro. In addition to getting to talk to customers about Windows PowerShell and meeting with way cool Windows PowerShell MVPs, it also means we have access to the television. At home in Charlotte, North Carolina, we do not watch television, so in some ways it is sort of a treat. For example, the Scripting Wife has a couple of shows she likes to watch. As she was watching one particular show in which contestants receive letters and blank spaces and try to match a clue, it dawned on me that I could write that in Windows PowerShell.


So here it is; the Dr. Scripto Windows PowerShell Challenge. The script is interesting, but it is also illustrative of an interesting way to manipulate strings. The techniques could possibly be applicable in a wide variety of situations.


DrScriptoChallenge.ps1
$array = @()
$array = Get-Command -CommandType cmdlet |
ForEach-Object { $_.name.tostring() }
Foreach($cmdlet in $array)
{
 $rndChar = get-random -InputObject ($cmdlet.tochararray()) -count ($cmdlet.length/4)
 foreach($l in $rndchar)
 {
  $cmdlet = $cmdlet.Replace($l,"_")
 }# end foreach $l
 $cmdlet
} #end foreach $cmdlet

The first thing I do in the DrScriptoChallenge.ps1 script is create an empty array and store it in the $array variable. Next, I use the Get-Command cmdlet to retrieve all of the cmdlets, and I pipe the cmdletinfo objects to the Foreach-Object cmdlet where I obtain the name of each cmdlet as a string. I then store the resultant cmdlet names in the $array variable. All of this provides me with an array of Windows PowerShell cmdlet names. This portion of the code is shown here:


$array = @()
$array = Get-Command -CommandType cmdlet |
ForEach-Object { $_.name.tostring() }

The next thing I want to do is to choose a random grouping of letters from each Windows PowerShell cmdlet. To do this, I turn the Windows PowerShell cmdlet names that are stored in the $array variable into an array of characters. This allows me to choose individual letters from the Windows PowerShell cmdlet names. I pass the cmdlet names to the Get-Random cmdlet to choose the random letters. One thing I do is look at the length of the Windows PowerShell cmdlet name, and if a cmdlet name is longer, I select additional random letters. To make the challenge harder, use a 2 or a 3 instead of a 4. To make it easier, use a 5 or a 6 instead of a 4. I store the resulting random letters in an array called $rndChar. This section of the Windows PowerShell script is shown here:


$rndChar = get-random -InputObject ($cmdlet.tochararray()) -count ($cmdlet.length/4)

Now I want to replace each of these random letters in the Windows PowerShell cmdlet names with an underscore character. I use a Foreach loop to walk through the array of letters, and the replace method to replace each letter with the underscore character. A key point here is to write the replacement string back to the original string, or else when the script completes, only the last letter will be replaced. This portion of the script is shown here:


foreach($l in $rndchar)
 {
  $cmdlet = $cmdlet.Replace($l,"_")
 }# end foreach $l

The last thing to do is to display the modified string and close out the loop:


$cmdlet
} #end foreach $cmdlet

Because there are several Foreach type of loops, I add a comment at the closing brace (curly bracket) to let me know the purpose of the brace. This makes troubleshooting easier. As the script currently stands, it simply displays the modified cmdlet names on the screen; to create a test simply redirect it to a text file as seen here:


$cmdlet >>c:\fso\DrScriptoChallenge.txt

The script and associated output displayed on the screen are shown in the following figure.

Image of script and associated output

 

Well that is all there is to creating the DrScriptoChallenge. You might think the script is easier than the challenge—it is a great way to test your knowledge of the default Windows PowerShell cmdlets. Until tomorrow, keep on scripting.


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

 

Use PowerShell Parameter Attributes to Avoid Errors

$
0
0

Summary: Use Windows PowerShell parameter attributes to avoid input errors in a function.

 

Microsoft Scripting Guy Ed Wilson here. This week is great. The Scripting Wife and I are enjoying meeting Windows PowerShell luminaries. I am working with a group of IT pros to help them to learn Windows PowerShell, and the weather has been wonderful. I hope to get some  ime to head out to a really neat hardware store and gaze longingly at the hand planes.

Anyway, one of the students, who reads the Hey, Scripting Guy! Blog on a daily basis, commented that he thought that the script in yesterday’s Cool PowerShell Game Teaches Cmdlet Names post could stand a bit of improvement. I definitely agree.

In today’s article, I am going to modify the script from yesterday and turn it into a function. In addition, I am going to add parameter validation. For example, in yesterday’s script it would be possible to get a divide-by-zero error if you ran the script with the wrong number for the  ount. In addition, it is possible to receive an output of a continuous solid line if you were to change the input value to a one. To make it easy to use the code and to modify the difficulty of the puzzle, I will include a command-line parameter to allow for quickly changing the difficulty. The complete New-CmdletPuzzle function is shown here.

function New-CmdletPuzzle

{

 Param(

  [Parameter(Position=0,

             HelpMessage="A number between 2 and 7")]

  [alias("Level")]

  [ValidateRange(2,7)]

  [int]$difficulty = 4

 ) #end param

 $array = @()

 $array = Get-Command -CommandType cmdlet |

 ForEach-Object { $_.name.tostring() }

 

 Foreach($cmdlet in $array)

 {

  $rndChar = get-random -InputObject ($cmdlet.tochararray()) `

           -count ($cmdlet.length/$difficulty)

  foreach($l in $rndchar)

  {

   $cmdlet = $cmdlet.Replace($l,"_")

  }# end foreach l

  $cmdlet

 } #end foreach cmdlet

}
#end function New-CmdletPuzzle

 

The two changes I made to yesterday’s script convert the inline script to a function and add a command-line parameter. To convert the script to a function, all I needed to do was use the function keyword, provide a function name, and place the code in a pair of braces (curly  rackets).

The param keyword tells the function to expect a value from the command line. I use the parameter attribute to create a parameter in position 0 and assign a helpmessage value as well. Here is the first part of the param statement:

Param(

  [Parameter(Position=0,

             HelpMessage="A number between 2 and 7")]

In addition, I decided to create an alias for the input parameter. To do this I use the alias attribute:

[alias("Level")]

The most important attribute for my New-CmdletPuzzle is the validaterange attribute. It limits the permissible values for the function. For this function, I do not want anyone to use a level less than 2 or greater than 7. To do this, I specify my minimum and maximum range as  rguments to the attribute. Here is validaterange attribute.

[ValidateRange(2,7)]

If I supply a value to the function that is outside the allowed range specified by the validaterange attribute, a nonterminating error occurs. A sample error is shown here:

Image of sample error

The last thing I do is specify a default value for the difficulty parameter. In addition, I use a type constraint to ensure the value is an integer. This line of code is shown here:

[int]$difficulty = 4

The entire parameter section is shown here:

Param(

  [Parameter(Position=0,

             HelpMessage="A number between 2 and 7")]

  [alias("Level")]

  [ValidateRange(2,7)]

  [int]$difficulty = 4

 ) #end param

That is it. When I run the function with no input parameters, it runs with a default value of 4. If I made the parameter mandatory, the function would ignore the default value and prompt. But when I have a default value assigned, it is always used unless it is overridden, so the  arameter is essentially mandatory because a value is always used for the parameter. I can use the level alias, and pipe the output as I would a regular Windows PowerShell cmdlet. The command and associated output for this are shown in the following image.

Image of command and associated output

The file created by the previous command is shown in the following image.

Image of file created by command

Well, that is enough playing around for one day. Join me tomorrow as I continue to explore string manipulation, and as I continue, my progression from script to function to…see you tomorrow. Until then, have a great day.

 

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

 

 

Create a PowerShell Quiz Script

$
0
0

Summary: Learn how to use hash tables and create a Windows PowerShell quiz script.

 

Microsoft Scripting Guy Ed Wilson here. For the past several days, I have been building a Windows PowerShell cmdlet name quiz. On the first day, I looked at replacing random letters in a string. Next I moved the code into a function and  dded parameter validation to limit the values that can be supplied to the function. In this way I was able to prevent a divide-by-zero error that could arise depending on what someone supplied from the command line. This is actually a great way to do error handling—prevent the error from arising in the first place.  Today, I am going to add the question and answer feature for the Windows PowerShell cmdlet name game.

The complete New-CmdletPuzzleQuiz.ps1 script is shown here.

function New-CmdletPuzzle

{

 Param(

  [Parameter(Position=0,

             HelpMessage="A number between 2 and 7")]

  [alias("Level")]

  [ValidateRange(2,7)]

  [int]$difficulty = 4

 ) #end param

 $array = @()

 $hash = New-Object hashtable

 $array = Get-Command -CommandType cmdlet |

 ForEach-Object { $_.name.tostring() }

 

 Foreach($cmdlet in $array)

 {

  $rndChar = get-random -InputObject ($cmdlet.tochararray()) `

           -count ($cmdlet.length/$difficulty)

  $cmdletP = $cmdlet #moved from inside foreach loop

  foreach($l in $rndchar)

  {

   #moved code to outside Foreach loop

   $cmdletP = $cmdletP.Replace($l,"_")

  }# end foreach l

  $hash.add($cmdlet,$cmdletP)

 } #end foreach cmdlet

 $hash

}
#end function New-CmdletPuzzle

 

Function New-Question

{

 Param(

  [hashtable]$Puzzle

 )

  Foreach ($p in $puzzle.KEYS)

   {

    $rtn = Read-host "What is the cmdlet name $($puzzle.item($P))"

    If($puzzle.contains($rtn))

     { "Correct $($puzzle.item($P)) equals $p" }

    ELSE

     {"Sorry. $rtn is not right. $($puzzle.item($P)) is $p" }

    } #end foreach $P

}
#end function New-Question

 

#
*** Entry point to script ***

 

$puzzle = New-CmdletPuzzle

New-Question -puzzle $puzzle

 

The first thing I do in the New-CmdletPuzzleQuiz.ps1 script is use the New-CmdletPuzzle function from yesterday’s script. That function creates a hash table with cmdlet names as the key value and cmdlet names with missing letters as the value. It then returns the hash table to  he calling code.

The new function I wrote for today is the New-Question function. It appears here.

Function New-Question

{

 Param(

  [hashtable]$Puzzle

 )

  Foreach ($p in $puzzle.KEYS)

   {

    $rtn = Read-host "What is the cmdlet name $($puzzle.item($P))"

    If($puzzle.contains($rtn))

     { "Correct $($puzzle.item($P)) equals $p" }

    ELSE

     {"Sorry. $rtn is not right. $($puzzle.item($P)) is $p" }

    } #end foreach $P

}
#end function New-Question

 

The input to the New-Question function is the hash table returned by the New-CmdletPuzzle function, but any question/answer type of hash table would actually work. For example, in the following script, QuestionsAndAnswere.ps1, I add a function that creates a hash table of  questions about capitals and their associated countries. The only change I needed to make to the New-Question function was to remove the phrase, “What is the cmdlet name” from the Read-Host command

This actually points to a major design issue—hard coded literals often cause code reuse issues. In this example, if I had a variable to hold the prompt string, and I passed the string when calling the function, it would be easier to reuse the function. The two evaluation strings (“Correct…equals...” and “Sorry…Is not right…is…”) are pretty generic and make sense (albeit a bit stilted) in most cases. A better approach there would be to use custom correct and incorrect strings, and pass them when calling the function. The more abstract a function becomes, the greater the reuse capabilities. The complete QuestionsAndAnswers.ps1 script is shown here.

QuestionsAndAnswers.ps1

Function New-Puzzle

{

 @{

   "What is the capital of Australia" = "Canberra"

   "What is the capital of Canada" = "Ottawa"

   "What is the capital of Germany" = "Berlin"

   }

 

}

Function New-Question

{

 Param(

  [hashtable]$Puzzle

 )

  Foreach ($p in $puzzle.KEYS)

   {

    $rtn = Read-host "$($puzzle.item($P))"

    If($puzzle.contains($rtn))

     { "Correct $($puzzle.item($P)) equals $p" }

    ELSE

     {"Sorry. $rtn is not right. $($puzzle.item($P)) is $p" }

    } #end foreach $P

}
#end function New-Question

 

#
*** Entry point to script ***

 

$puzzle = New-Puzzle

New-Question -puzzle $puzzle

 

In the New-Question function, I use a [hashtable] type constraint to ensure the input parameter is a hash table. This code is shown here:

Function New-Question

{

 Param(

  [hashtable]$Puzzle

 )

When I have the input hash table, I use the foreach language statement to walk through the collection of hash table keys. I obtain the hash table keys by using the keys property from the hashtable object. I use the variable $p to represent a single key (the enumerator) in the collection as I work my way through the collection. This portion of the foreach loop is shown here:

Foreach ($p in $puzzle.KEYS)

   {

I needed a way to receive input from the user of the script, and I decided that using the Read-Host cmdlet was the easiest for this application. The user types the answer to the question, and I store the answer in the $rtn variable. The Read-Host cmdlet creates an input box when run from the Windows PowerShell ISE. This box is shown in the following figure.

Image of created input box

When the script runs from the Windows PowerShell console, the Read-Host cmdlet generates a command-line prompt. This prompt is shown in the following figure.

Image of created command-line prompt

To display the cmdlet name with the random letters removed, I use the actual cmdlet name I received from the collection of keys. The variable $p contains the actual cmdlet name. When working with a hash table, the item method uses a key to retrieve the data stored in the value that is associated with the key. This concept is illustrated in the following figure.

Image of illustration of concept

This line of code is shown here:

$rtn = Read-host "What is the cmdlet name $($puzzle.item($P))"

 

When I have the user input, it is time to see if the input matches the actual cmdlet name. I do this by using the contains method from the hashtable object. If the input matches, I display a line that states the user is correct; if it does not match, the else condition matches. The contains operator here is case sensitive. Therefore, New-Object does not match New-Object. The if portion of the script is shown here:

  If($puzzle.contains($rtn))

     { "Correct $($puzzle.item($P)) equals $p" }

    ELSE

     {"Sorry. $rtn is not right. $($puzzle.item($P)) is $p" }

    } #end foreach $P

}
#end function New-Question

 

The entry point to the script is pretty simple. I call the New-CmdletPuzzle function and store the returned hash table in the $puzzle variable. I then pass the $puzzle variable containing the hash table to the New-Question function.

 

That’s it for today. Join me tomorrow when I will add the capability to determine the number of questions that make up a quiz, and return a score for the quiz.

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

 

 

 

Add Random Question Features to a PowerShell Game

$
0
0


Summary: Learn how to add random question features to a Windows PowerShell script game.

Microsoft Scripting Guy Ed Wilson here. Well, the Scripting Wife and I have really enjoyed Ottawa. The class I am teaching on Windows PowerShell has absolutely rocked! Ottawa is one of my favorite cities (there is a really cool woodworking store here), and I have been able to spend a decent amount of time with my friend George. All in all, it has seemed like vacation more than work. Next week, we are in Montreal.

Anyway, I have continued playing with my Windows PowerShell quiz script, and today I want to share my latest iteration. It provides the ability to choose a specific number of questions. The questions themselves are selected in a random manner. The script will also grade performance and provide the number of right and wrong answers, and the percentage of correct answers as feedback.

Note This is the fourth part of a multipart series of articles about writing a Windows PowerShell quiz script. On the first day, I looked at replacing random letters in a string. Next, I moved the code into a function and added parameter validation to limit the values that can be supplied to the function. In this way I was able to prevent a divide by zero error that could arise depending on what someone supplied from the command line. This is actually a great way to do error handling – prevent the error from arising in the first place. Yesterday I added the question and answer feature for the Windows PowerShell cmdlet name game. Today I am adding the ability to choose a specific number of questions, as well as a grade feature.

The complete New-CmdletPuzzleCountQuestions.ps1 script is shown here.

function New-CmdletPuzzle

{

 Param(

  [Parameter(Position=0,

             HelpMessage="A number between 2 and 7")]

  [alias("Level")]

  [ValidateRange(2,7)]

  [int]$difficulty = 4

 ) #end param

 $array = @()

 $hash = New-Object hashtable

 $array = Get-Command -CommandType cmdlet |

 ForEach-Object { $_.name.tostring() }

 

 Foreach($cmdlet in $array)

 {

  $rndChar = get-random -InputObject ($cmdlet.tochararray()) `

           -count ($cmdlet.length/$difficulty)

  foreach($l in $rndchar)

  {

   $cmdletP = $cmdlet

   $cmdletP = $cmdletP.Replace($l,"_")

  }# end foreach l

  $hash.add($cmdlet,$cmdletP)

 } #end foreach cmdlet

 $hash

} #end function New-CmdletPuzzle

 

Function New-Question

{

 Param(

  [hashtable]$Puzzle,

  [int]$num = 10

 )

  $quiz = @{}

  $right = $wrong = 0

  Get-Random -InputObject $($puzzle.keys) -Count $num |

   ForEach-Object { $quiz.add($_,$puzzle.item($_)) }

   Foreach ($p in $quiz.KEYS)

    {

     $rtn = Read-host "What is the cmdlet name $($quiz.item($P))"

     If($quiz.contains($rtn))

      {

       "Correct $($quiz.item($P)) equals $p"

       $right ++

       }

     ELSE

      {

       "Sorry. $rtn is not right. $($quiz.item($P)) is $p"

       $wrong ++

       }

     } #end foreach $P

     New-Object PSObject -Property @{

       "right" = $right

       "Wrong" = $wrong

       "Percent" = "{0:N2}" -f ($right/$num*100)  }

    

} #end function New-Question

 

# *** Entry point to script ***

 

$puzzle = New-CmdletPuzzle -level 5

New-Question -puzzle $puzzle -num 5

I did not make any changes to the New-CmdletPuzzle function, so there is no need to discuss it today. I did make several changes to the New-Question function, and that is what I will discuss today. The first change I made was to add an addition parameter to allow the user to determine the number of questions to receive. I set the default value to 10, which seems like a reasonable number (it also made it easy to check the accuracy of the percentage right feature). This portion of the script is shown here:

Function New-Question

{

Param(

  [hashtable]$Puzzle,

  [int]$num = 10

)

Next I create an empty hash table that I store in the $quiz variable. This hash table creates the quiz after the specific number of random questions is chosen. I then initialize the $right and $wrong variables to the number zero. At first I used $null, but when all the questions were right or all wrong, it was not obvious because the output would be blank. The number zero makes it obvious that a field is empty. This portion of the function is shown here:

  $quiz = @{}

  $right = $wrong = 0

The hardest part of the revised function is the code that accepts the original puzzle containing all cmdlet names and cmdlet names with obscured string. The Get-Random cmdlet will not select key value pairs from a hash table. So what I had to do was get a collection of keys, and allow the Get-Random cmdlet to choose a specific number of keys from the hash table. I pipe the keys to a Foreach-object cmdlet where I use the key to retrieve the associated value from the hash table. I then add both the key and the value to the $quiz hash table. This provides me with a hash table that contains the newly selected cmdlet and obscured cmdlet names. This portion of the script is shown here:

  Get-Random -InputObject $($puzzle.keys) -Count $num |

   ForEach-Object { $quiz.add($_,$puzzle.item($_)) }

Because I created a new hash table to hold the newly selected questions and answers, I had to change the name of the variables used in the Foreach loop. This is also where I added the use of $right counter variable. This portion of the script is shown here:

Foreach ($p in $quiz.KEYS)

    {

     $rtn = Read-host "What is the cmdlet name $($quiz.item($P))"

     If($quiz.contains($rtn))

      {

       "Correct $($quiz.item($P)) equals $p"

       $right ++

      }

The same type of changes are made to the else portion of the Foreach loop. This portion of the script is shown here:

ELSE

      {

       "Sorry. $rtn is not right. $($quiz.item($P)) is $p"

       $wrong ++

       }

     } #end foreach $P

After all of the questions are answered, it is time to evaluate the performance. I use a custom psobject to do this. I add the right, wrong, and percent properties to it. Here is the code for the New-Object.

New-Object PSObject -Property @{

       "right" = $right

       "Wrong" = $wrong

       "Percent" = "{0:N2}" -f ($right/$num*100)  }

    

} #end function New-Question

The entry point to the script does not really need to change. I decided to use the num parameter to change the number of questions asked from the default of 10 to 5. Here is the entry point:

$puzzle = New-CmdletPuzzle -level 5

New-Question -puzzle $puzzle -num 5

When the script runs, each question is evaluated as it is answered. After complete, the performance is displayed. This is illustrated in the following figure.

Image of quiz performance

 

That is about all there is for now. I think See you tomorrow when I will bring this series to an exciting conclusion. I have a bit of cleanup I am wanting to do, and I also want to add a couple more features.

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

 

Create a PowerShell Quiz by Reading a Text File

$
0
0

Summary: Learn how to create a Windows PowerShell hash table from a text file.

 

Microsoft Scripting Guy Ed Wilson here. Our Canadian trek continues. We got to meet with Microsoft MVP Sean Kearney and his wife Rose, and we spent the day roaming around Toronto. He even took me to the store where he bought his trademark BATCHman hat. Along the way, the Scripting Wife and Sean were attacked by a killer moose. As shown in the following photo, Sean struggled valiantly to protect the Scripting Wife (no moose were actually used for this photo, and no one was harmed in the taking of this picture).

Photo of Sean Kearney, the Scripting Wife, and...MOOSE!

After Sean explained his intentions to the moose, we were all on the best of terms. Sean even captured the moment as the Scripting Wife and I discussed plans with the moose for use in an upcoming series of Hey Scripting Guy articles. The negotiations were intense, but in the end, we were all friends.

Photo of the Scripting Guy, the Scripting Wife, and...MOOSE!

Following the moose incident, we drove over to Microsoft Windows Expert and IT pro MVP Mitch Garvis’s house where his wife Theresa fixed a sumptuous banquet. Microsoft Windows Azure MVP Cory Fowler stopped by, and soon the conversation turned to travel and Windows PowerShell. Stay tuned because Cory has agreed to write a guest blog article. In between hobnobbing with moose and hanging out with MVPs, I did get some time to work on my Windows PowerShell quiz script. I am anxious to share it with you, so let’s dive right in.

Note   This is the fifth part of a multipart series of articles about writing a Windows PowerShell quiz script. On the first day, I looked at replacing random letters in a string. Next, I moved the code into a function and added parameter validation to limit the values that can be supplied to the function. In this way I was able to prevent a divide by zero error that could arise depending on what someone supplied from the command line. This is actually a great way to do error handling—prevent the error from arising in the first place. Then, I added the question and answer feature for the Windows PowerShell cmdlet name game. And then I added the ability to choose a specific number of questions, as well as a grade feature. Today I am going to add a New-Quiz function, and clean up the code to permit different prompt strings. The one thing I am not going to have time to do is to add comment-based help, but that is really easy to do given my Windows PowerShell ISE Add-Help function.  

Another note   This script is getting kind of long, so I uploaded the Windows PowerShell Quiz script to the Scripting Guys Script Repository. I also attached the questions.txt file so that you would have a good sample of what a question file might look like.

The changes to the New-Question function are extensive, but not particularly complex. I abstracted the prompts from hard-coded text to variables. This allows for a great deal of flexibility. Not only can I use the quiz script to offer different quizzes, but I can even localize the language easily when calling the script. In addition, I wanted to keep the simplicity of the earlier scripts, so I set default values that are equivalent to the earlier scripts. One thing that was a bit tricky was supplying an array to the prompts. I used the @() syntax to create my arrays. Here is the parameter section of the New-Question function:

Function New-Question

{

 Param(

  [hashtable]$Puzzle,

  [int]$num = 10,

  [string]$prompt = "What is the cmdlet name",

  [array]$rightPrompt = @("Correct","equals"),

  [array]$wrongPrompt = @("Sorry.","is not correct","is")

 )

One reason I like to explicitly cast the parameters is that when using comment-based help, the help subsystem is smart enough to pick up the type constraints and use that information in the help output. It also helps to prevent errors. The first prompt is stored in the $prompt variable. It is the text that appears when the script runs. This prompt is shown in the following figure.

Image of prompt

Of course, in this example, the default value does not match up. This is because the value of $prompt is overridden at the entry point to the script where I supply a new prompt value at the prompt parameter. This is shown here:

New-Question -puzzle $puzzle -num 5 -prompt "What is the capital of" `

   -rightPrompt "Correct, the capital of","is" -wrongprompt "Sorry","is not correct. The capital of","is"

 

One thing to keep in mind is the use of line continuation. I hate using line continuation because it is always an extra level of complexity. In this case, the command line would be too long to be easily read on the blog, so I use the line continuation character—the back tick or grave symbol—after the close of the prompt string. If you have a wide screen and a small enough font, this line of code will fit on a single line. Remove the back tick character and remove the spaces between the end of the “What is the capital of” prompt and the rightprompt parameter. 

Both the rightprompt and the wrongprompt parameters accept an array for input. When supplied from the command line, a comma separates the elements of the array. The wrongprompt uses three elements, and the rightprompt uses two elements.

If an answer is correct, the quiz element matches the value supplied from the command line. The first element of the rightprompt array states the value is correct, and the value that is matched is displayed from the quiz hash table. The second element of the prompt is the verb that is appropriate to the question. This portion of the function is shown here:

If($quiz.contains($rtn))

      {

       "$($RightPrompt[0]) $($quiz.item($P)) $($RightPrompt[1]) $p"

       $right ++

       }

If the question is wrong, the wrongprompt array supplies values to complete the string returned to the user. This is shown here:

ELSE

      {

       "$($wrongPrompt[0]) $rtn $($wrongPrompt[1]) $($quiz.item($P)) $($wrongPrompt[2]) $p"

       $wrong ++

       }

     } #end foreach $P

 

In the script I uploaded to the Scripting Guys Script Repository, I included a text file (shown in the following figure) that provides the basis of a quiz on capitals. The capitals are not listed in any particular order. They are simply the ones that came up as I was playing around with Bing looking for capitals.

Image of text file that is basis for capitals quiz

I use the ConvertFrom-StringData cmdlet to create a hash table from a text file. There are several Hey, Scripting Guy! posts about using the ConvertFrom-StringData cmdlet to create a hash table, and I recommend them for background information about using this technique. In fact, I like this topic so much, I am also talking about it in Sunday’s Hey, Scripting Guy! Blog post.

Because of the way that Get-Content cmdlet works, I could not make it do what I needed for the stringdata of the cmdlet. I solved the problem by using the .NET Framework io.file class and calling the static ReadAllText method. This function is shown here:

Function New-Puzzle

{

 Param([string]$path)

  ConvertFrom-StringData -StringData ([io.file]::ReadAllText($path))

} #end function new-puzzle

The entry point to the script is how you control which quiz is offered and which prompts are used. It’s up to you whether you hard-code them as I did here, or you simply type them from the command line when calling the script, but I thought adding the actual command lines here was useful because the syntax of the prompts can take a bit of experimentation until it makes sense. This is something I would not expect people to do on the fly. One way to solve the problem is to comment out a line when it is not used; this is what I did here:

#$puzzle = New-CmdletPuzzle -level 5

$puzzle = New-puzzle -path C:\fso\Questions.txt

New-Question -puzzle $puzzle -num 5 -prompt "What is the capital of" `

   -rightPrompt "Correct, the capital of","is" -wrongprompt "Sorry","is not correct. The capital of","is"

 

That is all there is for today. Join me tomorrow when I will go over more detail about hash tables (our informal topic for the week). Hope to see you then.

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

 

 


Easily Create a PowerShell Hash Table

$
0
0

 Summary: Learn how to automatically populate a hash table in a Windows PowerShell script.

 

Microsoft Scripting Guy Ed Wilson here. A hash table is an important data structure in Windows PowerShell. Many of the cmdlets use hash tables to format their input. For example, if I want to create a custom column header in a table, I have to use a hash table. A hash table consists of one or more key value pairs (of course, it is possible to create an empty hash table that contains no key value pairs, but let’s go with the easy description first).

The ampersand and a pair of braces (curly brackets) identify a hash table. Normally a variable stores the hash table, but it is possible to create a hash table and not store it in a variable. An example of this is shown here:

@{

"key1" = "value1"

"key2" = "value2"

}

In the following figure, I first run the code and display the contents of the hash table. Next, I pipe the results to the Get-Member cmdlet.

Image of contents of hash table

Most of the time, a hash table is stored in a variable for use in other places. It is possible to create a hash table on a single line, but it is difficult to read, and if a problem occurs, it is hard to troubleshoot. The semicolon separates key value pairs and indicates a new line. In the code seen here, I create a hash table on a single line.

$hash = @{"key1" = "value1";"key2" = "value2"}

The same hash table is easier to read when spread out on multiple lines. This technique is shown here:

$hash1 = @{

  "key1" = "value1"

  "key2" = "value2"

 }

Whether the closing brace appears on its own line or after the final key value pair is a matter of stylistic taste. I generally prefer to place it on line because it is easier to spot when troubleshooting. When working with a script editor that automatically matches brace pairs, this advantage disappears, and I then prefer to close up the code.

The real power of hash tables comes by adding key value pairs automatically from within the script. When used in this way, hash tables are essentially temporary data storage. The advantage a hash table has over an array is the key value pairs. The keys provide a way to retrieve the associated value by name; with an array, the value is accessible via the element number. A disadvantage over an array is that with a hash table, the key must be unique; an array permits multiple elements to be the same.

To create a hash table dynamically, follow these steps:

1.       Create an empty hash table.

2.       Store the empty hash table in a variable.

3.       Collect the data.

4.       Store the collected data in a variable.

5.       Use the foreach statement to walk through the collected data.

6.       Inside the loop call the add method to add the key value pairs to the hash table.

An example of this procedure is shown here:

$hash = $null

$hash = @{}

$proc = get-process | Sort-Object -Property name -Unique

 

foreach ($p in $proc)

{

 $hash.add($p.name,$p.id)

}

The first thing I do is assign the value $null to the $hash variable. I do this because when running code multiple times in the Windows PowerShell ISE, the values of global variables continue to be present. If I am not paying attention, the value stored in variables can change with each run of the script. After I have initialized the $hash variable with $null, I create an empty hash table and store it in the $hash variable. These two lines of code are shown here:

$hash = $null

$hash = @{}

Next I use the Get-Process cmdlet to collect information about each process that is running on the computer. I sort these process objects based upon the name property, and I use the unique switched parameter to return only unique instances of the process objects. The reason for doing this is I want to use the name property as the value for the keys in my hash table. The key of a hash table must be unique, and in most cases, there are several duplicate instances of processes running on a computer. For example, the following code reveals there are several processes named svchost.

PS
C:\Users\edwils> gps | ? {$_.name -eq 'svchost'}

 

Handles             NPM(K)             PM(K)               WS(K)               VM(M)              CPU(s)               Id ProcessName

    702               20                     10324               13464               56                     612                   svchost

    443               15                     6328                 11808               53                     936                   svchost

    165               12                     5512                 10580               48                     1036                 svchost

    653               28                     28756               28172               93                     1120                 svchost

    789               31                     16132               26284               119                   1152                 svchost

   2625              127                   97160               88144               438                   1184                 svchost

    798               37                     19064               22760               154                   1292                 svchost

    339               34                     16332               19100               92                     1312                 svchost

    599               29                     9136                 16152               68                     1348                 svchost

    615               28                     11352               15524               59                     1388                 svchost

    105               9                      2900                 6644                 35                     2052                 svchost

     86                8                      2596                 5660                 47                     2332                 svchost

    350               15                     7444                 9340                 47                     4256                 svchost

    524               22                     8832                 11304               56                     4812                 svchost

     50                4                      1520                 3336                 13                     5560                 svchost

When I use the unique switched parameter, I retrieve only one instance of each process with the same name. I do not know which instance I obtain, but for this application, it does not matter. This line of code is shown here:

$proc = get-process | Sort-Object -Property name -Unique

Now it is time to walk through the collection of process objects and add the name and path to the hash table. I use the name of the process for the key and the process ID as the value. The foreach statement is the best command to use to walk through the collection of process objects. I use the add method from the hashtable object that is stored in the $hash variable. The add method requires both the key and the value. This portion of the code is shown here:

foreach ($p in $proc)

{

 $hash.add($p.name,$p.id)

}

When I display the contents of the $hash variable, I am presented with the following output.

Image of contents of $hash variable

 

That is all there is to dynamically creating a hash table.

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

 

Dealing with PowerShell Hash Table Quirks

$
0
0

Summary: Microsoft Scripting Guy Ed Wilson shows how to deal with two Windows PowerShell hash table quirks.

 

Microsoft Scripting Guy Ed Wilson here. Our week in Ottawa draws to a close. We leave for Montreal today, and are really excited about the Windows PowerShell people we will meet while we are there. I enjoyed working on my Windows PowerShell Quiz scripts this week. The point of the articles was not so much about creating a quiz engine, but the fact that it offered a good exercise for working on function design and with hash tables.

Today, I want to focus in on two aspects of hash tables that came up this week while I was writing the scripts. The first aspect I want talk about is piping a hash table to other cmdlets. This also comes into play when supplying a hash table to a cmdlet as an inputobject. As an example, I will use the hash table created in yesterday’s post, Easily Create a PowerShell Hash Table.

Here is the DemoHashtableWithProcesses.ps1 script that creates a hash table from process information:

$hash = $null

$hash = @{}

$proc = get-process | Sort-Object -Property name -Unique

 

foreach ($p in $proc)

{

 $hash.add($p.name,$p.id)

}

$hash

 

The $hash variable contains a hash table with a number of key value pairs in it. The count property tells me how many items are in the hash table. When I pipe the hash table to the Get-Random cmdlet and tell the Get-Random cmdlet to return one random key value pair, the results are confusing. Here is the command I am talking about:

$hash | Get-Random -Count 1

Image of command and associated output

As seen in the previous figure, all the key value pairings from the hash table are returned. I was expecting a single, randomly selected pair. I see this same problem when using the Sort-Object cmdlet. For example, when I type the following command, I expect to see the processes sorted by name:

$hash | Sort-Object -Property name

But as shown in the following figure, the sort is not working.

Image of sort not working

The problem extends itself even to the Where-Object. The following command returns nothing, even though there is a process with a value of 6108:

$hash | Where-Object { $_.value -eq 6108}

I solved this problem earlier in the week in Create a PowerShell Quiz Script post by getting a collection of keys, walking through the keys, and using the item method to retrieve the associated value.

The applicable line of code is shown here:

Function New-Question

{

 Param(

  [hashtable]$Puzzle

 )

  Foreach ($p in $puzzle.KEYS)

   {

    $rtn = Read-host "What is the cmdlet name $($puzzle.item($P))"

    If($puzzle.contains($rtn))

     { "Correct $($puzzle.item($P)) equals $p" }

    ELSE

     {"Sorry. $rtn is not right. $($puzzle.item($P)) is $p" }

    } #end foreach $P

}
#end function New-Question

Though this methodology works just fine for the Windows PowerShell Quiz script, it is too much trouble to do for a simple pipeline operation. There needs to be an easier way to walk through a hash table. And there is! The secret is to use the getEnumerator method from the hashtable object. If I want to choose a random key value pair from a hash table, I call the getenumerator method prior to passing it to the Get-Random cmdlet. The command is shown here:

$hash.getenumerator() | Get-Random -Count 1

The GetEnumerator method works the same way with the Where-Object cmdlet. The command is shown here:

$hash.GetEnumerator() | Where-Object { $_.value -eq 6108}

It also works with the Sort-Object cmdlet, as shown here:

$hash.GetEnumerator() | Sort-Object -Property name

All three of these commands and their associated output are shown in the following figure.

Image of commands and associated output

 

The second topic I want to talk about came up while I was writing Create a PowerShell Quiz by Reading a Text File.

All the examples of using the ConvertFrom-StringData cmdlet illustrate using a Here-String or similar hardcoded string data to create a hash table. This technique will not work for me because I wanted to read a text file. My first attempt generated the error shown here:

PS C:\> ConvertFrom-StringData C:\fso\Questions.txt

ConvertFrom-StringData : Data line 'C:\fso\Questions.txt' is not in 'name=value' format.

At line:1 char:23

+ ConvertFrom-StringData <<<< C:\fso\Questions.txt

    + CategoryInfo          : InvalidOperation: (:) [ConvertFrom-StringData], PSInvalidOperationException

    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.ConvertFromStringDataCommand

The error basically says that my input file is not in name=value format. But as shown in the following figure, that is not true.

Image showing error message is not true

So, I thought I needed to read the content of the file first. This time I got the error shown here:

PS C:\> ConvertFrom-StringData (Get-content C:\fso\Questions.txt)

ConvertFrom-StringData : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'StringData

'. Specified method is not supported.

At line:1 char:23

+ ConvertFrom-StringData <<<<  (Get-content C:\fso\Questions.txt)

    + CategoryInfo          : InvalidArgument: (:) [ConvertFrom-StringData], ParameterBindingException

    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.ConvertFromStringDataCommand

Next, I got the idea to pipe the information to the cmdlet. When I did this, it appeared I had hit upon a successful combination:

PS
C:\> Get-Content C:\fso\Questions.txt | ConvertFrom-StringData

 

Name                           Value

----                           -----

Canberra                       Australia

Berlin                         Germany

Ottawa                         Canada

I then tried to use my hash table. So I stored the hash table in a variable, and attempted to access the keys property, as shown here:

PS C:\> $hash = Get-Content C:\fso\Questions.txt | ConvertFrom-StringData

PS C:\> $hash.keys

Nothing came back. There are no keys? So, I examined the $hash variable by using the Get-Member  cmdlet (gm is an alias). The results of this exploration are shown here:

PS C:\> $hash | gm

 

   TypeName: System.Collections.Hashtable

 

Name                           MemberType                  Definition

Add                              Method                            System.Void Add(System.Object key, System.Object value)

Clear                             Method                           System.Void Clear()

Clone                            Method                            System.Object Clone()

Contains                        Method                            bool Contains(System.Object key)

ContainsKey                   Method                           bool ContainsKey(System.Object key)

ContainsValue                Method                           bool ContainsValue(System.Object value)

CopyTo                         Method                         System.Void CopyTo(array array, int arrayIndex)

Equals                           Method                         bool Equals(System.Object obj)

GetEnumerator              Method                         System.Collections.IDictionaryEnumerator GetEnumerator()

GetHashCode                Method                         int GetHashCode()

GetObjectData               Method                         System.Void GetObjectData(System.Runtime.Serialization.SerializationInfo inf...

GetType                        Method                         type GetType()

OnDeserialization           Method                         System.Void OnDeserialization(System.Object sender)

Remove                         Method                         System.Void Remove(System.Object key)

ToString                        Method                         string ToString()

Item                              ParameterizedProperty   System.Object Item(System.Object key) {get;set;}

Count                            Property                        System.Int32 Count {get;}

IsFixedSize                     Property                        System.Boolean IsFixedSize {get;}

IsReadOnly                    Property                        System.Boolean IsReadOnly {get;}

IsSynchronized               Property                        System.Boolean IsSynchronized {get;}

Keys                              Property                        System.Collections.ICollection Keys {get;}

SyncRoot                       Property                        System.Object SyncRoot {get;}

Values                           Property                        System.Collections.ICollection Values {get;}

 

Well, it looks like it is a hash table. So how about looking at the values property? The results are shown here—nothing. Next, I use the count property, and it tells me I have three items in the hash table.

PS C:\> $hash.values

PS C:\> $hash.count

3

This looks really weird. Then I had an idea: I wonder if somehow I obtained an array. I index into the array, and sure enough, I have an array of hash tables. This is shown here:

PS C:\> $hash[0]

Name                           Value

Canberra                       Australia

 

PS C:\> $hash[1]

Name                           Value

Berlin                            Germany

 

PS C:\> $hash[2]

Name                           Value

Ottawa                          Canada

I have an array of hash tables because of the way that Get-Content returns information. It returns an array. One element for each line of the file is a behavior that is normally fine. But in this example, the behavior causes problems. The easy way around this is to use the ReadAlltext static method from the io.file .NET Framework class. This technique is shown here:

PS C:\> ConvertFrom-StringData ([io.file]::ReadAllText("C:\fso\Questions.txt"))

Name                           Value

Berlin                            Germany

Canberra                       Australia

Ottawa                          Canada

 

That’s it for today. Join me tomorrow for more Windows PowerShell goodness. See you then.

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

 

 

Easily Remove Columns from a CSV File by Using PowerShell

$
0
0

Summary: Use a super simple, one-line command to remove columns easily from a CSV file using Windows PowerShell.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have a CSV file from which I need only two of eight columns. Can Windows PowerShell delete certain columns? I have searched the Internet for days with no luck.

—RK

 

Hey, Scripting Guy! AnswerHello RK,

Microsoft Scripting Guy Ed Wilson here. The Scripting Wife and I are absolutely enjoying Canada. The classes I have been teaching are populated with extremely bright students who have asked engaging questions. I love teaching because I get lots of good questions that force me to spend time learning new things about Windows PowerShell. Great questions are also one of the reasons I like reading the email sent to scripter@microsoft.com.

RK, your question jumped out at me today. The reason is that at first I thought it would be a pain to answer because I would need to parse through the file, and I hate parsing text. But then, I thought about it some more. A comma-separated value (CSV) file is structure text—it is not plain text. In addition, Windows PowerShell has several great cmdlets for working with CSV files. I then wondered if, after I modify the stream, I can pipe it back out to another CSV file?

I have a CSV file with process IDs, process names, and CPU time. I can use the Import-CSV cmdlet to read a CSV file and to display the contents on the Windows PowerShell console. This is shown in the following figure.

Image of reading CSV file with Import-CSV cmdlet

The text file itself contains the same information. The file is shown in the following figure.

Image of text file with same information as in previous image

It is obvious the problem with the data stored in this CSV file is that it is missing the CPU column for about half of the processes. This can be a distraction, so I want to create a new CSV file that contains the process name and the process ID information. To do this manually would be a major pain. To write a VBScript to do this would be an even bigger pain. But doing this with Windows PowerShell is surprisingly easy. The secret does not lie in either the Import-CSV cmdlet that creates an object from the CSV file, or in the Export-CSV cmdlet that writes the information to a CSV file. The secret is the Select-Object cmdlet. The cool thing about the Select-Object cmdlet is that it creates a custom object from the piped input.

Custom objects are cool. In fact, the Import-CSV cmdlet itself creates a custom object if no type information exists in the file. The following output illustrates this fact:

PS C:\> Import-Csv C:\fso\co.csv | Get-Member

 

 

   TypeName: System.Management.Automation.PSCustomObject

 

Name                                       MemberType                             Definition

Equals                                       Method                                     bool Equals(System.Object obj)

GetHashCode                            Method                                     int GetHashCode()

GetType                                    Method                                     type GetType()

ToString                                    Method                                     string ToString()

CPU                                          NoteProperty                            System.String CPU=

Id                                             NoteProperty                            System.String Id=2008

ProcessName                             NoteProperty                            System.String ProcessName=AEADISRV

 

When I use the Select-Object cmdlet to choose the processname and the id properties, I still have a custom object, but this time it only contains two properties, as shown in this output:

PS C:\> Import-Csv C:\fso\co.csv | select processname,id | get-member

 

 

   TypeName: Selected.System.Management.Automation.PSCustomObject

 

Name                           MemberType                                         Definition

Equals                           Method                                                 bool Equals(System.Object obj)

GetHashCode                Method                                                 int GetHashCode()

GetType                        Method                                                 type GetType()

ToString                        Method                                                 string ToString()

Id                                 NoteProperty                                        System.String Id=2008

ProcessName                 NoteProperty                                        System.String ProcessName=AEADISRV

 

Because the Export-CSV cmdlet accepts piped input, all I need to do is pipe the custom object from the Select-Object cmdlet (select is an alias) to the Export-CSV cmdlet. The command is shown here:

Import-Csv C:\fso\co.csv | select processname,id | Export-Csv -Path c:\fso\modco.csv –NoTypeInformation

When I open the newly created CSV file in Notepad, I can see that the command worked. I no longer have any CPU time information recorded in the file. The file is shown in the following figure.

Image of proof that command worked

 

RK, that is all there is to using Windows PowerShell to remove columns from a CSV file. The secret lies in the way that Windows PowerShell pipes objects. And when working with objects, the easiest way to create a custom object is to use the Select-Object cmdlet. In fact, if you have been following the 2011 wrap-up articles, you will see that I was recommending using Select-Object to return an object from a function instead of using Format-Table or some other format type of cmdlet.

Join me tomorrow for more cool Windows PowerShell stuff. See you here!

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

 

Convert a Semicolon-Delimited File to a CSV File

$
0
0

Summary: Learn how to convert easily a semicolon delimited file into a CSV file using Windows PowerShell.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have a problem at work, and I hope you can help. We have an application that writes one log file every four hours. These log files are formatted with semicolons, which makes them a pain to work with. My boss wants me to write a script to open each log file, search for all the semicolons, replace them with commas, save the file to a temporary file, delete the old file, and then rename the temporary file to a new format. He says I can use Windows PowerShell to do this. I have heard that Windows PowerShell is easy to use, but this sounds like a lot of work. In fact, it sounds pretty much like I would have to do the process in VBScript.

—BL

 

Hey, Scripting Guy! AnswerHello BL,

Microsoft Scripting Guy Ed Wilson here. BL, you are right, that does sound like something one might do in VBScript. I hope your boss does not mind if you take a shortcut. In fact, we do not need to open and read the contents of the semicolon-separated file. Nor do we need to do a search-and-replace operation for the semicolon in order to replace it. We can do this because everything in Windows PowerShell is an object—even the result of an Import-CSV cmdlet.

To emulate your problem, I used the Export-CSV cmdlet to export process information to a text file. In the export, I specified that I wanted to use a semicolon for the delimiter. The command I used is shown here:

Get-Process | Export-Csv -Path c:\fso\procSemi.txt -Delimiter ';' –notype

The semicolon-separated file is shown in the following figure.

Image of semicolon-separated file

I can use the Import-CSV cmdlet to read a semicolon-separated file, but I must specify that the delimiter is a semicolon. If I do not specify the delimiter, the command runs, produces no errors, and does nothing but display the columns I choose. This is shown here:

PS C:\> Import-Csv -Path C:\fso\procSemi.txt  | select name, handles

 

Name                                                   handles

When I use the delimiter parameter and specify the semicolon, the command works. The command is shown here:

Import-Csv -Path C:\fso\procSemi.txt -Delimiter ';'  | select name, handles

The command and associated output are shown in the following figure.

Image of command and associated output

To convert from one separator to another separator, it is only necessary to pipe the results from the Import-CSV cmdlet to the Export-CSV cmdlet. When using the Import-CSV cmdlet, specify the semicolon as the delimiter. When using the Export-CSV cmdlet, specify the comma as the delimiter. The command to do this is shown here:

Import-Csv -Path C:\fso\procSemi.txt -Delimiter ';' | Export-Csv -Path c:\fso\procCSV.csv -Delimiter ',' -NoType

BL, you also had the requirement to delete the original text file, and to rename the newly created CSV file with the original text file’s name. This is easy to do with Windows PowerShell by using the Remove-Item cmdlet and the Rename-Item cmdlet. The use of these two cmdlets is shown here:

Remove-Item -Path C:\fso\procSemi.txt

Rename-Item -Path C:\fso\procCSV.csv -NewName procsemi.txt

Because you really want to impress your boss, this can all be accomplished on a single line. It is best to use aliases when attempting to create a single-line command because one-liners can get quite long, and it is best to conserve space. To find out aliases for the commands I used in today’s article, I use the Get-Alias cmdlet. But rather than having to type Get-Alias four different times, I can type a single command and retrieve all the aliases I used. This command and associated output are shown here:

PS C:\> Get-Alias -Definition Import-Csv, Export-Csv, Remove-Item, Rename-Item

 

CommandType              Name                           Definition

Alias                              ipcsv                             Import-Csv

Alias                              epcsv                            Export-Csv

Alias                              del                                Remove-Item

Alias                              erase                            Remove-Item

Alias                              rd                                 Remove-Item

Alias                              ri                                  Remove-Item

Alias                              rm                                Remove-Item

Alias                              rmdir                            Remove-Item

Alias                              ren                               Rename-Item

Alias                              rni                                Rename-Item

 

I deleted my test file, so first I need to delete the newly renamed ProcSemi.txt file, and then recreate it. Here is the command I use from my history:

Get-Process | Export-Csv -Path c:\fso\procSemi.txt -Delimiter ';' –notype

Now I need to recreate the import/export CSV command using aliases from my list above. The command shown here is a single command that imports the semicolon-separated file, exports it as a comma-separated file, deletes the original semicolon-separated file, and renames the comma-separated file to the name of the original file. This is a single logical command, and I did not include any line continuation commands:

ipcsv C:\fso\procSemi.txt -del ';' | epcsv c:\fso\procCSV.csv -del ',' -not; ri C:\fso\procSemi.txt ; rni c:\fso\proccsv.csv -new procsemi.txt

The newly created and renamed CSV file is shown in the following figure.

 

BL, that is all there is to converting the format of a delimited file. Join me tomorrow for more Windows PowerShell fun.

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 

 

 

Use PowerShell to Find the Top Values Returned by WMI

$
0
0

Summary: Learn how to use Windows PowerShell to slice and dice WMI data in an easy, SQL-like fashion.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! Does the WMI select statement have any other clauses like SQL does? For example, can I just select top 10 * from to get a sample of the collection rather than doing a * for the entire collection?

—UJ

 

Hey, Scripting Guy! AnswerHello UJ,

Microsoft Scripting Guy Ed Wilson here. This is actually a rather common request. People see WMI queries that sort of look like Structured Query Language (SQL), and they immediately want to know if they can use other language statements instead of just the select statement.

UJ, the first thing you need to know about querying WMI is that it does not use SQL. It uses a query language called WQL (WMI Query Language). If the name WQL sort of looks like SQL, that is a good thing because WQL is sort of like SQL. And it sort of is not like SQL. WQL is actually a subset of ANSI SQL, with a few additions. The language keywords and their meanings are documented on MSDN. They are also covered in my WMI book.

So, to directly answer your question, no. WMI does not have a top keyword, a sortby keyword, or any of the other more sophisticated features of SQL. However, all is not lost because, using Windows PowerShell, it is very easy to accomplish these tasks. I simply pipe the results of the WMI query to other Windows PowerShell cmdlets.

For example, if I want to query the Win32_Process WMI class and find the top 10 processes that are creating the most pagefaults, I can use the following command (gwmi is an alias for the Get-WmiObject cmdlet; sort is an alias for the Sort-Object cmdlet; and select is an alias for the Select-Object cmdlet):

gwmi win32_process | sort pagefaults -des | select name, pagefaults -First 10

The command and associated output are shown in the following figure.

Image of command and associated output

If I am concerned about reducing the amount of data that is returned by the WMI query, I can save a decent amount of space by only choosing the two properties I am displaying. This modified command is shown here:

gwmi win32_process -prop name,pagefaults | sort pagefaults -des | select name, pagefaults -First 10

If I want to see the most efficient processes (in terms of the number of pagefaults generated), I use the last parameter of the Select-Object cmdlet instead of the first parameter. This command is shown here:

gwmi win32_process -prop name,pagefaults | sort pagefaults -des | select name, pagefaults -Last 10

The command and associated output are shown in the following figure.

Image of command and associated output

Using Windows PowerShell, some really cool queries are produced. For example, I decided I wanted to see the top 20 processes that are producing the most page faults. When I ran that command, I noticed that several processes appeared multiple times. I then decided to group by name so that I could see how many processes occupied the top 20 slot. To do this, I introduced a new cmdlet into the mix: Group-Object (the alias is group). The output was actually a bit surprising. First, my command; it is shown here:

gwmi win32_process -prop name,pagefaults | sort pagefaults -des | select name, pagefaults -First 20 | group name | sort count –Des

The command and associated output are shown in the following figure.

Image of command and associated output

For the last little bit, I have been running the same WMI command over and over again. If this were a WMI command that took a decent amount of time to run, it would be much better to store the results in a variable and work through the same offline data. In fact, from a decision perspective, my data keeps changing each time I run the command. To store and process, my commands would look like the following:

$wmi = gwmi win32_process -prop name,pagefaults

$wmi | sort pagefaults -des | select name, pagefaults -First 20 | group name| sort count –Des

Of course, for analyzing data it is hard to beat the Out-Gridview cmdlet because it makes it really easy to add conditions that permit quick slicing and dicing. I like to pass the results through the Select-Object cmdlet to ensure I only have the data I want to examine. Here is the command I used:

$wmi = gwmi win32_process -prop name,pagefaults | select name, pagefaults

$wmi | Out-GridView

The Out-Gridview cmdlet produces a gridview tool that is shown in the following figure.

Image of gridview tool produced by Out-Gridview cmdlet

I add a criterion by clicking Add criteria and selecting the property I want to examine. Next, I choose the operator and type the value I want to see. The cool thing is that, as I type zeroes on my number, the list of processes changes dynamically. I ended up with 500,000 page faults and found six processes that had more than that number. It would have taken me a while to discover this information without using this tool. The gridview with the associated filter is shown in the following figure.

Image of gridview with associated filter

 

UJ, the short answer to your question is no. The longer answer is that, by using Windows PowerShell, I have an extremely powerful tool set that actually makes it easy to slice and to dice the WMI data in a very logical manner. Well, that is it for today. Join me tomorrow for 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

 

 

Use the Set-WmiInstance PowerShell Cmdlet to Ease Configuration

$
0
0

Summary: Learn how to use the Set-WmiInstance Windows PowerShell cmdlet to ease configuration of computers.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I was using the Get-Command cmdlet to look at what things I can do with Windows PowerShell, and I noticed a cmdlet called Set-WmiInstance. I looked at the help, but I don’t get it. What is the big deal?

—BL

 

Hey, Scripting Guy! AnswerHello BL,

Microsoft Scripting Guy Ed Wilson here. Well, the Scripting Wife and I have had a lot of fun in Montreal this week. In a few days, we will be leaving and heading home. The Windows PowerShell class I have been teaching this week has been great, and the students have asked many good questions. In addition, they have given me many ideas for Hey, Scripting Guy! Posts for the future. These questions and ideas usually take the shape of, I am trying to do such and such, but I am having a hard time finding out what I need to do.

BL, the Set-WmiInstance cmdlet is cool, but using it can be a bit complicated. The class parameter sets a property directly on a WMI class. Unfortunately, this works only on singleton WMI classes, of which there are very few. The one singleton class used by network administrators is Win32_Operatingsystem, but unfortunately, it does not work with the class parameter. Therefore, I only use the Set-WmiInstance cmdlet with the path parameter. The trick to using the Set-WmiInstance class with the path parameter is to know what a WMI path actually is and how to retrieve the WMI path for a WMI instance of a class.

Note   In the help files, the example uses the Win32_WMISetting, and it changes the logginglevel property. This works on Windows Server 2003 and earlier, but beginning with Windows Vista, WMI uses ETL logs and does not use this legacy parameter.

One of the good things about working with a singleton class is that the path to the class is easy. The path is the WMI class name equals the @ symbol. This translates into syntax such as that shown here:

WMISingleTonClass=@

When using the Set-WmiInstance on a WMI class, the property to be set uses a hash table as it passes to the arguments parameter. The pairs are the property name and the value to assign to the property name. An example of this syntax is shown here:

@{propertyName=Value}

To assign a new description to the Win32_OperatingSystem singleton class, I perform the following steps (these steps require administrator rights on the target computer):

  1. I use the Set-WmiInstance class
  2. I supply the path to the Win32_OperatingSystem class to the path parameter
  3. I create a hash table using the WMI property name and the value to update the property with

The following command illustrates this technique:

Set-WmiInstance -Path win32_operatingsystem=@ -argument @{description="iammred"}

If I did not have the Set-WmiInstance cmdlet, I would need to use several steps to set the Operating System description property. The steps required to assign a new value to the description property of the Win32_OperatingSystem class are shown here:

  1. Use the Get-WmiObject cmdlet to retrieve an instance of the Win32_Operatingsystem WMI class.
  2. Store the returned management object in a variable.
  3. Assign a new value for the description property.
  4. Call the put method to write the information back to WMI.

The following commands illustrate this technique (the commands must run with elevated permissions):

$os = Get-WmiObject –class win32_operatingsystem

$os.Description = "MyNewDescription"

$os.put()

The situation changes a bit when working with a WMI class that is not a singleton. For example, if I query the Win32_logicalDrive WMI class on my laptop, three instances of the class appear. If I limit the results to only fixed local disks (drivetype = 3), I have two instances of the WMI class. The commands and associated output are shown in the following figure.

Image of commands and associated output

To use the path parameter of the Set-WmiInstance cmdlet requires the path to a specific instance of the WMI class. The following WMI command returns the name of each logical disk and the path to each fixed logical disk.

PS C:\> Get-WmiObject -Class win32_logicaldisk -Filter {drivetype=3} | select name, __path

 

name                                                        __PATH

C:                                                          \\MRED\root\cimv2:Win32_LogicalDisk.DeviceID="C:"

Q:                                                          \\MRED\root\cimv2:Win32_LogicalDisk.DeviceID="Q:"

 

The important port to focus on right now is the result of the query that returns the __path property. The WMI path represented here comprises five parts. The parts in the __Path are shown in the following table.

 

Computer

Namespace

Class

Key Property

Value

\\MRED

root\cimv2

Win32_LogicalDisk

DeviceID

C:

 

If I want to change the Volumename on the C: drive, I use the Set-WmiInstance cmdlet as shown here (this command must run with elevated rights):

Set-WmiInstance -Path '\\MRED\root\cimv2:Win32_LogicalDisk.DeviceID="C:"' -argument @{VolumeName="newlabel"}

The command and associated output are shown in the following figure.

 Image of command and associated output

Now, if I were to change the volume name using the Get-WmiObject cmdlet, I would use the syntax that is shown here:

$disk = Get-WmiObject -Class win32_logicaldisk -Filter "deviceID='c:'"

$disk.VolumeName = "mycustomlabel"

$disk.Put()

In light of the long required path for the Set-WmiInstance command, it might appear the previous Get-WmiObject cmdlet exposes the property modification capability easier. It is possible to combine the best of both worlds. I use the Get-WmiObject cmdlet to retrieve the __Path property to the C: drive, and I store it in the $path variable. I then use that path with the Set-WmiInstance cmdlet. The resultant command is two lines instead of the three lines required for the other method.

The command to combine the two techniques is shown here:

$path = (Get-WmiObject -Class win32_logicaldisk -Filter "deviceID='c:'").__path

Set-WmiInstance -Path $path -argument @{VolumeName="test"}

 

In the following figure, the first line illustrates the technique of obtaining the __path directly from the Get-WmiObject cmdlet. The second line displays the path. The third line stores the path in the $path variable, and the fourth line is the Set-WMiInstance command.

Image of commands

Well, BL, that is all there is to using the Set-WmiInstance cmdlet. I hope you found the discussion helpful. To everyone who has tuned in; I invite you to join me tomorrow.  

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

 

Create a Really Cool PowerShell ISE Profile

$
0
0

Summary: Learn how to create a powerful Windows PowerShell ISE profile by running a single script.

 

Microsoft Scripting Guy Ed Wilson here. One of the problems with writing a daily blog is that after a while, you end with a huge collection of articles and scripts. When I got a new laptop, and immediately left the country, I was really happy that I could access the Hey, Scripting Guy! Blog, and copy scripts so I could customize my Windows PowerShell ISE. Then I realized I had a problem: everything was cumulative, which meant that I spent hours and hours copying scripts, running scripts, and checking through dozens of Hey, Scripting Guy! Blog posts (many of which referenced other Hey, Scripting Guy! Blog posts). You get the idea.

Luckily, I was able to begin with the Weekend Scripter article, Clean Up Your PowerShell ISE Profile by Using a Module. But I wrote that article nearly a year ago, so there was a huge amount of new material to work through. In the end, I decided to do a “new release” of my profile and associated modules.

I have uploaded my current Windows PowerShell ISE profile to the Scripting Guys Script Repository. The profile script is very simple, and it is shown here:

import-module PowerShellISEModule

import-module MenuModule

import-module SnippetModule

import-module copymodule

BackUp-Profile

Add-MenuItems

New-ModuleDrives

The reason the code is so short is that everything is in the four accompanying modules. I will begin with the PowerShellISEModule. This is the main module, and it contains a number of really cool functions and a few aliases for those functions. The aliases and associated functions are discoverable by using the Get-Command cmdlet. Here is the code I use:

Get-Command -Module powershellisemodule | sort commandtype

The command and associated output are shown in the following figure.

Image of command and associated output

The functions from the PowerISEModule are shown here. I have included links to the Hey, Scripting Guy! Blog post where I discussed each of these functions:

New-ModuleDrives                                                                               

Start-iseTranscript                                                                            

Remove-AliasFromScript                                                                         

Set-Profile                                                                                    

Add-Help                                                                                       

Add-HeaderToScript                                                                             

backUp-Profile                                                                                 

Get-logNameFromDate                                                                            

Get-FileSystemDrives    

 

The MenuModule is new, but it incorporates all of the elements from my articles that talk about adding menu items to the Windows PowerShell ISE. The four functions contained in this module are shown here:

Add-MenuItems                                                                                 

Get-Fonts                                                                                     

Get-PsIseColorValues                                                                           

Set-PsISE   

I made a few changes to the Add-MenuItems function because I moved the ISE preference scripts into functions in order to consolidate the functionality and to make it easier to customize the ISE. Now instead of having numerous dependencies on various scripts that may reside in different locations, I put everything into a module.

The SnippetModule is also new. I modified the Export-ModuleMember command to export only one function. When using the Get-Command cmdlet, only one function appears. That function is the Get-CodeSnippetV2 function. I wrote a series of articles that talk about adding code snippet functionality to the Windows PowerShell ISE.

However, there are many other functions in the SnippetModule:

New-Sniptype

Remove-sniptype

copy-codeSnippetsFromInterNet

Test-IsAdministrator

Register-CodeSnippets

The preceding functions are used to install the code snippet functionality into the Windows PowerShell ISE. If you need to do this, modify the Export-ModuleMember command at the bottom of the snippetModule. This command is shown here:

Export-ModuleMember -Function Get-CodeSnippetV2, Register-CodeSnippets -Variable sniphome

After you have installed the code snippets, there is no reason to include the Register-CodeSnippets function into your daily Windows PowerShell ISE profile. I highly recommend that you use the –path parameter and specify the path to the snip.zip file when installing the code snippets feature. When I was testing the installation from the Internet feature, it worked once out of four attempts. The command to register the code snippets using the path feature would appear something like this (assuming I had copied the snip.zip file to the c:\fso folder):

Register-CodeSnippets –path c:\fso\snip.zip

I initially wrote the Copy-Modules.ps1 script for my Windows PowerShell 2.0 Best Practices book, and I have been using it on a regular basis ever since. Well, today I decided to add it to a module itself. The reason is I wanted to make it easy to bring into my Windows PowerShell ISE profile. I put all of the functions into the CopyModule module, and only export the Copy-Modules function. The Copy-Modules function takes a single parameter that is the folder that contains all of the psm1 files to import. This command is shown here:

Copy-Modules –path c:\fso

To install this ISE module release, use the Copy-ISEProfile.ps1 script that is included in the ISE_Modules.zip file that you can download from the Scripting Guys Script Repository.

The Copy-ISEProfile.ps1 script is shown here:

$path = "C:\fso"

If(!(Test-Path $profile))

  {new-item -Path $profile -ItemType file -force}

Import-Module -Name (Join-Path -Path $path -ChildPath copymodule.psm1)

Copy-Modules -path $path

psedit $profile

When you run the Copy-ISEProfile.ps1 script, provide the path to where the ISE modules are extracted. At the end of running the Copy-ISEProfile script, it will open the ISE profile in a new tab. Paste the ISE module commands to the profile. These commands appear here, but are on the Scripting Guys Script Repository as well:

import-module PowerShellISEModule

import-module MenuModule

import-module SnippetModule

import-module copymodule

BackUp-Profile

Add-MenuItems

New-ModuleDrives

 

Well, that is about it for now. Join me tomorrow for more Windows PowerShell goodness.

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

 


Use a PowerShell Function to Find Specific WMI Classes

$
0
0

Summary: Use a Windows PowerShell function to find WMI classes with specific qualifiers.

 

Microsoft Scripting Guy Ed Wilson here. In Thursday’s article, I talked about using the Set-WmiInstance cmdlet to work with WMI classes. One of the parameters, the class parameter, works with WMI singleton objects. Now, it is certainly possible to use WBEMTest to find singleton WMI classes. Such a WMI class is shown in the following figure in the WBEMTest utility.

Image of a WMI singleton class

But with 1,085 WMI classes in Root\Cimv2, it is faster and more fun to use a WMI schema query. WMI schema queries are mentioned on MSDN, but there are no Windows PowerShell examples. I decided I needed to write a Windows PowerShell function that would query the schema to find the singleton classes for which I was looking. In addition, there are other class qualifiers I was interested in seeing as well. For example, there is a supportsupdate qualifier that lets me know that I can use that class to make modifications to a computer. There are other qualifiers that are even more important: abstract and dynamic. As an IT pro, I want to query dynamic WMI classes, and not the abstracts. For ease of use, I uploaded the script to the Scripting Guys Script Repository.

I ended up writing a function I could use to find classes with specific qualifiers. As shown in the following figure, there are a few singleton WMI classes. I opened the function in the Windows PowerShell ISE, ran the script once to load the function into memory, and then I went to the command pane and typed the following command:

Get-WMIClassesWithQualifiers -qualifier singleton

The command and associated output are shown in the following figure.

Image of command and associated output

A WMI schema query queries the meta_class WMI class. It uses the isa keyword to specify from which WMI class I want to return the schema information. That part is rather simple. The difficult part was getting the quotation marks placed in the right position to enable automatic querying. Here is the query line I derived:

$query = "select * from meta_class where __this isa ""$($class.name)"" "

I am interested in the qualifiers; therefore, I choose only the name of the WMI class and the qualifiers. This line appears is shown here:

$a = gwmi -Query $query -Namespace $namespace |

  select -Property __class, qualifiers

If the qualifiers contain the qualifier I am looking for, I return the WMI class name:

if($a.qualifiers | % { $_ | ? { $_.name -match "$qualifier" }})

    { $a.__class }

 

The core portion of the script, with the aliases removed is shown here:

Param([string]$qualifier = "dynamic",

  [string]$namespace = "root\cimv2")

 $classes = Get-WmiObject -list -namespace $namespace

 foreach($class in $classes)

 {

  $query = "Select * from meta_class where __this isa ""$($class.name)"" "

  $a = Get-WmiObject -Query $query -Namespace $namespace |

  Select-Object -Property __class, qualifiers

   if($a.qualifiers | ForEach-Object { $_ | Where-Object { $_.name -match "$qualifier" }})

    { $a.__class }

  } #end foreach $class

 

Well, that is about it for today. I hope you enjoy the function, and have an awesome weekend.

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

 

Focus on the Object

$
0
0

Summary: In today’s guest article, Microsoft PowerShell MVP Shane Hoey reminds us to focus on the object.

Microsoft Scripting Guy Ed Wilson here. Today we have a guest article by Windows PowerShell MVP Shane Hoey. Here is what Shane has to say about himself: “My scripting experience started back when Windows XP was the new kid on the block, and soon found myself scripting on a daily basis with VBScript, WMI, and ADSI . Fast forward to the Scripting Games 2009, and I finally discovered Windows PowerShell, and I’ve been hooked ever since. One of the things I like most about Windows PowerShell is its ability to easily automate, especially my daily repetitive tasks. My background is in system and network administration, but at work they just call me the PowerShell Geek these days! By the way, I also run the PowerShell Usergroup Brisbane.

Take it away, Shane.

 

I’ve changed my background to HTML color #012456, I need some inspiration, and this time http://drscripto.tv is just not what I need! After all, I’ve been asked to write a Hey, Scripting Guy! Blog post.

My buddy Chris Brown who recently started a Windows PowerShell user group in Melbourne, Australia, was on Twitter as usual. In fact, he’d give Sean Kearney a run for his money when it comes to being passionate about Windows PowerShell. And so the Twitter conversation started:

@shanehoey: Hey @chrisbrownie, any ideas I can steal/borrow for a Windows PowerShell blog article I need to write ? I have scripter’s block

@kittenstix: @shanehoey how to clean up Active Directory accounts using PowerShell & scheduled tasks

@ChrisBrownie: @shanehoey umm.. how about how awesome PowerShell is and why SysAdmins should learn it?

@jamesbannan @shanehoey how to manage Hyper-V and VMware VMs using PowerShell ;-)

@Brendonrd RT @Kittenstix @shanehoey how to clean up AD accounts using powershell & scheduled tasks - PS>format domain$ /q /y =)

I sometimes think Twitter was created for the Windows PowerShell community!

A few more tweets arrived, and I started to think about how I started scripting back in 2009. I remember thinking, “OK, this can’t be too hard. It looks just like the cmd box I’m used to. Hey, I can kind of even understand VBScript, so this is going to be a breeze.” And I started off by typing in Help.

I wrote my first Windows PowerShell script that night. It was my first submission for the 2009 Scripting Games Event 1. Well, I guess you could call it a script, but little did I realize the horror of Event 3 would stick with me for a long time to come. I still remember it to this day! In hindsight, it really was not that hard. It’s just that I did not understand the basic PowerShell principles.

Here is the exciting thing. Those cmdlets I learned to use during the 2009 Scripting Games—Get-Help, Get-Command, and Get-Member—I still use on a daily basis.

So the Twitter conversation continued.

@shanehoey: @Kittenstix hows this for a starting point? http://psdu.co/re52NX  & http://psdu.co/pWm3Em  

@shanehoey: @jamesbannan  have you seen this ? http://psdu.co/pvJABy

@shanehoey: @Brendonrd have you seen http://psdu.co/qhobXk Free PowerShell Training in November

 

SHAMELESS PLUG ALERT   Yes, that’s right. Shane and Chris are running Free PowerShell Training in November. It’s based on the More Lunches series by Don Jones, so grab the book and join us for some free training in November.

So it seems like previous Scripting Guys articles have already covered all the blog suggestions from Twitter.

I’m staring at some Lego on my desk wondering, what can I blog about? Then it hit me: “Always focus on the object.” When I’m helping IT pros to start learning Windows PowerShell, I often talk about “Always focusing on the object.” I say it all the time, but what do I really mean by it ? Well, let’s have a look:

Get-process | sort PM -descend | Select -first 10

Let’s break down to each command by running Get-Member, and take note of the object’s type, property, and methods.

Get-process | get-member

Get-process | sort-object –property PM -descending | Get-Member

Get-process | sort-object –property PM -descending | Select -first 10 | Get-Member

So the object has not changed, but remember that it can indeed change. This time, we are going to run a similar command again:

Get-Service | Select-Object –property  Name | Format-list Name

So once more I want you to use Get-Member and discover a bit more about the objects in the pipeline:

Get-Service | Get-Member

Get-Service | Select-Object –property Name  | Get-Member

Get-Service | Select-Object –property Name | Format-List Name | Get-Member

Hey. that’s cool. By using the Get-Member cmdlet, we can quickly see how important it is to “focus on the object” as it traverses the pipeline. Let’s look at this a bit closer:

Get-Service | Select-Object –property Name | Get-Member

Get-Service | Select-Object –first 10 | Get-Member

Did you see that? Want a hint? Have a closer look at the object’s type, and you’ll notice that it’s been changed.

So hopefully through these simple examples, you can see the importance on “focusing on the object,” especially as it passes thru the pipeline. Unfortunately, we don’t have time today to dig too deep. Oh, and by the way, one more thing: don’t forget to watch out for pipeline input ByValue and ByPropertyName!

 

I want to thank Shane for his article today. I especially want to thank Shane and Chris for their efforts in spreading the word about Windows PowerShell. Check out their free training series in November. It is sure to be awesome. Join me tomorrow as I begin a new week on the Hey Scripting Guy! Blog.

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

 

A PowerShell WMI Helper Module Described

$
0
0

Summary: In this article, Microsoft Scripting Guy Ed Wilson begins part 1 of a multipart WMI helper function module for Windows PowerShell.

 

Microsoft Scripting Guy Ed Wilson here. While I was teaching my Windows PowerShell Best Practices class in Montreal, we spent an entire day talking about Windows Management Instrumentation (WMI). While WMI seems to have a “bad reputation” in terms of complexity, consistency, and discoverability, Windows PowerShell has done much to make WMI more consistent, less complex, and definitely more discoverable. Personally, I love WMI not only because it provides ready access to reams of documentation about my computer systems, but also because it exposes many methods and writable properties that allow me to quickly and accurately configure many aspects of my systems. Ever since I began working on my Windows PowerShell Step By Step book for Microsoft Press, I have written hundreds of WMI scripts and helper functions. This week, I am going to collect together many of the helper functions into a single module that will make access to WMI information easier.

The first function I add to my WMI module is the Get-WMIClassesWithQualifiers function that I wrote for the Use a PowerShell Function to Find Specific WMI Classes post last Saturday. See that article for details on the function.

Creating a Windows PowerShell module is really easy. I open the Windows PowerShell ISE and paste my Get-WMIClassesWithQualifiers function into the script pane. This technique is shown in the following figure.

Image of technique to create module

After I have pasted the first function into the module, I need to save the module so that I do not lose my work. I save the module into my scratch directory (called FSO off the root drive), giving me a place to work. After I have completed the module, I will use my Copy-Modules function to install the module into my user module location. The key thing to remember when saving a module is that the file extension must be .psm1. By default, the Windows PowerShell ISE saves files with a .ps1 extension, which is a Windows PowerShell script. A .psm1 file extension indicates a Windows PowerShell module. This technique is shown in the following figure.

Image of saving module with .psm1 extension

I decided that I would also like to include the Get-WmiClassMethods function and the Get-WMIClassProperties functions in my WMI module. The great thing about these two functions is that they make it really easy to find methods and properties that are implemented and writable. This is really important! Because of the way that the Get-Member cmdlet works, it shows everything in WMI as read/write. And though this is technically correct (at least to a point in that I can update an object in memory), it is of very little practical value because I cannot update all WMI objects. For example, if I use the Get-Member cmdlet on the Win32_LogicalDisk WMI class, it reports that all of the properties are Get/Set. Obviously, I cannot use WMI to change the size of my disk drive, and I am not certain I want to attempt to change the amount of free space on my drive by using WMI. This is shown in the following figure.

Image of running Get-Member on Win32_LogicalDisk WMI class

Therefore, I need a better methodology for obtaining this information. I can use the Windows Management Instrumentation Tester (WbemTest) tool, but unfortunately, it requires me to examine every property in an individual manner to discover if a property is writable. This is not an acceptable solution when attempting to do a bit of quick scripting work.

Back in March of 2011, I wrote a series of Hey, Scripting Guy! posts where I explored writable WMI properties and implemented WMI methods. I decided to adapt those scripts to meet the need of my HSGWMIModule. The original script from March 12, 2011, is shown on the Scripting Guys Script Repository. I uploaded the modified functions to the Scripting Guys Script Repository so that you will have them if you wish to follow along.

I made the following changes to the original functions:

  1. It no longer checks for all WMI classes in a particular namespace. Instead, I have limited the scope to a single class.
  2. I made the $class parameter a mandatory parameter. You must supply a WMI class name to use the modules.
  3. I added comment-based help, and included examples of use.
  4. I changed the default computer name from "." to $env:computername so that an actual computer name is used.
  5. I perform a [wmiclass] cast of the string supplied to $class so that it converts the string into a management object. This precludes the use of the Get-WmiObject cmdlet to return management objects.
  6. I removed the "entry" to the script, so running the script loads the functions into memory, but does not execute any searches.

So I have added the newly revised modules to my HSGWMIModule. I import the module from my scratch location, and use the Get-Command cmdlet (gcm is alias) to see the names of the functions contained in the module. Here are the commands and associated results:

PS C:\Users\edwils> Import-Module C:\fso\HSGWMImodule.psm1

PS C:\Users\edwils> gcm -Module hsg* | select name

Name                                                                                                                                                                                                           

Get-WMIClassesWithQualifiers                                                                                                                                                                                  

Get-WmiClassMethods                                                                                                                                                                                            

Get-WmiClassProperties                                                                                                                                                                                         

New-Underline                 

 

The complete HSGWMIModuleV1 appears on the Scripting Guys Script Repository. You should download it, install it, and play with it. It is cool.

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

 

Use PowerShell to Easily Find the Key Property of a WMI Class

$
0
0

Summary: Learn how to find the key property of a WMI class by using a Windows PowerShell function.

 

Microsoft Scripting Guy Ed Wilson here. Well, I received a decent amount of feedback about version 1 of my HSGWMIhelper Windows PowerShell module. Today, I want to add a needed feature to the module: I need the ability to find a WMI key property from a WMI class. I added the Get-WMIKey function to the Scripting Guys Script Repository.

One thing I often need is to be able to do is find the key property of a WMI class. For example, after I have imported my HSGWMImoduleV2 module, I can use the Get-WmiKey function to retrieve the key property from the Win32_Process WMI class. The handle property is the key property from the class. This is shown here:

PS C:\> Import-Module hsg*v2

PS C:\> Get-WmiKey win32_process

I now start an instance of Notepad, and use the Get-Process cmdlet to retrieve information about the Notepad process as shown here:

PS C:\> notepad

PS C:\> Get-Process notepad

 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName

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

     78       9     4272       8948    86     0.03   6872 notepad

I can then use the id property value (equals the handle property from WMI) with the [WMI] instance accelerator to retrieve that specific instance of the Win32_Process class. The following is the syntax for this command:

[wmi]"win32_process.handle=6872"

The command and associated output are shown in the following figure.

Image of command and associated output

One thing that is great about the WMI instance accelerator is that instance methods are immediately available. This technique is shown in the following code:

([wmi]"win32_process.handle=6872").terminate()

The command and associated output are shown in the following figure.

Image of command and associated output

In the Get-WmiKey function, the first thing I do is create help by using comment-based help (all the functions in my HSGWMIModuleV2 module have comment-based help). The help portion of the function is shown here.

function Get-WmiKey

{

  <#

   .Synopsis

    This function returns the key property of a WMI class

   .Description

    This function returns the key property of a WMI class

   .Example

    Get-WMIKey win32_bios

    Returns the key properties for the Win32_bios WMI class in root\ciimv2

   .Example

    Get-WmiKey -class Win32_product

    Returns the key properties for the Win32_Product WMI class in root\cimv2

   .Example

    Get-WmiKey -class systemrestore -namespace root\default

    Gets the key property from the systemrestore WMI class in the root\default

    WMI namespace.

   .Parameter Class

    The name of the WMI class

   .Parameter Namespace

    The name of the WMI namespace. Defaults to root\cimv2

   .Parameter Computer

    The name of the computer. Defaults to local computer

   .Notes

    NAME:  Get-WMIKey

    AUTHOR: ed wilson, msft

    LASTEDIT: 10/18/2011 17:38:20

    KEYWORDS: Scripting Techniques, WMI

    HSG: HSG-10-24-2011

   .Link

     Http://www.ScriptingGuys.com

 #Requires -Version 2.0

 #>

Next, I create the input parameters. I use parameter attributes to make the class parameter mandatory, and I assign it to the first position. The remainder of the parameters use techniques I have written about in the past.

Param(

   [Parameter(Mandatory = $true,Position = 0)]

   [string]$class,

   [string]$namespace = "root\cimv2",

   [string]$computer = $env:computername

 )

Now, I do something pretty cool (I use this technique in other functions in the HSGWmiModuleV2 module). I cast the string in the $class variable to be an instance of a management object. I use parameter substitution to put together a complete path: computer name, WMI namespace, and WMI class name:

[wmiclass]$class = "\\{0}\{1}:{2}" -f $computer,$namespace,$class

Now I obtain a collection of the properties of the class, and I expand the qualifiers associated with each property. I then look for the name that is equal to “key” and I print out the property names. It is important to note that some WMI classes have multiple keys, such as Win32_product. This portion of the function is shown here:

  $class.Properties |

      Select-object @{Name="PropertyName";Expression={$_.name}} `

        -ExpandProperty Qualifiers |

      Where-object {$_.Name -eq "key"} |

      ForEach-Object {$_.PropertyName}

} #end GetWmiKey

 

I included the Get-WMiKey function in the second version of my HSGWmiModuleV2 module. You will find the complete module on the Scripting Guys Script Repository.

 

That’s it for now. See you tomorrow when I will add another really cool WMI function to my WMI module.

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

 

Use a PowerShell Function to Get WMI Key Property Values

$
0
0

Summary: Microsoft Scripting Guy Ed Wilson discusses his version 3 of a Windows PowerShell WMI helper module.

 

Microsoft Scripting Guy Ed Wilson here. I have really enjoyed working on my HSGWMIModule project this week. On Monday, I created the base WMI module, and included a couple of really great functions that query the WMI schema and return WMI class methods and properties. On Tuesday, I added a function that returns the key property of a WMI class. This is useful when working WMI instance methods. Today, I create a function that will return the values of those key properties. There are times when I only need to know the key property of a WMI class, and there are other times when I want to see the path to all the possible instances of a WMI class. Today’s function addresses these situations.

Note   As I did in the two previous Hey, Scripting Guy! Blog posts, I have uploaded the current iteration, version 3, of the WMI module to the Scripting Guys Script Repository.

To use HSGWMImoduleV3, I first must “install” it. Installation is simple; I use my Copy-Modules function from the Windows PowerShell ISE Profile and Modules project I wrote. I discussed this on Friday, October 21, 2011.

Note   If you need more information about Windows PowerShell modules, see this collection of Hey, Scripting Guy! posts.

After you have installed the module, import the module by using the Import-Module cmdlet. It is not necessary to use the complete module name; all that is required is to use wildcard characters that uniquely identify the module name. I use the command seen here to load the module into my Windows PowerShell ISE:

Import-Module hsg*wmi*3

After it's loaded (imported), all the functions contained in the module load onto the function drive. As shown in the following figure, I use the Get-WMIKeyvalue function to find the path to instances of the Win32_Process WMI class.

Image of using Get-WMIKeyvalue function

Most of the lines in the Get-WMIKeyValue function are comment-based help. Therefore, when I use the Get-Help cmdlet, as shown in the following figure, nicely formatted help is displayed in the output pane.

Image of using Get-Help cmdlet

The main portion of the Get-WMIKeyValue function consists of three parameters, a Get-WMIObject command, and a Select-Object command:

Param(

    [Parameter(Mandatory=$true)]

    [string]$class,

    [string]$computername = $env:COMPUTERNAME,

    [string]$namespace = "root\cimv2"

)

  Get-WmiObject -Class $class -ComputerName $computername -Namespace $namespace |

  Select __PATH

} #end function get-WmiKeyvalue

 

I call the Get-WMIKeyValue function by passing at least the WMI class name. The Get-WmiObject cmdlet retrieves the class, and pipes the management object to the Select-Object cmdlet where I choose the __Path system property. The __Path system property returns to the caller of the function.

Suppose I want to use the Invoke-WMIMethod cmdlet to terminate an instance of Notepad.exe. I use the Get-WMiKeyValue function to return instances of the Win32_Process WMI class. This command is shown here:

Get-WmiKeyvalue win32_process

I use the Get-Process cmdlet to ensure I get the right instance. The value in the ID property returned by Get-Process matches the value in the handle WMI property of the Win32_Process class. I copy the path, and paste it into the path property of the Invoke-WMIMethod class. I then call the terminate method. The Invoke-WMIMethod command is shown here:

Invoke-WmiMethod -Path '\\MRED\root\cimv2:Win32_Process.Handle="4652"' -name terminate

The commands and associated output are shown in the following figure.

Image of commands and associated output

 

Well, that is about all there is to the Get-WmiKeyValue function. Download version 3 of the WMI helper function module from the Scripting Guys Script Repository, and let me know what you think. If there are things you would like to see, just let me know. See you tomorrow.

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

 

 

Viewing all 3333 articles
Browse latest View live