Posts RSS Comments RSS 244 Posts and 356 Comments till now

Archive for August, 2008

Moving on up… (Powershell #15 on TIOBE Index)

Tiobe TIOBE Programming Community Index for August 2008

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().

Tracing LDAP calls with Powershell

Spat had an eerily coincidental blog post the other day (HERE). The reason I say eerily is because the night before I was fighting trying to get a LDAP trace, this trace was to help figure out EXACTLY how SDS.ActiveDirectory got replication cursors from a Domain Controller (another joe Richards discussion.) Anyway, I digress, I found the blog entry EXTREMELY useful as it allowed me to get what I needed. I proceeded to leave a comment suggesting that this looked like a good job for Powershell as the resulting file from the tool is a CSV. This has led to a “challenge” from Spat and this is my response. I hope I did it justice.

Useful Links about Tracelog.exe
———–
Details about TraceLog.exe
LDAP tracing with TraceLog
ADSI tracing with TraceLog

Details about Script

Here are the functions in the script

Trace-Log
-flag: Hex value for the flags you want to pass (Default Value = “0×1FFFDFF3″)
-guid: GUID or File for tracing (Default Value = “LDAP”)
-SessionName: Unique Name for the actual trace (Default Value = “mytrace”)
-exe: The full name with extension of the EXE to add to registry to enable tracing. This only has to be done the first time you want to trace for an EXE.
[switch]Start: If set it enables logging. If not set, logging is disabled.
[switch]ADSI: If set it passes the ADSI GUID for tracing
[switch]LDAP: If set it passes the ADSI GUID for tracing

Convert-TraceLog
-Source: Trace (etl) file to convert to csv (Default Value = “.\ldap.etl”)
-file: File to set the results to (Default Value = “TraceResults.csv”)
[switch]$import: If set it will return a custom object with results

Below is a video that shows a demo of the script in use. I hope to do another one of these showing how to trace ADSI as well as LDAP. Make sure to read the Comments in Green. I tried to allow enough time. You can click to pause.

Download Tracelog Transcript (right click | Save Target As…)
Best Viewed Full Screen

Get the Flash Player to see this content.

Code
Download Trace Log Functions (right click | Save Target As…)

function Trace-Log {
    Param($file = ".\ldap.etl",
        $flag = 0×1FFFDFF3,
        $guid = "LDAP",
        $SessionName = "mytrace",
        $exe,
        [switch]$start,
        [switch]$ADSI,
        [switch]$LDAP
    )
    if($ADSI){$guid = "ADSI"}
    switch -exact ($guid)
    {
        "LDAP"  {$myguid = ‘#099614a5-5dd7-4788-8bc9-e29f43db28fc’}
        "ADSI"  {$myguid = ‘#7288c9f8-d63c-4932-a345-89d6b060174d’}
        Default {$myguid = "’$_’"}
    }
   
    Write-Host
   
    if($start)
    {
        Write-Host " Action: Start" -fore Yellow
        Write-Host " GUID:   $GUID" -fore Yellow
        Write-Host " File:   $file" -fore Yellow
        Write-Host " Flag:   $flag" -fore Yellow
        if($exe){Write-Host " Exe:    $exe" -fore Yellow}
       
    }
    else
    {
        Write-Host " State: Disabled" -fore Red
    }
   
    Write-Host
   
    if(!(test-Path "HKLM:\System\CurrentControlSet\Services\ldap\tracing\$exe") -and $exe)
    {
        new-Item -path "HKLM:\System\CurrentControlSet\Services\ldap\tracing" -name $exe | out-Null
    }
   
    if($start)
    {
        $cmd = "Tracelog.exe -start ‘$SessionName’ -f ‘$file’ -flag ‘$flag’ -guid ‘$myguid’"
    }
    else
    {
        $cmd = "tracelog -stop $SessionName"
    }
   
    Write-Host
    Write-Host "==========================" -fore White -back black
    write-Host "Running Command:" -fore White
    Write-Host " ==> $cmd" -fore Yellow
    invoke-Expression $cmd
    Write-Host "==========================" -fore White -back black
    Write-Host
}
function Convert-TraceFile{
    Param($Source=".\ldap.etl",$file="TraceResults.csv",[switch]$import)
   
    $cmd = "tracerpt.exe $Source -o $file -of CSV -y"
   
    invoke-Expression $cmd
   
    if($import)
    {
        import-Csv $file
    }
}

For those of you missing it…

There is a lively and useful discussion going on in the “Why use foreach vs foreach-object” comment thread.

Please feel free to join in :)
Why use foreach vs foreach-object.

PowerScripting Podcast – Episode 37

Have a listen
PowerScripting Podcast

Why use foreach vs foreach-object.

I have been ask several times why I use the foreach statement instead of the foreach-object cmdlet. The simple answer is performance.

First it is important to understand that foreach -ne foreach-object unless used in the pipeline. I know that sounds confusing so I will try to explain.

foreach-object: This is a looping cmdlet that processes (executes the script block) in the pipeline and uses $_ to reference the current item.
Syntax:

$objectCollection | foreach-object{"Do something to $_"}

foreach statement: This is a looping statement that process each item in a collection ($objectCollection) and perform the script block referencing each item as declared ($obj.)
Syntax:

foreach($obj in $objectCollection){"Do Something to $obj"}

Rules:
- foreach is considered to be foreach statement if it begins the line. If you want to use foreach-object you must use the fullname.
Example:

foreach-objectinput $objectCollection {"Do Something to $_"}
foreach($obj in $objectCollection){"Do Something to $obj"}

- foreach is considerd to be foreach-object if it used in the pipe.

Correct: $objectCollection | foreach-object{"Do Something to $_"}
Wrong: $objectCollection | foreach($obj in $objectCollection){"Do Something $obj"}

- % and foreach are alias to foreach-object

More Detail on performance:
The reason “foreach(){}” is faster is because it compiles into a single expression tree which gets evaluated in a single function call.

While, foreach-object is effectively compiled into three expression trees: For example get-childitem | foreach-object { $_.Name }

  • Get the value to pipe. Get-Childitem
  • Call the Foreach-Object. foreach-object
  • One for the ScriptBlock. {$_.Name}
  • Here is some sample code to show the performance Difference

    70# $ds = 1..50000
    71# measure-command {$ds | %{$_*9834} }

    Days              : 0
    Hours             : 0
    Minutes           : 0
    Seconds           : 16
    Milliseconds      : 551
    Ticks             : 165517175
    TotalDays         : 0.000191570804398148
    TotalHours        : 0.00459769930555556
    TotalMinutes      : 0.275861958333333
    TotalSeconds      : 16.5517175
    TotalMilliseconds : 16551.7175

    72# measure-command {foreach($item in $ds){$item*9834}}

    Days              : 0
    Hours             : 0
    Minutes           : 0
    Seconds           : 0
    Milliseconds      : 734
    Ticks             : 7341052
    TotalDays         : 8.49658796296296E-06
    TotalHours        : 0.000203918111111111
    TotalMinutes      : 0.0122350866666667
    TotalSeconds      : 0.7341052
    TotalMilliseconds : 734.1052

    Using Conditional Operators with “-not(match|like)”

    I recently answered a post on PoshComm about the use of -or and -and. There was a little confusion on the expected results as this gets especially confusing when using ‘-not’, ‘-notmatch’, or ‘-notlike’. It is effectively a difference between “Include ALL but x” vs “Exclude ALL but x.”

    The key to understanding how ‘-or’ and ‘-and’ will work is understanding that everything evaluates to $true or $false and this value is returned for EVERY condition check independantly. Working from the inside (left to right) to the outside (left to right.)

    Here is an example of outter and inner conditions.

    # Outter Condition 1 (-or’s the return of "inner Condition 1" and the return of "inner Condition 2")
    where(
       # inner Condition 1 (Processed First)
       ($description -notmatch "TestOne")
       -or
       # inner Condition 2 (Processed Second)
       ($description -notmatch "AnotherTest")
    )

    Below is a some working code that will illustrate the point.
    The goal is to find all the descriptions that do not match ‘TestOne’ or ‘AnotherTest’.

    # Setup a Variable to Test against
    $myDescriptions = @("My Description has an ‘TestOne’ in it")
    1..2 | %{$myDescriptions += "Really descriptive ’s$_’ description"}
    $myDescriptions += "My Description has an ‘AnotherTest’ in it"
    foreach($description in $myDescriptions)
    {
        $matchTestOne           = $description  -notmatch "TestOne"
        $matchAnotherTest       = $description  -notmatch "AnotherTest"
        $UsingOr                = ($description -notmatch "TestOne") -or ($description -notmatch "AnotherTest")
        $UsingNotMatchCorrectly = $description  -notmatch "(TestOne|AnotherTest)"
        "Value:                    $description"
        "Match One:                $matchTestOne"
        "Match Two:                $matchAnotherTest"
        "The Or:                   $UsingOr"  
        "Using NotMatch Correctly: $UsingNotMatchCorrectly"
        Write-Host
    }

    Here is the output from the above code. Notice that $UsingOr does not return as the one might expected. This is because every description will pass at least one of the conditions and therefore the entire condition will return $true.

    Value:                    My Description has an ‘TestOne’ in it
    Match One:                False
    Match Two:                True
    The Or:                   True
    Using NotMatch Correctly: False

    Value:                    Really descriptive ’s1′ description
    Match One:                True
    Match Two:                True
    The Or:                   True
    Using NotMatch Correctly: True

    Value:                    Really descriptive ’s2′ description
    Match One:                True
    Match Two:                True
    The Or:                   True
    Using NotMatch Correctly: True

    Value:                    My Description has an ‘AnotherTest’ in it
    Match One:                True
    Match Two:                False
    The Or:                   True
    Using NotMatch Correctly: False