The How and Why of Learning PowerShell (Part 2 of more than 1)

In my last post in this series I said that you should start learning to use PowerShell now if you haven’t already, but I really didn’t give any reasons why you should. I then went on to talk about how to get started.

In this post I wan to focus a bit more on why I think learning PowerShell is a good thing. I’d like to start this conversation by talking a bit about careers. 

The purpose of a career is to enable people to take care of  fundamental concerns.

We all need to be able to support ourselves, our family, and loved ones. We want to provide shelter, safety, and nourishment for ourselves and the ones we care for.

For those of us that are blessed with a job that we really enjoy, our career can help us meet other needs further up the pyramid. In IT, there is often a lot of creativity and problem solving among other things. (Maybe too much creativity but that is another topic entirely)

The rest of this post is being written with the assumption that the reader acknowledges career as important. So how does PowerShell fit into this whole discussion. Well to start, I have to make one more assumption. Microsoft makes some great products and will continue to have a strong influence in the world of Information Technology. I am not saying they are the only game in town by any means, but I am saying that they have large influential role in IT.

OK, that pretty much does it for assumptions.

What  a career often comes down to is being able to add value to a business. If Person A can do a task in 20 minutes and Person B can do a task in 50 minutes, Person A has the capacity to spend another 30 minutes adding value to the company that  Person B does not have

Person A is the person that has spent the time learning how to effectively use PowerShell. There are a few reasons why scripting and automating can be so powerful.

  • Procedures and tasks become repeatable
  • The results of procedures and task become consistent
  • The number of mistakes is greatly reduced

Microsoft has added PowerShell to its list of Common Engineering Criteria as of the beginning of Microsoft’s 2009 fiscal year. What this means is that all server products that ship from MS must support PowerShell. Now that we are 3/4s of the way through 2010, this is becoming more and more evident. Just off the top of my head here are a few MS server products that support PowerShell.

  • Exchange Server
  • SQL Server
  • Virtual Machine Manager
  • System Center Operations Manager
  • System Center Data Protection Manager
  • Active Directory

There are a bunch more and I haven’t even began to list third party companies that are banking on PowerShell. The list is big and continues to grow every quarter.

Here’s another secret. PowerShell will never completely replace the GUI. Server products are going to have a GUI admin interface forever and a day. This is a good thing! GUIs enable us to understand and learn  new technologies. They help us see what options are available to us and what kinds of things we can do with a particular product. However, we are getting closer to the point in IT in which being able to fuddle through GUIs will be enough. Our peers will be using GUIs to learn how something works and then they are going to use the shell to automate every routine task they can think of. So here’s the question. Do you want to be the person that is creating the routines or the person that executes the routines. Thinking about value for a company and my career, I know which boat I want to be in.

The How and Why of Learning to Use PowerShell (Part 1 of more than 1)

Two years ago, I spent a great deal of time evangelizing PowerShell within my company and publicly as much as possible. Frankly, I felt a little bit like Paul in Athens in Acts 17:32-34.

Some sneered, but others said “We want to hear you again on this subject.” At that, Paul left the Council. A few men became followers Paul and believed.”

Indeed, I did get a few sneers, but mostly what I heard was, “We want to hear more on this subject.” I eventually took that to mean, “This sounds really great but I am not ready for it and it can’t help me solve an immediate problem need right now.”

If that was you one to two years ago, I think its time to check out PowerShell again.

I was listening to the PowerScripting Podcast the other day and Hal and Jonathan were interviewing Don Jones, a well known trainer and PowerShell MVP. During the interview, he brought up a very interesting point that I think was spot on. There are a ton of developers who are PowerShell MVP’s. These people provide a tremendous service to the community, and their input is invaluable. They have brought incredible amounts of additional functionality to PowerShell. Take a look at PowerTab and PowerBoots as just a couple examples.

However, when you go to search on the web for PowerShell,  there is a ton of information that is heavily developer centric that addresses developer oriented problems and solutions. If you are a systems administrator just jumping into PowerShell. Don’t be overwhelmed! It doesn’t have to be that complicated. You don’t have to know how to write code or even script. The only thing you have to be willing to do is work with a command shell and type in commands. That’s all it takes to get started!

PowerShell was created to make the lives of us system admins easier. However, there was a design choice that the PowerShell team made up front that you will have to deal with and overcome. The first 10 minutes of using PowerShell will be a little tough. Just know that it is for the sake of making the next 10 years of your life as an engineer much easier.

Here’s the first thing to know about PowerShell, all of your existing command line tools work as expected. Here I am using ipconfig and some net user commands

image

So that makes life pretty simple when you get started. There are even linux commands that will work. Check out ls or cat if you’ve got a *NIX background.  Things get a little (just a little) bit more tricky when you start using built in PowerShell commands, called “Cmdlets” They are called Cmdlets because they are tiny commands. Think of novels compared to novelettes. This will make more sense as you start to use PowerShell. Basically you can string a set of Cmdlets together to execute a complete command. Cmdlets are the building blocks of functionality in PowerShell.

I will be the first to admit, when I first saw a Cmdlet, I was annoyed. I saw a small stet of Cmdlets like theses:

  • Get-Content
  • Set-Content
  • Get-Serivce
  • Start-Service
  • Get-Process
  • Stop-Process

Here’s what annoyed me. I hated that there was a “-“ in the middle of all the Cmdlets. I remember thinking that it would be hard to type these all the time. I just remember being overwhelmed by the examples I saw and thinking it would be hard to figure out what went where when I was typing in the command shell. However, I didn’t have the context and understanding of how these Cmdlets work so I hope this will help you get over the appearance of complexity.

Cmdlets consist of 2 parts. They all have a verb and a noun. The syntax is “verb – noun” All Cmdlets do something to something. Lets look at Get-Process. This Cmdlet gets (lists/shows/displays) processes that are running on your computer. This brings up another interesting point. Nouns are always singular. This is done for the sake of clarity. There is a Cmdlet called Get-ChildItem. Using plural forms of the word could cause confusion. Do you use –childs or –children? Because PowerShell is used by many people in many countries that speak many languages, the goal is to keep the naming scheme as simple as possible.

The idea here is if you want to look at processes, you know you can use Get-Process. So what if you wanted to look at VMs? You can probably guess that it will be Get-VM. In fact, there is a set of accepted verbs that should be used by people that create Cmdlets. If they have verbs that don’t adhere to the list, you should let them know.

So here’s a couple things to get started. PowerShell comes with the OS in Windows 7. If not, download it from here.

Fire up PowerShell and run some commands like you would in CMD. (See, that wasn’t so bad)

Now try a few cmdlets like Get-Process and Get-Service

Next, there are two Cmdlets that will help you discover all kinds of things.

Get-Help and Get-Command

You can also try using Wildcards.

image

If you are a Systems Engineer working with Windows, its time to start learning PowerShell. What are you waiting for?

PowerShell to get Windows Experience Index

I was just listening to Scott Hanselman’s podcast on building the “Ultimate Developers Machine Part 2.”  They are trying to build a machine with a WEI score of 7.9 with a budget of $3000. In the show, they mentioned that you can use WMI to get the WEI score.

Get-WmiObject Win32_WinSat

image

Hey Scripting Guy! Guest Blog Post

I had the pleasure of writing an article for the “Hey Scripting Guy!” Blog. In the article I describe how I wrote a function to change the screen resolution. I highly recommend checking out the blog.  Ed and the rest of the Scripting Guys generate a lot of great content.

NetApp releases PowerShell Toolkit 1.0

NetApp has just released the first version of their PowerShell Toolkit last week. Overall, I am really impressed with their implementation. They did a bunch of things right.

  1. It’s a PowerShell 2.0 Module
  2. Lots of great help files for every Cmdlet
  3. Full coverage of the ONTAP API
  4. Used “Approved” verbs
  5. 357 Cmdlets

About a year ago, NetApp released a .NET version of their API. Using their DLL, I was able to write a bunch of PowerShell 2.0 Advanced Functions to automate the 20 or so tasks that my team did with our NetApps on a weekly basis. Using the .NET API, I created a module with 20 or so Advanced Functions. So far, every one of them has been implemented in the new cmdlets. There are also a great deal of cmdlets that are extremely useful that I never got around to writing.

The ONTAPI .NET API exposed functionality with an object of type Netapp.Manage.NaElement, which is basically an XML Element. Essentially, everything in the Netapp API takes in XML and returns XML. PowerShell does a great job with XML and it was relatively easy to cast from XML to PSObjects as needed. However, the new PowerShell toolkit has a bunch of wrapper classes which seem to be a bunch of de-serialized NaElement Objects. This makes working with the objects much easier.

NetApp has really done a terrific job with their adoption of PowerShell. Again, I am particularly impressed with how well they adhered to the “ground rules” for working with PowerShell. In my opinion, these cmdlets are a great example for other third party companies that are looking to adopt PowerShell.

Now, this is of course version 1. There is always room for improvement. One of the first things I noticed is that there could have been some work done on the default formatting for some of the most basic objects that we use all the time, particularly luns and volumes, NetApp.Ontapi.Filer.Lun.LunInfo  NetApp.Ontapi.Filer.Volume.VolumeInfo respectively. Frankly, I like the default view to be a table with 4 or 5 critical columns. If something has to default to format-table, its too much information to process for me, but that’s my humble opinion.

Here’s an example

image

 

I think I would prefer to have a table view that just shows the name, sizeTotal, SizeUsed, and perhaps 1 or 2 other attributes. This seems more useful out of the box to me

image

NetApp filers are SAN’s. They are all about storage. They are built on a hierarchical model. Frankly, I think a PowerShell Provider could be a great fit for managing a NetApp filer. However, as Jeffrey Snover often says, to ship is to choose, and I wholeheartedly belive that the cmdlets are more useful than just a provider. But, if you have a provider and cmdlets, they are definitely better together. I love using a Provider for discovery purposes. But I think in general, cmdlets are more useful for actually executing tasks. I would love to see a complimentary PS Provider in version 2 of the Toolkit.

All that being said, I tip my hat to Netapp. Great job on version 1!

Enable-BlackISE

I have been asked a few times about the color scheme I use in PowerShell ISE. I can’t take credit for designing it. The work was done by David Mohundro. You can find his post at http://www.mohundro.com/blog/2008/12/31/PowerShellISEThemes.aspx.  Here is my version of the function. It simply uses the $psISE variable to mess with the token colors.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
Function Enable-BlackISE {   
    $psISE.Options.FontName = 'Consolas'
    $psISE.Options.FontSize = 14 
    $psISE.Options.OutputPaneBackgroundColor = '#FF000000'
    $psISE.Options.OutputPaneTextBackgroundColor = '#FF000000'
    $psISE.Options.OutputPaneForegroundColor = '#FFFFFFFF'
    $psISE.Options.CommandPaneBackgroundColor = '#FF000000'
    $psISE.Options.ScriptPaneBackgroundColor = '#FF000000'
    $psISE.Options.TokenColors['Command'] = '#FFFFFF60'
    $psISE.Options.TokenColors['Unknown'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['Member'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['Position'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['GroupEnd'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['GroupStart'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['LineContinuation'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['NewLine'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['StatementSeparator'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['Comment'] = '#FFAEAEAE'
    $psISE.Options.TokenColors['String'] = '#FF00D42D'
    $psISE.Options.TokenColors['Keyword'] = '#FFFFDE00'
    $psISE.Options.TokenColors['Attribute'] = '#FF84A7C1'
    $psISE.Options.TokenColors['Type'] = '#FF84A7C1'
    $psISE.Options.TokenColors['Variable'] = '#FF00D42D'
    $psISE.Options.TokenColors['CommandParameter'] = '#FFFFDE00'
    $psISE.Options.TokenColors['CommandArgument'] = '#FFFFFFFF'
    $psISE.Options.TokenColors['Number'] = '#FF98FE1E'
}

Have fun. It would be great to see what other kinds of themes people can come up with.

Pull PCI Bus information for Network Cards with PowerShell

Since its been about 3 months, I figured its about time for another post. A few weeks ago, a colleague of mine and I were discussing ways to identify which network cards were which when we build new Hyper-V cluster nodes. It can get a little nuts when you have 2 iSCSI networks, 1 Corp connection, and one connection that is tagged for VM’s on different VLAN’s, and a Heartbeat network.

We found that there is no real way to predict which NIC will be named “Local Area Connection”, “Local Area Connection 2”, Local Area Connection N …” and so on. However, poking around we did find that there is one thing in common across all of our servers. Assuming the NICS were all plugged into the switch in order, we could identify them by their PCI bus, device, and function ID as shown in the screenshot below.

image

Well that’s all good but how can we leverage that information and automate our network configuration for new cluster nodes. First, we need to come up with a standard. We know the PCI Bus information for each NIC in the server. Once we made that map, we can specify which NIC gets which IP Address.  Once you manually create that map, you still need a way to identify which NIC has what Bus information. Enter PowerShell.

This gets a little nuts because we have to piece this together from a couple different sources. The first bit of information that we need for each NIC is something called the PnPDeviceID. You can pull this wit WMI using the win32_networkAdapter class.

image

So what does this buy us. It turns out this is part of the path in the registry that stores the PCI bus information. Lets assume you store the PnPDeviced id in $deviceID. You can get the PCI Bus information with the following:

image

Those last 3 numbers that are circled in red are the BusID, DeviceID, and FunctionID. Note that this is for 2008 and 2008 R2. All I really need is those last 3 numbers so some regex trickery along with multi-variable assignment comes in pretty handy here.

image

So once we have that, we can put this all together in a couple functions

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
Function Get-NicBusFunctionID {
$adapters = get-wmiobject win32_networkadapter -filter "netenabled=true"

foreach ($adapter in $adapters) {
      
        $deviceId = $adapter.PnPDeviceID
        $locationInfo = (get-itemproperty -path "HKLM:\SYSTEM\CurrentControlSet\Enum\$deviceID" `
                                          -name locationinformation).locationINformation
       
        $businfo = Resolve-PCIBusInfo -locationInfo $locationinfo
       
        new-object psobject -property @{
            "Name"        = $adapter.NetConnectionID;
            "MacAddress"  = $adapter.MacAddress
            "Index"       = $adapter.Index;
            "PCIBusID"      = $businfo.BusID;
            "PCIDeviceID"   = $businfo.DeviceID;
            "PCIFunctionID" = $businfo.FunctionID
        }
}

}

Function Resolve-PCIBusInfo {

param (
[parameter(ValueFromPipeline=$true,Mandatory=$true)]
[string]
$locationInfo
)
PROCESS {
[void]($locationInfo -match  "\d,\d,\d")
$busId,$deviceID,$functionID = $matches[0] -split ","

new-object psobject -property @{
          "BusID" = $busID;
          "DeviceID" = "$deviceID"
          "FunctionID" = "$functionID"
          }
}        
}


And a screenshot of how to use it

image

Determining if IP addresses are on the same subnet

This is actually a fairly simple task that many people have done in many different languages. However, I thought it would be useful to share for the sake of showing how .NET can do a lot of tedious work for us.

IP Addresses are made up of 32 bits. Subnet masks are 32 bits as well. The subnet mask is what is used to determine where the Network ID ends and the host ID begins. Masks must have contiguous 1’s. If there is a 1 in the mask, then the corresponding bit in the IP Address is part of the Network ID. If there is 0 in the mask, the corresponding bit in the IP Address is part of the host ID.

So obviously there is going to be a lot of binary math, which any good network engineer can do. However, if you can get it for free, even better!

There is a type in .NET called System.Net.IPAddress that has several useful properties. The one I will focus on is Address.

image

Using the Address property and some bitwise operators in PowerShell, we can come up with a function pretty quickly. The –and and –or operators are pretty commonplace in the world of PowerShell but there is occasionally a need to do a compare of two numbers at the bit level. This is done with the –band and –bor operators. I may be off but if memory serves, using –bor and –band you can build any other bit function like xor. I won’t event think about K-maps.

So with all that, here is some code:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
Function Test-SameSubnet {
param (
[parameter(Mandatory=$true)]
[Net.IPAddress]
$ip1,

[parameter(Mandatory=$true)]
[Net.IPAddress]
$ip2,

[parameter()]
[alias("SubnetMask")]
[Net.IPAddress]
$mask ="255.255.255.0"
)

if (($ip1.address -band $mask.address) -eq ($ip2.address -band $mask.address)) {$true}
else {$false}

}

The Address property is the binary number that represents the IP Address. Doing a bitwise AND with a mask will tell us if they are in the same subnet. I have a default value of 255.255.255.0 for the mask, or /24 if you prefer CIDR notation, but you can specify it as a parameter if you like.

 image

Specifying Listener IP Address for PowerShell Remoting

I have a lot of servers that have more than one network interface. For example, in my Hyper-V cluster, we might have iSCSI NICs, Live Migration NICs, Heartbeat NICs, and Client Access NICs. When I enabled remoting I was not comfortable with WinRM listening on all of the IP addresses on my server. I really only wanted it to listen on 1 IP.

On my local machine, you can see that my listener is listening on any address it can find on my IP stack.

image

I’d like to set this to only listen only on a single IP V4 Address. When I first tried to change this I started looking at the value for Address under my listener but kept running into an error that reads Set-Item : Item has already been added. Key in dictionary: 'Address'  Key being added: 'Address'

Poking around the WSMAN provider, (which is fantastic by the way!) I found something else that looked promising. In WSMAN:\localhost\Service there are items called IPV4Filter and IPV6Filter.

image

Sweet!  Let’s try to set it to my local IP Address… and fail with this error

Set-Item : The WinRM client cannot process the request. The IP Filter is invalid. Ranges are specified using the syntax IP1-IP2. Multiple ranges are separated using , as delimiter. * is used to indicate that the service should listen on all available IPs on the machine. When * is used, other ranges in the filter are ignored. If filter is blank,
the service doesn't listen on any address. For example, if service should be restricted to listen on only IPv4 addresses, IPv6 filter should be left empty.

But this is great news. The error message is actually helpful – Christmas miracle maybe?

If you want to listen on a single IP Address, you can specify a range that starts and ends at the same IP. For example,

image

So why would you have to enter all these crazy ranges.? Well it turns out you can specify these in a GPO. Say you have a Hyper-V Cluster that has a client access network (10.10.10.0/24), an iSCSI network (10.11.11.0/24), and a few others for things like heartbeat and live migration.  If you only wanted to have a listener on the client access network for all of your cluster nodes, you could specify the IPv4Filter to be 10.10.10.1- 10.10.10.254 and the policy would apply to all our servers and they would not be listening on the 10.11.11.0/24 iSCSI network.

To configure GPO settings, you can go to Computer Configuration\Administrative Templates\Windows Components\WinRM Service and in there you will find a setting called “Allow automatic configuration of listeners”

image

PowerShell Advanced Functions

A while back I posted a screencast on using the ISE for debugging. It seems like a lot of people enjoyed the video so I thought I would do another one. As a result, here is a Screencast that discusses Advanced Functions in PowerShell. I hope you find it helpful.