Posts RSS Comments RSS 253 Posts and 411 Comments till now

Blogging from iPad

Trying out the new iPad app for WordPress. Typing is not perfect but it is not to bad either.

For those of you wondering I got an iPad because of the Citrix receiver. It make the device priceless. IMO… Everyone should have one.

List of All Domain Controllers in Your Domain

The Scripting guys have a post over here: Hey, Scripting Guy! Can I Obtain a List of All Domain Controllers in My Domain?

That is one way but IMO this is easier and more DC specific data

[DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().DomainControllers

On a side note: The AD Replication Module will have a cmdlet so you will be able to just do this:

Get-ADRDomainController -domain MyDomain

Serverless binding with ADSI

joe and I were discussing this yesturday and he blog about it here So I used Serverless Binding with ADSI (or .NET), now what DC am I talking to?

On a side note… I think this is bad form. IMO you should be very deliberate with your code even with discovery. I would use rootDSE to do the discovery then stick with that server.

blog: Creating Custom Objects with “Select-Object”

Once you grab a hold of the "Object" concept in Powershell it brings a whole new light to scripting. The power that is at your finger tips is basically limitless, but to harness this power you not only need to know how to use objects but also create them as well.

Here is my way of creating objects.
  1. $myobj = ""| select-Object Server,Result
IMO this is the simplest way to create custom objects.

Effectively what happens is you set your $myobj to a empty string and pipe to select-object. While this may seem odd, its actually quite useful because what select-object returns is a PSCustomObject with the properties that you specify. This allows you to create you object with defined properties that you can fill out later in one line.

The "proper" way to create a custom object is to do this.
  1. $myobj = new-object System.Object
  2. $myobj | add-member -membertype noteproperty -name Server -value $sname
  3. $myobj | add-member -membertype noteproperty -name Result -value $sResult
While this isn't that much more work... I think its harder to read (and more typing) so I go with this shortcut
  1. $myobj = "" | select-Object Server,Result
  2. $myobj.Server = $sname
  3. $myobj.Result = $sResult
I will give you one warning about select-object (not related, but while I'm on this subject.) select-object does Change the object type and therefore you lose methods and properties you may expect to be there. I have seen numerous post on the news groups that have this exact problem.

If you pipe Get-ChildItem to Get-member you get tons of methods and properties for both System.IO.FileInfo and System.IO.DirectoryInfo
  1. PS> get-childitem | gm
Now pipe it to select-object and do a Get-Member
  1. PS> (get-childitem | select-object Fullname,length) | gm
  2.  
  3. TypeName: System.Management.Automation.PSCustomObject
  4.  
  5. Name MemberType Definition
  6. ---- ---------- ----------
  7. Equals Method System.Boolean Equals(Object obj)
  8. GetHashCode Method System.Int32 GetHashCode()
  9. GetType Method System.Type GetType()
  10. ToString Method System.String ToString()
  11. FullName NoteProperty System.String FullName=C:WindowsSystem32409
  12. length NoteProperty length=null
Just be careful to remember this. I actually avoid using select-object to filter objects simply because of this. Instead... I just use the properties.

Dealing with Parameters in Powershell

I often get asked to review code for people and I often see them use $args for argument parsing (most often with VBScript converts.) While there is nothing wrong with that method, I do not believe it is the best way. Powershell has numerous ways to pass data to scripts/functions. Today, after helping a friend understand the differences between them, I decided it would be good to blog about it.


Let’s take a look at the different ways scripts/functions can take input.



You can pass data by position parameters:

.\myscript.ps1 filename.txt corp.lab

You can pass data by named parameters:

.\myscript.ps1 -list filename.txt -domain corp.lab

Finally, you can pipe data in:

get-content filename.txt | .\myscript.ps1 -domain corp.lab


That is great… but what would the code look like foreach of these?


There is little difference between a script and a function so I will illustrate using functions.
To use positional parameters

Example: PassByPosition filename.txt corp.lab

function PassByPosition{
  "FileName: {0}" -f $args[0]
  "Domain: {0}" -f $args[1]
}



To process Named parameters you use the Param() statement included in Powershell

Example: PassByName -list filename.txt -domain corp.lab

function PassByName{
    Param($FileName,$DomainName)
    "FileName: {0}" -f $FileName
    "Domain: {0}" -f $DomainName
}



To process piped data you can do something like

Example: get-content filename.txt | PassByPipe corp.lab

function PassByPipe{
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $args[0]
        }
    }
}

NOTE: This is not only way to process piped input, but it is the simplest example. If you would like to see a more efficient way to process look HERE.


It gets REALY cool when using them together


Using the script below you can do any of these 

UseAllThree filename.txt corp.lab
UseAllThree -list filename.txt -domain corp.lab
get-content filename.txt | UseAllThree -domain corp.lab

function UseAllThree{
    Param($FileName,$DomainName)
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $args[0]
        }
    }
    else
    {
        "FileName: {0}" -f $FileName
        "Domain: {0}" -f $DomainName
    }
}

 

We can even have default values for the Parameters


Using the script below you can do any of these 

UseAllThreewithDefaults filename.txt
UseAllThreewithDefaults -list filename.txt
get-content filename.txt | UseAllThreewithDefaults

function UseAllThreewithDefaults{
    Param($FileName = "FileName.txt",$DomainName = "Corp.lab")
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $DomainName
        }
    }
    else
    {
        "FileName: {0}" -f $FileName
        "Domain: {0}" -f $DomainName
    }
}

Multiple Paths to the same End (Citrix)

Today I was asked this question

“Is there a more efficient way to list each published app and list out servers that are associated with it? In hope to give me an output that will display the ‘AppName’ objects once and display all the servers.”

He was referring to script I have posted somewhere (/shrug.)

I knew exactly what he wanted and thought “HEY! This would be a good time to blog about MFCom object Nesting.”

What do I mean by “MFCom object Nesting?” I simply mean that MFCom object model is nested in such a way that you can attain the same information from various code paths. For example, Farm objects contain Application objects which contain Server objects, but Farm objects also have Server objects which contain Application objects.

Farm -> Applications -> Servers
Or
Farm -> Servers -> Applications

Why is this important or why would Citrix do this? IMO, it is to provide a shorter code path for the information you want.

Here are some examples

In the case of the question I received, what he had required getting all the applications from each server and returning the information like this:

$mfarm.Servers | Select ServerName -expandproperty Applications | Select ServerName,
                                                                          AppName,
                                                                          DistinguishedName,
                                                                          @{n="Groups";e={$_.Groups | %{$_.GroupName}}}

What he wanted to get was all the applications and output the information directly like:

$mfarm.Applications | %{$_.LoadData(1);$_} | Select-Object AppName,
                                                           DistinguishedName,
                                                           @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}},
                                                           @{n=‘Servers’;e={$_.Servers | %{$_.ServerName}}}

 
Perhaps a more direct example would be collecting Sessions. If I want all the Sessions for a Server I could do one of the following:

$farm.Sessions | ?{$_.ServerNamematch "Server1"}

this works, but has to touch all my sessions… a better way would be like:

Get-CitrixServer  "Server1" | %{$_.Sessions}

 
More Info on LoadData()
If you are not familiar with the LoadData() method on the Application object it is critical that you get familiar, and get familiar quick.

LoadData() was introduced in MPS 4.0 (I believe) to allow the farm (or directly instantiated Application objects) to be returned with just a small bit of information. This allows you to quickly get basic application information without having to collect the entire data set, saving both time and network traffic. The problem is that most people do not know this and it can get quiet messy.

Why is it messy? If you get an application and set a property (like adding a user) and then commit the data back to the server using the SaveData() method, what do you think will happen? One could expect it would only update that which has change, but one would be wrong. When you call SaveData() it actually commits whatever is in local memory to the farm (yup, you got it… HOSED!) You just committed an effectively empty application and set it back to the Farm wiping out any existing information.

Moral of the story? USE LoadData().

joe’s Response to my perf testing.

I really appreciate him taking the time to post. He didn’t have to. It can be found HERE

A couple of things here:

1) I don’t want to pretend to be a software developer. I have not the first clue when it comes to writing apps. I am just now learning how data types affect performance. It is quite a leap from scripter to developer. My point here is that adfind is an app that has grown over time and has what is called “feature creep.” Most of the features were not intended and joe has just squeezed them in. I have no doubt if joe wanted to he could re-write adfind in a way that would absolutely blow my little feature lacking script away.

2) My point in this exercise is that when it comes to AD and Perf is concern there is a potential option out there if you do not mind the extra work.

3) I respect joe not only for his developer skills but as a person with a ton of knowledge about AD and a willingness to share that knowledge. joe does not advertise on his site and he gives all his tools away for free. He doesnt ask for anything in return.

4) My testing is not over. IMO for this to be a real success I need to be able to achieve similar performance returning objects… I mean that is the whole point right?

5) I will be working with Darren
(GPOGuy) from SDMSoftware to produce a CMDLet version of my script with a ton more features so watch his site.

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
}

How would you do it (AD Convergance?)

I think its great how you can do so many things so differently in Powershell. So here is the Challenge.

Goal is to check Active Directory replication convergance?

Conditions:
1) No third Party tools/snapins (only whats there by defualt)
2) No Creating Objects in AD
3) Must work in Large Environments
4) It needs to be fast.

Optional:
1) Return the Up To Dateness Vector Table
2) Have a monitor mode where it loops
3) Power Gadget Snap-in for RESULTS ONLY!

UPDATE:
IMO the only way to do this is to get this information is the to get the
HighestOrginatingUSN for a server and then check the UTD Vector on each
server to see if it is greater than the HighestOrginatingUSN. The only
problem with this approach is time… when done serially… this takes
awhile which makes it useable in large environments. I am investigating
runspaces to achieve this.

Using Aliases in Scripts or in your Examples.

Richard has a post on his blog about this The Over Use of Aliases?

I dont like duplicating other peoples post, but I think this one is worth it.

If you are an experienced Powershell person and you provide help or scripts to people. PLEASE do not use aliases. It makes it very hard for people. I am guilty myself. I mean gc % ? gm are SO tempting, but it really doesn’t help the person learn (it may even hinder.) IMO, using aliases can confuse newcomers… remember to them when you type gc… they think it is gc.exe not get-content. Peoples first impression is key… lets not make it a confusing one 🙂

Basically, I think aliases are best use for interactive use, but should be avoided in scripts or sample code.

p.s. COMMENT… COMMENT… COMMENT… Comments in code help a ton for people learning.

UPDATE:
Jeffrey brought up a good point that I over looked. Numerous Powershell IDE’s like PrimalScript and Powershell Analzyer automatically expand built in aliases. I find this very useful. I will be writing another post about IDE’s and why I think they are VERY important.

Next »