Posts RSS Comments RSS 249 Posts and 391 Comments till now

Archive for January, 2009

Testing AD LDS (ADAM) replication with Powershell

Earlier this month I had a discussion with Laura (of AD Cookbook fame) regarding ADLDS and how to test convergence. After a few minutes I remembered I had a AD convergence script I wrote a while back found HERE. With a little tweaking (specifically discoverability) we converted it to test ADLDS as well. Below you will fine the result.

Parameters
- Server: The ADLDS/ADAM server that hosts the application partition you want to test
- DN: The distinguished name of the application partition you want to test (will try to discover)
- Port: Port ADLDS/ADAM list on (Default 389)
- Table [switch]: A switch that outputs an object with the results.

Note: Please feel free to provide any feedback you have regarding this. I do not use ADLDS or ADAM so other than my test environment I really cannot play with this.

The Code
Test-LDSReplication.ps1


 

Param($Server = $Env:ComputerName,
      $DN,
      $Port = "389",
      [switch]$table
      )

function Ping-Server {
   Param([string]$srv)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’ and Timeout=1000"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}

$DirectoryServer = "{0}:{1}" -f $Server,$Port

$Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$DirectoryServer)
$ADAM = [System.DirectoryServices.ActiveDirectory.AdamInstance]::GetAdamInstance($context)

if(!$DN)
{
    $AppPartition = $ADAM.ConfigurationSet | %{$_.ApplicationPartitions} | Select-Object -first 1
    $DN = $AppPartition.Name
    $dclist = $AppPartition.DirectoryServers | ?{$_.HostName -notmatch $Server}
}
else
{
    $dclist = $ADAM.ConfigurationSet.AdamInstances | ?{($_.Partitions -contains $DN) -and ($_.HostName -notmatch $Server)}
}

if($table)
{
    $DCTable = @()
    $myobj = "" | select Name,Time
    $myobj.Name = ("$Server [SOURCE]").ToUpper()
    $myobj.Time = 0.00
    $DCTable += $myobj
}

$timestamp = [datetime]::Now.ToFileTime().ToString()
Write-Host "`n  Modifying wwwHomePage Attribute on Object [$DN] on [$DirectoryServer] with value [$timestamp]"
$object = ([ADSI]"LDAP://$DirectoryServer/$DN")
$object.wWWHomePage = $timeStamp
$object.SetInfo()
$objectDN = $object.distinguishedname
Write-Host "  Object [$objectdn] Modified! `n"

$start = Get-Date

$i = 0

Write-Host "  Found [$($dclist.count)] LDS replicas"
$cont = $true

While($cont)
{
    $i++
    $oldpos = $host.UI.RawUI.CursorPosition
    Write-Host "  =========== Check $i ===========" -fore white
    start-Sleep 1
    $replicated = $true
    foreach($dc in $dclist)
    {
        if($server -match $dc.HostName){continue}
        if(ping-server $dc.HostName)
        {
            $DCServer = "{0}:{1}" -f $dc.HostName,$dc.LdapPort
            $object = [ADSI]"LDAP://$DCServer/$dn"
            if($object.wwwHomePage -eq $timeStamp)
            {
                Write-Host "  - $DCServer Has Object Description [$dn]" (" "*5) -fore Green
                if($table -and !($dctable | ?{$_.Name -match $dc.HostName}))
                {
                    $myobj = "" | Select-Object Name,Time
                    $myobj.Name = $dc.HostName.ToUpper()
                    $myobj.Time = ("{0:n2}" -f ((Get-Date)-$start).TotalSeconds)
                    $dctable += $myobj
                }
            }
            else{Write-Host "  ! $($dc.HostName.ToUpper()) Missing Object [$dn]" -fore Red;$replicated  = $false}
        }
        else
        {
            Write-Host "  ! $($dc.HostName.ToUpper()) Failed PING" -fore Red
            if($table -and !($dctable | ?{$_.Name -match $dc}))
            {
                $myobj = "" | Select-Object Name,Time
                $myobj.Name = $dc.HostName.ToUpper()
                $myobj.Time = "N/A"
                $dctable += $myobj
            }
        }
    }
    if($replicated){$cont = $false}else{$host.UI.RawUI.CursorPosition = $oldpos}
}

$end = Get-Date
$duration = "{0:n2}" -f ($end.Subtract($start).TotalSeconds)
Write-Host "`n    Took $duration Seconds `n" -fore Yellow

if($table){$dctable | Sort-Object Time}

More Powershell RegEx fun (playing with ‘route Print’)

A problem come that required me to check the routing table on a large number of machines. My first thought was to try to find a .NET class to dump the table, but alas I could not find any class to do what I wanted. That was a bummer but not unexpected so I decided to just use ol’faithful psexec.exe (Found here.)

Now, for those of you that have used ‘route print’ before you know the output is well… verbose :) and clearly that would not be very helpful for filtering.


Enter ‘Get-RouteTable” (found below)

I share this, not so much because I found it incredibly useful (although I did,) but because it illustrates what I have found to be a great way of creating complex Regular Expressions (for the sake of brevity I will use RegEX for the rest of the article.)

My approach is quiet simple. Create a string object for the RegEx and build the Regex over time testing each section.

Lets take this string for example

Active Routes:
Network Destination        Netmask                 Gateway          Interface            Metric
          0.0.0.0                   0.0.0.0                    192.168.1.1     192.168.1.13     25
        127.0.0.0                 255.0.0.0                127.0.0.1          127.0.0.1           306
        127.0.0.1                 255.255.255.255    127.0.0.1           127.0.0.1           306

I wanted to extract the Network Destination, Netmask, Gateway, Interface, and Metric and make them properties on a custom object. How does one go about that? One way is to create a regular expression with labels and use $matches to extract the information. This is route I took, but instead of trying to write a single complex RegEx and then trouble-shooting it for hours I decided to break down the regular expression into bite size chunks.

To show you what I mean by complex… here is the RegEx you end up with

“^\s+(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Gateway>\d{1,3
}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Metric>\d{1,4})$”

As you can this is fairly complex and could be a HUGE pain to trouble shoot.

Following my approach you break down. First you add the begin and end to the regex

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “.+$”

The next step is we add a label and pattern for the “Network Destination” and test

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”

$DiscoverRoute += “.+$”

(route print) | ?{$_ -match $DiscoverRoute}

If you get results you expect the next step is to make sure $matches sees the labels as you expect

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Great! Now we can the next part

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+$”

Repeat the Last test but make sure you have a value for both Network and NetMask

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Rinse and repeat until you end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})”
$DiscoverRoute += “.+$”

This does not work just yet… can you spot why? The reason is because we have been adding “.+$” (zero or more of anything) to the end every time for testing purposes. We now need to remove this and add the $ to the end of the Metric string because the EOL is immediately after the last digit in Metric column. We end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

We took a complex RegEx and with a little string concantination we were able to break it down and make the task quite simple.

Note: Another cool effect of this method is that we can easily comment out a section that maybe break the entire regular expression like this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
#$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

Just make sure that you add the “.+” to the line just after the comment line. This allows the regular expression to replace that section with basically anything.

NOTE: A final, slightly embarrassing, note. After writing the script below I realized this information (save the persistent routes) is readily available using WMI via the Win32_IP4RouteTable class.

The Code:


Param($Servers)

function Convert-RouteTabletoObject ($data)
{
    # RegEx for Discovery Routes
    $DiscoverRoute  = "^\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,4})"
    $DiscoverRoute += "$"

    # RegEx for Persistent Routes
    $PersistentRoute  = "^\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,4})"
    $PersistentRoute += "$"

    $Routes = @()
    switch -regex ($data)
    {
        $DiscoverRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = $matches.Interface
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $False
                        $Routes += $NewRoute
                    }
        $PersistentRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = "N/A"
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $True
                        $Routes += $NewRoute
                    }
    }
    $Routes
}

foreach($server in $servers)
{
    $myobj = "" | Select-Object Name,RouteTable
    $myobj.Name = $Server
    $myobj.RouteTable = Convert-RouteTabletoObject (invoke-Expression "psexec \\$server route print" 2&gt;$NULL)
    $myobj
}

# Note: You can use WMI to get most of this info – Get-WMIObject Win32_IP4RouteTable -ComputerName

Regular Expression Info and Tips for Powershell

One of the most useful tools a scripter has at their disposal is Regular Expressions. The problem is that regular expressions can seem like Greek and for most folks learning them can seem an impossible task. I hope this blog post provides those people some hope as well as some tools they can use to tackle this mountain one foot at a time.

NOTE: I would like to make this blog entry as dynamic as possible, so if you have your own tips… let me know and I will add them.


Everyone is different, but this is how I went about learning Regular Expressions

  • Phase one: Learn EXACTLY what a regular expression is and what it can and can NOT do.
  • Phase two: Learn the syntax. I printed out the RegEx Cheet Sheet and use it daily.
  • Phase three: Find a purpose. I find if I actually have a problem I spend more energy on learning.
  • Phase four: Create a simple Regular Expression.
  • Phase five: Build on your Regular Expression.
  • Phase six: Trouble shooting the Regular Expression.

Useful Tips:

  • First things first… Click here and watch the vids
  • Keep a cheat sheet close at hand!
  • Take them in small bites. I hope to blog in detail about this later, but lets just say string concatenation is your friend
  • Keep it simple!
  • $matches is your friend! $matches is an object that Powershell creates when you make a RegEx call (like -match.)
  • Google It. Chances are there is Regex already out there for what you need.
  • Using Lables can save you time by extracting certain data and making it a property on $matches.
  • RichardP from IRC says “Dont eat yellow snow?” Apparently he has experience in this area.

Online Vids:
This is GREAT stuff provided by Shay (aka scriptfanatic)

Good Blog Entries:
MoW at it again.

References:
RegEx Cheet Sheet (my Favorite)
Regular-Expressions.info
Regular Expression Library

Online Regex Checkers:
RegEx Tester (my Favorite)
Regular Expression Library Tester

Books:
Mastering Regular Expressions (my Favorite)
Regular Expression Recipes for Windows Developers

Tools:
Expresso
RegEx Buddy

Windows 2008 R2 Beta Released to Technet and MSDN

Login… download… enjoy!

This is includes Powershell v2 (later build than CTP3) installed by default!