Determine Overlapping Active Directory Sites

This is merely pointing out an excellent script I found when trying to troubleshoot SMS/SCCM site boundary issues.  One issue I had was where different active directory sites had overlapping subnets.  Anyway, this script came from http://web2.minasi.com/forum/topic.asp?TOPIC_ID=13286 and requires Perl to run. :-

#!perl -w
# wkasdo 2004
# find overlapping subnets in AD. Those are not necessarily a
# bad thing if it was done on purpose, but can be the cause of
# some very confusing logon behaviour.

use strict;
use Win32::OLE 'in';
$Win32::OLE::Warn = 3;

my @subnets;        # array of anonymous hashes {cn, from, to}
my %overlaps;       # hash {cn} of anonymous arrays of subnets
my ($subnet, $from, $to, $i, $j);

# read all subnets from AD, convert to integer range, and sort.
foreach $subnet (&read_all_subnets) {
    ($from, $to) = &subnet2range ($subnet);
    push @subnets, { cn => $subnet, from => $from, to => $to };
}
@subnets = sort { $a->{from} <=> $b->{from} } @subnets;

# compare all possible subnets for overlap; depend on sort order.
for ($i=0; $i<=$#subnets; $i++) {
    for ($j=$i+1; $j<=$#subnets; $j++) {
        last if $subnets[$i]->{to} < $subnets[$j]->{from};    # no possible overlap anymore;
        push @{$overlaps{$subnets[$i]->{cn}}}, $subnets[$j]->{cn} if $subnets[$i]->{to} >= $subnets[$j]->{from};
    }
}

if (scalar (keys %overlaps)) {
    print "The following overlapping subnets were found:\n";
    foreach $subnet (sort keys %overlaps) {
        printf "%-20s : %s\n", $subnet, join (", ", sort @{$overlaps{$subnet}});
    }
} else {
    print "No overlapping subnets found.\n";
}
exit (0);
# &read_all_subnets
# read the subnets from the configuration context and return as array.
#
sub read_all_subnets {
    my ($cnc, $o_subnets, $o_subnet, @subnets);

    $cnc = Win32::OLE->GetObject("LDAP://RootDSE")->Get("configurationNamingContext");
    $o_subnets = Win32::OLE->GetObject("LDAP://CN=Subnets,CN=Sites,$cnc");
    $o_subnets->{Filter} = ["subnet"];
    foreach $o_subnet (in $o_subnets) {
       push @subnets, $o_subnet->Get("cn");
    }
    return @subnets;
}

# &subnet2range ($subnet)
# convert subnets to integers in order to compare them later.
# A subnet looks like this: 10.1.2.0/24
# returns beginning and end of subnet as integer
#
sub subnet2range {
    my $subnet = shift (@_);
    my ($from, $to);
    $subnet =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/ || die "bad subnet $subnet\n";
    $from = $1*2**24 + $2*2**16 + $3*2**8 + $4;
    $to = $from + 2**(32-$5) - 1;
    return ($from, $to);
}

I am by no means a Perl expert, but this indeed did show some overlaps from different sites that needed resolving.  A copy of Perl, which is free to use, can be downloaded from http://www.activestate.com/activeperl/.

A simple VBScript to list all your active directory site names and their subnets is:-

On Error Resume Next

Set objRootDSE = GetObject("LDAP://RootDSE")
strConfigurationNC = objRootDSE.Get("configurationNamingContext")
strSubnetsContainer = "LDAP://cn=Subnets,cn=Sites," & strConfigurationNC
Set objSubnetsContainer = GetObject(strSubnetsContainer)
objSubnetsContainer.Filter = Array("subnet")
Set objHash = CreateObject("Scripting.Dictionary")
For Each objSubnet In objSubnetsContainer
    objSubnet.GetInfoEx Array("siteObject"), 0
    strSiteObjectDN = objSubnet.Get("siteObject")
    strSiteObjectName = Split(Split(strSiteObjectDN, ",")(0), "=")(1)
    If objHash.Exists(strSiteObjectName) Then
        objHash(strSiteObjectName) = objHash(strSiteObjectName) & "," & _
            Split(objSubnet.Name, "=")(1)
    Else
        objHash.Add strSiteObjectName, Split(objSubnet.Name, "=")(1)
    End If
Next
For Each strKey In objHash.Keys
    WScript.Echo strKey & "," & objHash(strKey)
Next

Credit http://www.activxperts.com/activmonitor/windowsmanagement/scripts/activedirectory/sites/#LSADS.htm

Published Wednesday, January 27, 2010 11:57 AM by Tom_Watson

Comments

No Comments