Posts RSS Comments RSS 253 Posts and 399 Comments till now

Archive for the 'HowTo' Category

To Hex and Back (The Journey of an Integer)

This is so simple and I forget it every time. This time I am blogging it! (I apologize if the title has been taken before.)

From HEX to Integer
Start Value: 124f80

To convert to Integer we just add 0x and Powershell will do the rest

0x124f80
1200000

From Integer to Hex
Start Value: 1234567

To Convert to HEX we can use the -f operator

"{0:x}" -f 1234567
12d687

UPDATE!

Oisin pointed out these options as well

PS: (4545).tostring("x")
11c1

# or

PS: $n = 4545; $n.tostring("X")
11C1

The Power of LDAP Filters

A common problem when dealing with Active Directory is the end user trying to parse the results themselves.

Let take this example

$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = [ADSI]""
$selector.pagesize = 1000
$adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
foreach ($person in $adobj) {
   $date120DaysAgo = [DateTime]::Now.AddDays(-120).ToFileTime()
   $LL1 = $person.properties.lastlogontimestamp
   if(($LL1 -le $date120DaysAgo) -and ($person.GetDirectoryEntry().psbase.invokeget(‘AccountDisabled’))){$person}
}

Instead of doing the parsing on results side… we should let the server do the work. How do we do that?

With LDAP filters. Here is an example.

$date = (Get-Date).AddDays(-120).ToFileTime()
$filter = "(&(objectcategory=user)(userAccountControl:1.2.840.113556.1.4.803:=2)(lastlogontimestamp<=$date))"
$ds = New-Object DirectoryServices.DirectorySearcher([ADSI]"",$filter)
$ds.PageSize = 1000
$users = $ds.FindAll()
$users

Or with Quest tools… even easier!

PS: $date = (Get-Date).AddDays(-120).ToFileTime()
PS: $filter = "(&(objectcategory=user)(userAccountControl:1.2.840.113556.1.4.803:=2)(lastlogontimestamp<=$date))"
PS: Get-QADUser -LdapFilter $filter

I think you will find with an LDAP filter you can save a TON of time.

Here is the output of measure-command for the two examples above (this was a very small sample.)

Without Filter
————–
Days : 0
Hours : 0
Minutes : 0
Seconds : 3
Milliseconds : 477
Ticks : 34776670

With Filter
———–
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 34
Ticks : 340740

If you were to do this on a large AD the difference in time would be HUGE! Here is an example with 600K users…

With Filter
————
Days : 0
Hours : 0
Minutes : 0
Seconds : 17
Milliseconds : 353
Ticks : 173535605

I can’t post one with out filter… because it has been hours and it is still not done :)

Calculated Properties (UPDATE!)

I had a user point out that my code didnt work in a 4.5 farm. I did some testing and while some information was there, it was not complete. I did Test XP and 4.0 and they worked fine. I created a new one for CTX PS 4.5

$MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
$MF.Initialize(1)
$MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,DistinguishedName,
  @{n="AppName";e={$_.WinAppObject.BrowserName}},
  @{n=‘Users’; e={$_.LoadData(1) | %{$_.Accounts2} | ?{$_.AccountType -eq 2} | %{$_.AccountName}}},
  @{n=‘Groups’;e={$_.LoadData(1) | %{$_.Accounts2} | ?{$_.AccountType -eq 4} | %{$_.AccountName}}}

Lets talk about whats going on here

First We Get the MFCom Farm Object and Initialize it.

$MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
$MF.Initialize(1)

Next we iterate throught the servers parsing out only the ServerName and Getting MFCom Application Objects and pipe it along

$MF.Servers | Select-Object ServerName -expand Applications |

The Final part is where we make the changes. In 4.5 the Application object no longer has Users Property or Group Property. It also no longer uses AppName.
For AppName we use: $_.WinAppObject.BrowserName
For Users we use: Accounts2 with AccountType 2
For Groups we use: Accounts2 with AccountType 4
The User/Group Name is stored in a property called AccountName

You will also notice the LoadData. This because not all information is there be default. LoadData fills out the object with data.

 # Get the AppName from BrowserName
  @{n="AppName";e={$_.WinAppObject.BrowserName}},
  # Get users from Accounts2 filtering type 2 and selecting AccountName
  @{n=‘Users’; e={$_.LoadData(1) | %{$_.Accounts2} | ?{$_.AccountType -eq 2} | %{$_.AccountName}}},
  # Get Groups from Accounts2 filtering type 4 and selecting AccountName
  @{n=‘Groups’;e={$_.LoadData(1) | %{$_.Accounts2} | ?{$_.AccountType -eq 4} | %{$_.AccountName}}}

Calculated Properties

nicad49 asked
“Can you explain the last part of each script? Specifically the @{n=’Servers’;…}etc. I understand that we are creating some sort
of an array, but if I run it as is, I don’t get any values returned.”

This is great question. I see examples like this used all the time without explanation. Unless you had the chance to review all the
help files from all the different CMDLets there is good chance you missed some REALLY cool stuff. This is one of those things
hidden in the bowels of Help files and Books.

Before I go into my specific example, lets look at this excerp from http://technet.microsoft.com/en-us/library/bb978655.aspx

“You can also use Select-Object to add calculated properties to an object. To add a calculated property, use the Property
parameter and type a hash table for the parameter value. Create an Expression key in the hash table and assign to the key an
expression that calculates a value. The hash table can also have a Name key.”

Basically what we can do is add a property to an object that is calculated. This calculation could be from other properties or just a
change to the way the data is configured. Here is how you use this feature @{name=”SomeName”;expression={“Script block used
to provide value(s)”}}.

Some important things to note:
* The Name can be anything, even a valid property name.
* Expression is a scriptblock. What does this mean? It means that you can put any code you want in there… even an entire script
and what ever is returned is put in the property you created.

Now that we have some foundation lets look back at my code. Here is the first example.

$MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
$MF.Initialize(1)
$MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
  @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType

Now the first problem maybe cut/paste not working correct so make sure line three is all one big long line.

Next lets zoom in on the Important line… number three

$MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
  @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
  @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType

First we have the $MF.Servers. This returns an array of Citrix Server Objects with all sorts of properties. We only really care about
the Applications and the Server name so we go to the next step.

Select-Object ServerName -expand Applications. This code takes each one of the MFCom Server objects and Strips out everything
but the ServerName and the Applications. The reason for -expand is because Applications is an array of MFCom Application
Objects and we want them all.

Here is the biggy
Select-Object ServerName,AppName,DistinguishedName,@{n=’Users’;e={$_.Users | %{$_.UserName}}},@{n=’Groups’;e={$_.Groups | %{$_.GroupName}}}
This section takes the object we Created with the ServerName and the Applications and Passes on the ServerName, but extracts
information about the applications we want to know like AppName and DistinguishedName. The problem is that we want the users
and groups to. The problem with this is these properties are (like Server and Application) arrays of MFCom User or MFCom Group
objects. Enter calculated fields.

Lets look each calculated field
@{n=’Users’;e={$_.Users | %{$_.UserName}}}
This basically says create a property named Users and add the values from $_.Users, but only give me the UserName… not all the
other properties.
@{n=’Groups’;e={$_.Groups | %{$_.GroupName}}}
Same thing except for with Groups.

Perhaps multilevel Citrix Nesting isn’t the best place to get your teeth wet on calculated properties, but I hope I cleared it up a bit
for you.

An Interactive Case for Powershell (Yet more Citrix Fun!)

I was recently in a discussion on Brian Madden Forums about listing Citrix Information and exporting to CSV. It seemed like a
perfect fit for Powershell so I converted the VBScripts to Powershell (of course taking an 85 line script to 3 lines. Convert is
hardly the correct term.)

Here is the Forum Topic

http://www.brianmadden.com/Forum/Topic/95285

Here is my Code. There were three scripts, so I made three as well.

# Apps by Server CTXApps_by_Server_w_Users
$MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
$MF.Initialize(1)
$MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
       @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
       @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType
# Apps with Servers
$MF = New-Object -com MetaFrameCOM.MetaFrameFarm
$MF.Initialize(1)
$MF.Applications | Select-Object AppName,DistinguishedName,
      @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}} | export-Csv C:\AppsWithServer.Csv -noType
# Apps with Servers and Users CTXApps_w_Servers_w_Users
$MF = New-Object -com MetaFrameCOM.MetaFrameFarm
$MF.Initialize(1)
$MF.Applications | Select-Object AppName,DistinguishedName,
      @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}},
      @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
      @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsWithServerandUsers.Csv -noType

If you notice in my three scripts they all start with the same two lines. Effectively these are one liners that could be used
interactively. I think this does a great job of showing Citrix Admins how nicely Powershell will fit in to their daily lives.
Things that use to take 100s of lines of script writing can now be done interactively at a shell.

Format-XML (Journey to Pretty XML output)

I ran across a need to use Export-CliXML to produce human readable XML. Since, I never really cared what the output looked like so I let the blob go.

Now I care :)

I sent a message to the Powershell Dev Team and as always they are super speedy in response. (less than 1hr… that is INSANE.)

Enter Format-XML

http://blogs.msdn.com/powershell/archive/2008/01/18/format-xml.aspx

Help for Export-Clixml

http://technet.microsoft.com/en-us/library/bb978636.aspx

Thanks Again Jeffrey and Team!

p.s. I dont believe I ever said the output (Export-CliXML) looked like crap… just not Pretty :)

Converting Secure String

I recently heard a question about ConvertTO-SecureString and ConvertFrom-SecureString.

These CMDLets may be a little confusing so let’s talk a little about them.

Before you read the rest, it is key to understand what a Secure String actually is.

A Secure String is simple text that is encrypted in memory. This lets you store passwords or other secure data in memory without having to concern yourself with someone snooping your session or dumping your memory contents to get your data.

More Info Here

http://msdn2.microsoft.com/en-us/library/system.security.securestring.aspx

ConvertTo-SecureString: http://technet.microsoft.com/en-us/library/bb978707.aspx
I think this where it gets a little confusing for people. ConvertTo is meant to take an encrypted string and store it as a Secure String. Specificially for the output from ConvertFrom-SecureString. It will allow you use -asPlainText w/ -Force if you just want to convert a piece of text to Secure String.

ConvertFrom-SecureString: http://technet.microsoft.com/en-us/library/bb978629.aspx
This is used to convert a Secure String to text. It is import to note…. this is NOT the original text but the string representation of the encryption. This is a great tool for exporting the Secure String to a file in an encrypted form.

One other note before I show the code. By default (unless you provide a key) it uses Windows Data Protection API (DPAPI). This is VERY important. This process it is fairly secure, but can only decrypted by you on that specific machine. On the flip side… using a Key is not near as secure and some would argue security by obscurity.

Now… lets look at what we have… nothing magic here. We have three functions Export-EncryptedText. Import-EncryptedText, and Get-EncryptedText.
Export-EncryptedText: This gets a Secure String and exports to a file
Import-EncryptedText: This imports a Secure String Text from a file and returns a Secure String
Get-EncryptedText: This converts a Secure String into the orginal Text

We use “Read-Host -AsSecureString” to create convert our text to a secure string.

function Export-EncryptedText{
    param($text,$file,$key)
    if($key){ConvertFrom-SecureString -SecureString $text -key $key | out-file $file}
    else{ConvertFrom-SecureString -SecureString $text | out-file $file}
}
function Import-EncryptedText{
    Param($file,$key)
    $textFromFile = Get-Content $file
    if($key){ConvertTO-SecureString $TextFromFile -key $key}
    else{ConvertTO-SecureString $TextFromFile}
}
function Get-EncryptedText($text) {
    $Ptr = [System.Runtime.InteropServices.Marshal ]::SecureStringToCoTaskMemUnicode($text)
    $result = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
    $result
}

# Example Use
$dataToStore = Read-Host -AsSecureString
"My String To Encrypt Here"

# Key… needs to be a 16, 24, or 32 byte array
$key = (200..231)

# Use this to put the password in a file
Export-EncryptedText -text $dataToStore -file c:\data\testfile.secure -key $key

# To get the password back you do this
$myText = Import-EncryptedText c:\data\testfile.secure -key $key

# To see the text use Get-SecurePass
Get-EncryptedText $myText

AD Replication Metadata (when did that change?)

There was a discussion on the NG about determining when a user was disabled. The initial request was to determine this based on whenChanged, but I thought that could be invalid as you can easily change an account after it was disabled. I can not think of a way to be sure, but the best way I can think of is to use the replication metadata on the attribute userAccountControl (the second bit is what determines if its disabled or not.) While it is possible to change the useraccountcontrol after a user is disabled it is unlikely.

More info for UserAccountControl bits

http://support.microsoft.com/kb/305144

Of course the next question was how do you check the Replication Metadata for an attribute on and AD object?

Enter Get-ADObjectREplicationMetadata.ps1

This uses

System.DirectoryServices.ActiveDirectory.DirectoryContext
– http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectory.directorycontext.aspx
System.DirectoryServices.ActiveDirectory.DomainController
– http://msdn2.microsoft.com/en-gb/library/system.directoryservices.activedirectory.domaincontroller.aspx

# Get-ADObjectREplicationMetadata.ps1
# Brandon Shell (www.bsonposh.com)
# Purpose: Get attribute(s) Replication Metadata from a Domain controller.
Param($Domain,$objectDN,$property)
# Sets Context to Domain for System.DirectoryServices.ActiveDirectory.DomainController
$context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$domain)
# .NET Class that returns a Domain Controller for Specified Context
$dc = [System.DirectoryServices.ActiveDirectory.DomainController]::findOne($context)
# GetReplicationMetadata returns metadate from the DC for the DN specified.
$meta = $dc.GetReplicationMetadata($objectDN)
if($property){$meta | %{$_.$Property}}else{$meta}

This will return either all the metadata or just the metadata for a specific attribute. I should note that if you do not specify an attribute it returns all of them. You should expect to parse these as each attribute has a child object with the data in it.

All Attributes. The value can be found by .PropertyName

PS# .\Get-ADObjectMetaData.ps1 ‘my.lab.domain’ ‘CN=TestUser,DC=my,dc=lab,dc=domain’

Name                           Value
—-                           —–
countrycode                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
cn                             System.DirectoryServices.ActiveDirectory.AttributeMetadata
mail                           System.DirectoryServices.ActiveDirectory.AttributeMetadata
scriptpath                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
ntsecuritydescriptor           System.DirectoryServices.ActiveDirectory.AttributeMetadata
accountexpires                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
displayname                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
profilepath                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
primarygroupid                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
unicodepwd                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectclass                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectcategory                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
instancetype                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
homedrive                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
samaccounttype                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
homedirectory                  System.DirectoryServices.ActiveDirectory.AttributeMetadata
whencreated                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
useraccountcontrol             System.DirectoryServices.ActiveDirectory.AttributeMetadata
msmqsigncertificates           System.DirectoryServices.ActiveDirectory.AttributeMetadata
dbcspwd                        System.DirectoryServices.ActiveDirectory.AttributeMetadata
title                          System.DirectoryServices.ActiveDirectory.AttributeMetadata
samaccountname                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
supplementalcredentials        System.DirectoryServices.ActiveDirectory.AttributeMetadata
userparameters                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
givenname                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
description                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
lmpwdhistory                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
pwdlastset                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
msnpallowdialin                System.DirectoryServices.ActiveDirectory.AttributeMetadata
codepage                       System.DirectoryServices.ActiveDirectory.AttributeMetadata
name                           System.DirectoryServices.ActiveDirectory.AttributeMetadata
ntpwdhistory                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
userprincipalname              System.DirectoryServices.ActiveDirectory.AttributeMetadata
admincount                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectsid                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
sn                             System.DirectoryServices.ActiveDirectory.AttributeMetadata
msmqdigests                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
logonhours                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
lastlogontimestamp             System.DirectoryServices.ActiveDirectory.AttributeMetadata

Here is a specific Attribute

PS# .\Get-ADObjectMetaData.ps1 ‘my.lab.domain’ ‘CN=TestUser,DC=my,dc=lab,dc=domain’ ‘useraccountcontrol’

Name                        : userAccountControl
Version                     : 8
LastOriginatingChangeTime   : 9/15/2005 1:45:32 PM
LastOriginatingInvocationId : eeaeb6f9-8422-dddd-as34-04d7bd779285
OriginatingChangeUsn        : 47264036
LocalChangeUsn              : 49555172
OriginatingServer           : dc.my.lab.domain

Citrix MFCom Enums

One of the biggest problems I run into when writing Citrix MFCom Scripts I normally like finding examples (Vbscript mostly.) Because the COM Object model is very picky about what is passed and how it is passed, it helps knowing how and what the COM object is expecting.

Most of these examples use MFCOM enumerations in the initializing of the object. So I always find my self trying to figure out what the value is suppose to be because the ENUM is a pain to do in Powershell. I finally decided to let Visual Studio do my dirty work and figure out the Enums for me.

Here is the list (it is not complete, but it has most of them. I will add more as I find them.) If you find any missing let me know!

You can either use this as reference for the values or you cut/paste this code in a script and ‘.’ Source the Script in your Citrix Scripts and call the Enums directly.

# Window Type Enums
$MFWinWindowUnknown = 0
$MFWinWindow640X480 = 1
$MFWinWindow800X600 = 2
$MFWinWindow1024X768 = 3
$MFWinWindow1280X1024 = 4
$MFWinWindowCustom = 5
$MFWinWindowPercent = 6
$MFWinWindowFullScreen = 7
$MFWinWindow1600X1200 = 8

# Color Enums
$MFWinColorUnknown = 0
$MFWinColor16 = 1
$MFWinColor256 = 2
$MFWinColor64K = 3
$MFWinColor16M = 4

# Authentication Enums
$MFAccountAuthorityUnknown = 0
$MFAccountAuthorityNTDomain = 1
$MFAccountAuthorityNDS = 2
$MFAccountAuthorityADS = 3
$MFAccountTypeUnknown = 0
$MFAccountLocalUser = 1
$MFAccountDomainUser = 2
$MFAccountLocalGroup = 3
$MFAccountGlobalGroup = 4
$MFAccountUniversalGroup = 5
$MFAccountDomainLocalGroup = 6
$MFAccountFolder = 7

# Object Enums
$MetaFrameUnknownObject = 0
$MetaFrameWinFarmObject = 1
$MetaFrameZoneObject = 2
$MetaFrameWinAppObject = 3
$MetaFrameLicenseObject = 4
$MetaFrameAcctAuthObject = 5
$MetaFrameWinSrvObject = 6
$MetaFrameUserObject = 7
$MetaFrameGroupObject = 8
$MetaFrameProcessObject = 9
$MetaFrameSessionObject = 10
$MetaFrameChannelObject = 11
$MetaFrameAppFolder = 12
$MetaFrameSrvFolder = 13
$MetaFrameIMSAppObject = 14
$MetaFrameRMSAppObject = 15
$MetaFrameUnixAppObject = 16
$MetaFrameContentObject = 17
$MetaFrameFileTypeObject = 18
$MetaFrameUserPolicyObject = 19
$MetaFrameSessionPolicyObject = 19
$MetaFrameLicenseSetObject = 20
$MetaFrameLicenseNumberObject = 21
$MetaFrameAccountFolder = 22
$MetaFramePrinterObject = 23
$MetaFramePrinterDriverObject = 24
$MetaFrameAdminObject = 25
$MetaFrameMeObject = 26
$MetaFrameLoadEvaluatorObject = 27
$MetaFrameSessionPolFilterObject = 28
$MetaFrameAppliedPolicyObject = 29
$MetaFrameFileObject = 30
$MetaFrameIconObject = 31
$MetaFrameVIPRangeObject = 32
$MetaFrameLMRuleObject = 33
$MetaFrameAIEObject = 34
$MetaFrameAIEFolder = 35
$MetaFrameAIERuleObject = 36
$MetaFrameHotfixObject = 37
$MetaFrameStreamedAppObject = 38
$MetaFrameIMPackageObject = 39
$MetaFrameIMPackageGroup = 40
$MetaFrameServerGroup = 41
$MetaFrameMPFolder = 42
$MetaFrameIMConfigObject = 43
$MetaFrameIMJobObject = 44

Using Switch -RegEx to create Custom Object (Getting HBA Info)

The other day I had a need to collect HBA (Host Base Fiber Adapters) from all my Servers. So the first place I looked was at WMI, but unfortunately… no dice. It didnt have the information I needed. The only way I knew to get the info I needed was to use was HBACmd.exe (utility to collect HBA information remotely.) So I went to writing a wrapper script in powershell to call the exe and then grep the text for what I was looking for, but I thought… HEY! Thats not the powershell thing to do! We do objects not text. So I went to parsing the text and making an object out of it. The script below is the result and while I dont believe many of you will find it particularly useful as it has a VERY specific use, I wanted to share with you how I went about objectizing the output.

Here is a Little Q and A on the script

Q: Purpose?
A: To get all the HBA information include Type, Firmware, Bios, Target Lun, WWN and a slew of other stuff.

Q: Why Did I objectize my text?
A: Because I know can simply use properties to filter and output data. Like what Machines have what Bios and what type of HBAs they have. Before I would have the parse the text for every different senario… now I just use where-object and filter away.

I few things that I wanted to point out here are the use of Switch to create the Custom Objects. IMO, Switch is one of the most powerful commands in the Powershell Language. It is INSANELY Powerfull. To be honest, It is pretty much the only one you need.

To compare it “Select Case” in vbscript would be insulting, but it can peform a similar function Like

switch ($a){
     Value1    {"It was Value 1"}
     Value2    {"It was Value 2"}
     Value3    {"It was Value 3"}
     Value4    {"It was Value 4"}
}

It would take a whole series of post to completely cover switch, but for this one I only want to go over -regex use. For complete use read the help located:
PS> Get-help About_Switch # Read it, Learn it, Love it

Some Quick Notes about Switch
- Can use RegEx, WildCard, Exact, CaseSensitive, or File options.
- It takes input via Pipeline {expression} or File. The cool thing is the pipeline can be any expression that results in piped output.
- For each match it can perform any ScritpBlock use $_ as the current Item
- It performs EVERY match on each element unless you use Continue after a match to stop processing that record

Like I said, Switch is insanely powerful. Just one of those powers is using RegEx for comparison.

Here is an example of using the -RegEx option

switch -RegEx (Get-ChildItem C:\test)
{
    "^\d"                  {"Starts with number:          " + $_.FullName}
    "\d"                   {"Has a number in it:          " + $_.FullName}
    "[^A-Za-z]"            {"Does NOT start with Number:  " + $_.FullName}
    "tmp"                  {"Has ‘tmp’ in it:             " + $_.FullName}
    # Notice that you can even use and expression to match
    {$_.Mode -match "-a"}  {"Has Archive Bit Set:         " + $_.FullName}
}

Now.. lets look at the script below. You will noticed I used RegEx to decide what value gets put in to what property of the object.

The script converts the output of three commands into two different objects. Lets look at one of them

It takes text like This

Manageable HBA List

Port WWN   : 10:00:00:00:11:11:11:11
Node WWN   : 20:00:00:00:11:11:11:11
Fabric Name: 00:00:00:00:00:00:00:00
Flags      : 0000f0a5
Host Name  : Server1
Mfg        : Emulex Corporation

Port WWN   : 10:00:00:00:22:22:22:22
Node WWN   : 20:00:00:00:22:22:22:22
Fabric Name: 00:00:00:00:00:00:00:00
Flags      : 0000f0a5
Host Name  : Server1
Mfg        : Emulex Corporation

And converts Into an object like This

  TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
—-        ———-   ———-
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
ToString    Method       System.String ToString()
Fabric      NoteProperty System.String Fabric=00:00:00:00:00:00:00:00
Flags       NoteProperty System.String Flags=0000f0a5
HBADetail   NoteProperty System.Object[] HBADetail=System.Object[]
Host        NoteProperty System.String Host=Server1
LUN         NoteProperty System.String LUN=01
MFG         NoteProperty System.String MFG=Emulex Corporation
NodeWWN     NoteProperty System.String NodeWWN=20:00:00:00:11:11:11:11
PortWWN     NoteProperty System.String PortWWN=10:00:00:00:11:11:11:11

Here is the Script

Param($List,$HostName,[switch]$FullDetail,[switch]$Verbose)
Begin{
    $erroractionpreference = "SilentlyContinue"
    $HBACMDPath = "<path To HbaCmd.exe>"
    function CreateHBAListObj{
        Param($srv)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" ListHBAs
        foreach($item in $result)
        {
            $parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
            switch -regex ($parsd[0])
            {
                "^Port" {
                            $myobj = "" | Select-Object Host,PortWWN,NodeWWN,Fabric,Flags,LUN,MFG
                            $myobj.PortWWN = $parsd[1]
                            $myobj.Lun = GetTargetLun $srv $myobj.PortWWN
                        }
                "^Node" {$myobj.NodeWWN = $parsd[1]}
                "^Fabr" {$myobj.Fabric  = $parsd[1]}
                "^Flag" {$myobj.Flags   = $parsd[1]}
                "^Host" {$myobj.Host    = $parsd[1]}
                "^MFG " {
                            $myobj.MFG     = $parsd[1]
                            $objCol += $myObj
                        }
            }
        }
        $objCol
    }
    function CreateHBAInfoObj{
        Param($srv,$wwn)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" HBAAttrib $wwn

        $myobj = "" |Select-Object Host,MFG,SN,Model,ModelDesc,NodeWWN,NodeSymname,
                                   HWVersion,ROM,FW,VenderID,Ports,DriverName,DeviceID,HBAType,
                                   OpFW,SLT1FW,SLT2FW,IEEEAddress,BootBios,DriverVer,KernelVer
        foreach($item in $result)
        {
            $parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
            switch -regex ($parsd[0])
            {
                "^Host"         {$myobj.Host         = $parsd[1]}
                "^Manufacturer" {$myobj.MFG          = $parsd[1]}
                "^Serial"       {$myobj.Sn           = $parsd[1]}
                "^Model  "      {$myobj.Model        = $parsd[1]}
                "^Model Desc"   {$myobj.ModelDesc    = $parsd[1]}
                "^Node WWN "    {$myobj.NodeWWN      = $parsd[1]}
                "^Node Symname" {$myobj.NodeSymname  = $parsd[1]}
                "^HW"           {$myobj.HWVersion    = $parsd[1]}
                "^Opt"          {$myobj.ROM          = $parsd[1]}
                "^FW"           {$myobj.FW           = $parsd[1]}
                "^Vender"       {$myobj.VenderID     = $parsd[1]}
                "^Number"       {$myobj.Ports        = $parsd[1]}
                "^Driver Name"  {$myobj.DriverName   = $parsd[1]}
                "^Device"       {$myobj.DeviceID     = $parsd[1]}
                "^HBA Type"     {$myobj.HBAType      = $parsd[1]}
                "^Operational"  {$myobj.OpFW         = $parsd[1]}
                "^SLI1 FW"      {$myobj.SLT1FW       = $parsd[1]}
                "^SLI2 FW"      {$myobj.SLT2FW       = $parsd[1]}
                "^IEEE"         {$myobj.IEEEAddress  = $parsd[1]}
                "^Boot "        {$myobj.BootBios     = $parsd[1]}
                "^Driver Ver"   {$myobj.DriverVer    = $parsd[1]}
                "^Kernel "      {$myobj.KernelVer    = $parsd[1]
                                 $objCol += $myObj}
            }
        }
        $objCol
    }
    function GetTargetLun{
        Param($srv,$wwn)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" TargetMapping $wwn
        [int]$lun = 0
        switch -regex ($result)
        {
            "^SCSI OS Lun" {$lun = $_.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)[1].trim()}
        }
        "{0:x}" -f $lun
    }
    function Ping-Server {
        Param([string]$srv)
        if($srv -eq ""){return $false}
        $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
        if($pingresult.statuscode -eq 0) {$true} else {$false}
    }
    Write-Host
    if($verbose){$VerbosePreference = "Continue"}
}
Process{
    if($_)
    {
        Write-Host "Getting HBA Info from $_"
        if($FullDetail)
        {
            $MyObject = CreateHBAListObj $_
            $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
            $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
            $MyObject
        }
        else
        {
            $MyObject = CreateHBAListObj $_
            $MyObject
        }
    }
}
End{
    if($list)
    {
        $servers = Get-Content $list
        Write-Host "Running HBA Check against Servers in $list"
        foreach($server in $servers)
        {
            if($server -ne "")
            {
                if(ping-server $server)
                {
                    Write-Host "Getting HBA Info from $server"
                    if($FullDetail)
                    {
                        $MyObject = CreateHBAListObj $server
                        $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
                        $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
                        $MyObject
                    }
                    else
                    {
                        $MyObject = CreateHBAListObj $server
                        $MyObject
                    }
                }
                else
                {
                    Write-Host "$Server not Pingable `n" -foregroundcolor RED
                }
            }
        }
    }
    if($HostName)
    {
        Write-Host "Running HBA Check against Servers in $HostName"
        if(ping-server $HostName)
        {
            Write-Host "Getting HBA Info from $HostName"
            if($FullDetail)
            {
                $MyObject = CreateHBAListObj $HostName
                $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
                $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
                $MyObject
            }
            else
            {
                $MyObject = CreateHBAListObj $HostName
                $MyObject
            }
        }
        else
        {
            Write-Host "$HostName not Pingable `n" -foregroundcolor RED
        }
    }
    Write-Host
}

« Prev - Next »