Author Archive

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

Comments 3 Comments »

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.

Comments No Comments »

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!

Comments No Comments »

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.

Comments 3 Comments »

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

Comments 1 Comment »

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

Comments 1 Comment »

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

Comments No Comments »

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.

Comments 3 Comments »

I am happy to announce that the Seattle PowerShell Script Club will be meeting on Thursday, Nov 19th at 818 Stewart St, Seattle WA from 7:00 PM to 9:00 PM and a good chance of heading out for beers when we’re done.

James Brundage has released his Windows PowerShell Pack. James and I thought we would start off with a couple examples of how to use WPK to create rich WPF based visualizations of data that is collected with PowerShell.  From there, we can go pretty much anywhere you want to.

Please leave a comment if you plan on attending. Please Register Here

Hope to see you there!

Andy

Comments 2 Comments »

I have been doing a lot of work with clustering Hyper-V lately and our storage is all iSCSI. Without going into all the gory details, iSCSI supports multipathing. My iSCSI SAN has two heads, each head has 3 NIC’s. On my Hyper-V hosts, we have two NIC’s that are used for iSCSI. Each NIC has a “target” for each IP on each head. Some quick math and you realize that’s a lot of connections, and a pain the butt to configure when you have a ton of nodes using this configuration. So naturally I started looking at scripting and didn’t find any cmdlets to work with iSCSI. However, there is a tool called ISCSICLI.

To add a an iSCSI target you can use the QAddTargetPortal command. Assuming your SAN’s iSCSI IP address is 192.168.168.10 you would use this command

iscsicli QAddTargetPortal 192.168.168.10

That’s fine, but what if you want to specify a source IP Address. This is pretty straight forward using the iSCSI Control Panel. In advanced settings you can choose both the “Microsoft iSCSI initiator” and the Initiator IP.

image

It turns out QAddTarget is “Quick Add Target” which gives you very few options. You can also use AddTargetPortal.

image

So there are a few things you should know. TargetPortalAddress is the IP address of the SAN you are connecting to. TargetPortalSocket is not really the socket, but just the port number, which is typically 3260. Normally a socket consists of an IP Address and a port number. The HBA name actually refers to Initiator Adapter. In this case it would be “Microsoft iSCSI Initiator.” Finally we get to the Port Number. It turns out that this port number is what is mapped to a source IP Address. The problem was that I could not find any documentation telling me how this port number was associated with an IP address on the server. At first I thought it might be the index of an interface, but quickly learned that was not the case, as you could have multiple IP’s on one interface anyway.

When something doesn’t show up in the GUI, I go exploring with WMI and PowerShell. It turns out there is a ton of iSCSI info buried in the root\wmi namespace

image

Looking around I found a msIScsi_portalinfoclass.

image

The PortalInformation property looked like it had some good info in it as well so I went digging into that.

image

Sweet! I see IPAddr and a port number. This looks promising. But when I crack open IPAddr I don’t see what I was expecting to see

image

Seriously.. since when is 1865687562 an IP V4 address? I was so close and then remembered that there is an IPAddress type in System.Net. Let’s just test something out here.

image

Woo Hoo! Given this I can now write my Get-IscsiPortNumber function in PowerShell.

001
002
003
004
005
006
007
Function Get-IscsiPortNumber {
$query = "select portalinformation from MSiSCSI_PortalInfoClass" 
$portalInfo = get-wmiobject -namespace root\wmi -query $query
$eScriptBlock ={([Net.IPAddress]$_.ipaddr.IPV4Address).IPAddressToString}
$customLabel = @{Label="IpAddress"; expression = $eScriptBlock}
$portalInfo.portalInformation | select port,$customlabel
}

Now that we have that info, we can use it in our iscsicli command

iscsicli addtargetportal 192.168.168.10 3260 ROOT\ISCSIPRT\0000_0 2 * * * * * * * * * *

Oh yeah, the ROOT\ISCSIPRT\0000_0 means MS Iscsi Initiator. There are some login flags as well that you would probably want to set for multipath as well.

image

So if your port number is 2 the final command would be

iscsicli addtargetportal 192.168.168.10 3260 ROOT\ISCSIPRT\0000_0 2 * 0x2 * * * * * * * *

Comments 3 Comments »