January 2010 - Posts

lastLogonTimestamp in Active Directory User Discovery

This is something you might find a use for if you have Active Directory User Discovery enabled.  Active Directory User Discovery might be something you have enabled on your central site, but not your child sites, purely for reporting purposes.

One word of warning, before I delve into this.  lastLogonTimestamp may not actually display the last logon for a user, as its purpose is more to find inactive accounts, as opposed to the exact time a particular user last logged in:-

http://blogs.technet.com/askds/archive/2009/04/15/the-lastlogontimestamp-attribute-what-it-was-designed-for-and-how-it-works.aspx

Remember the purpose of the lastLogontimeStamp attribute is locate inactive accounts not provide real-time logon information.

So anyway, this is still useful if that’s the type of information you are looking for.  To configure this on your ConfigMgr site is simply a case of adding an attribute to your discovery like so:-

lastLogonTimestamp

Once Active Directory User Discovery runs, it will populate with a lastLogonTimestamp column.

The second part of this is that the information is actually stored in Interger8 format.  This basically means it is stored as the number of 100-nanosecond intervals since 12:00 AM January 1, 1601.  For example “129085607775156538” basically means 12908560777515653800 nano-seconds since 12:00 AM January 1, 1601.  One way to convert is to use w32tm.exe /ntte.:-

w32tm /ntte <NT time epoch>
  Convert a NT system time, in (10^-7)s intervals from 0h 1-Jan 1601, into a readable format.

C:\ w32tm.exe /ntte 129085607775156538
149404 15:19:37.5156538 - 21/01/2010 15:19:37 (local time)

To do this in T-SQL, I borrowed a bit of code from http://myitforum.com/cs2/blogs/jnelson/archive/2009/08/25/140938.aspx and came up with the following report:-

SELECT User_Name0,
       Name0,
       DATEADD(mi,(lastLogontimeStamp0  / 600000000) - 157258080,0) AS [Last Logon Time Stamp (UTC)]
FROM   dbo.v_R_User
WHERE  User_Name0 LIKE @variable

This gives you an output like so:-

User Name Name Last Logon Time Stamp (UTC)
DoeJ ACME\DoeJ (John Doe) 24/01/2010 22:30:00
Posted by Tom_Watson | with no comments

KB974014 - Windows 7 and 2008 R2 support added for SMS 2003

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

One thing to note is that it says this will be the last update of its kind for an OS support in SMS 2003.

Posted by Tom_Watson | with no comments
Filed under:

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

Posted by Tom_Watson | with no comments

Finding DNS Forwarders Through ConfigMgr

There are a couple of ways of doing this, though I will describe the first in more detail.  This might be useful if you need information to help redesign your DNS infrastructure.  Anyway, DNS forwarders configured on your DNS servers can be obtained in a couple of ways.

  1. Registry – HKLM\SYSTEM\CurrentControlSet\Services\Dns\Parameters|Forwarders (see http://support.microsoft.com/kb/813963 for details)
  2. WMI – wmic.exe /NAMESPACE:\\root\MicrosoftDNS PATH MicrosoftDNS_Server GET Forwarders (see http://msdn.microsoft.com/en-us/library/ms682725(VS.85).aspx for details)

To use the registry method, you could use the technique explained in http://myitforum.com/cs2/blogs/tom_watson/archive/2009/09/16/alternative-registry-inventory-with-configmgr.aspx by adding the additional entry to configuration.mof

[DYNPROPS]
instance of Registry_Values
{
                 KeyName="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dns\\Parameters|Forwarders";
  [PropertyContext("local|HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dns\\Parameters|Forwarders")
  ,Dynamic
  ,Provider("RegPropProv")] String;
};

Posted by Tom_Watson | with no comments

Reboot Nag, Using AutoIt

I thought I’d start 2010 with a blog posting pretty early on.  I’ve had a blog for a few months, and only blogged a few times.  I’m hoping this year I can get a few more done.  Anyway, onto the blog…

Something I was trying to get my head around lately was how to do a reboot nag that displays a balloon, similar to Automatic Updates.  This is something that could be fairly useful at the tail end of SMS packages etc.  My first option of VBScript, as far as I know, doesn’t do notifications using balloons.  I searched for other ways, and couldn't come up with anything, apart from a few “freeware” that looked a bit suspicious.  Then I remembered AutoIT v3 - http://www.autoitscript.com/autoit3 which I’d used for some other scripts in the past.  AutoIt, for those who are unfamiliar, is a scripting language that is more catered for handling things on the screen (moving windows, selecting items, typing characters, handling input etc.).  Anyway, here’s the script I came up with:-

::::::::::::::
Nag.au3
::::::::::::::

;
; AutoIt Version: 3.0
; Language:       English
; Platform:       Win9x/NT
; Author:         Tom Watson
;
; Script Function:
;   Displays a balloon message to restart the system
;

TraySetIcon("mucltui.dll",3)
TraySetToolTip ("Please restart your system")
Break(0) ;Disable break

While 1 = 1
   TrayTip("Global IT", "Critical updates have been installed on your system. Please save your work and restart your system as soon as possible.", 10, 1)
   Sleep (55000)
WEnd

(Watch the word wrap).  All this does is display a balloon for 5 seconds, and waits for another 55 seconds before redisplaying it in an infinite WHILE loop.  It uses the Automatic Updates DLL for its icon, and if you hover over the icon while the balloon isn’t displayed, it displays a much shorter summary.  You could modify this to suit your needs.

Nag Balloon

AutoIt has lots of functions you can use to display notifications, handle input etc. (see http://www.autoitscript.com/autoit3/docs/functions.htm for more details).  Also, one of the things you can do with AutoIt is to compile your scripts into a .EXE, which is what you would normally do if using your script on other PCs.  If you compiled the script above to an .EXE, you could launch it in a number of ways.  e.g. from a .VBS script:-

::::::::::::::
Nag.vbs
::::::::::::::

Set objShell = CreateObject("Wscript.Shell")

strPath = Wscript.ScriptFullName

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strPath)

strFolder = objFSO.GetParentFolderName(objFile)

strPath =  CHR(34) & strFolder & "\Nag.exe" & CHR(34)
objShell.Run strPath, 1, FALSE

Or even launch it remotely by using PSExec:-

D:\My AutoIt3 Scripts> psexec.exe -s -c -f -d -i \\pc65 Nag.exe

PsExec v1.97 - Execute processes remotely
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com

Nag.exe started on pc65 with process ID 4644.


I hope this gives you some ideas for an alternative way to handle reboots, or even do other more complex tasks.

Posted by Tom_Watson | 2 comment(s)
Filed under: