Link to home
Start Free TrialLog in
Avatar of jsctechy
jsctechyFlag for United States of America

asked on

VBScript - Last logon

Hi, just trying this VB script in a test domain, need some help.

Set objUser = GetObject("LDAP://cn=Fred Flinstone,ou=Stone,ou=Users,dc=Testing,dc=com")
Set objLastLogon = objUser.Get("lastLogonTimestamp")

intLastLogonTime = objLastLogon.HighPart * (2^32) + objLastLogon.LowPart
intLastLogonTime = intLastLogonTime / (60 * 10000000)
intLastLogonTime = intLastLogonTime / 1440

Wscript.Echo "Last logon time: " & intLastLogonTime + #1/1/1601#


Look at AD, I click 'Testing.com' then go to 'Users' ou, and then 'Stone' ou, in which "Fred Flinstone" is in there.  I want to see when he logged on last.  Here is the link to MSFT Scripts- http://www.microsoft.com/technet/scriptcenter/topics/win2003/lastlogon.mspx


This doesn't seem to work, I get an error everytime on the first line, character 1, source NULL
Avatar of LauraEHunterMVP
LauraEHunterMVP
Flag of United States of America image

Have you confirmed that that is the correct DN syntax for the user in question?  "source NULL" sounds like it's trying to bind to that object and not finding it.
Avatar of jsctechy

ASKER

Yes.
After posting this, I am getting an error at line 2 character 1. Says that the attribute cannot be found in the cache.
Is your Active Directory domain running at the Windows Server 2003 domain functional level?  If not, the lastLogonTimestamp attribute will not be available.
I am looking at my Domain controller, it is running on a 2003 OS.  My mail server however is 2000 svr.
The fact that the DC has the 2003 OS does not necessarily mean that you are running at Windows Server 2003 DFL.  See the following for more information about functional levels and how to view/change them: http://www.petri.co.il/understanding_function_levels_in_windows_2003_ad.htm
I also tried this- which works, however, I don't get user names, I get computer names.  Which isn't helpful.
___________
' LastLogon.vbs
' VBScript program to determine when each user in the domain last logged
' on.
'
' ----------------------------------------------------------------------
' Copyright (c) 2002 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net
' Version 1.0 - December 7, 2002
' Version 1.1 - January 17, 2003 - Account for null value for lastLogon.
' Version 1.2 - January 23, 2003 - Account for DC not available.
' Version 1.3 - February 3, 2003 - Retrieve users but not contacts.
' Version 1.4 - February 19, 2003 - Standardize Hungarian notation.
' Version 1.5 - March 11, 2003 - Remove SearchScope property.
' Version 1.6 - May 9, 2003 - Account for error in IADsLargeInteger
'                             property methods HighPart and LowPart.
'
' Because the lastLogon attribute is not replicated, every Domain
' Controller in the domain must be queried to find the latest lastLogon
' date for each user. The lastest date found is kept in a dictionary
' object. The program first uses ADO to search the domain for all Domain
' Controllers. The AdsPath of each Domain Controller is saved in an
' array. Then, for each Domain Controller, ADO is used to search the
' copy of Active Directory on that Domain Controller for all user
' objects and return the lastLogon attribute. The lastLogon attribute is
' a 64-bit number representing the number of 100 nanosecond intervals
' since 12:00 am January 1, 1601. This value is converted to a date. The
' last logon date is in UTC (Coordinated Univeral Time). It must be
' adjusted by the Time Zone bias in the machine registry to convert to
' local time.
'
' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.




Dim objRootDSE, strConfig, objConnection, objCommand, strQuery
Dim objRecordSet, objDC, f, fso
Dim strDNSDomain, objShell, lngBiasKey, lngBias, k, arrstrDCs()
Dim strDN, dtmDate, objDate, lngDate, objList, strUser
Dim strBase, strFilter, strAttributes, lngHigh, lngLow
Const ForAppending = 8
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile("C:\Scripts\Output\Last_Logon.txt", ForAppending, True)

' Use a dictionary object to track latest lastLogon for each user.
Set objList = CreateObject("Scripting.Dictionary")
objList.CompareMode = vbTextCompare

' Obtain local Time Zone bias from machine registry.
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Control\" _
  & "TimeZoneInformation\ActiveTimeBias")
If UCase(TypeName(lngBiasKey)) = "LONG" Then
  lngBias = lngBiasKey
ElseIf UCase(TypeName(lngBiasKey)) = "VARIANT()" Then
  lngBias = 0
  For k = 0 To UBound(lngBiasKey)
    lngBias = lngBias + (lngBiasKey(k) * 256 ^ k)
  Next
End If

' Determine configuration context and DNS domain from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strConfig = objRootDSE.Get("configurationNamingContext")
strDNSDomain = objRootDSE.Get("defaultNamingContext")

' Use ADO to search Active Directory for ObjectClass nTDSDSA.
' This will identify all Domain Controllers.
Set objCommand = CreateObject("ADODB.Command")
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
objCommand.ActiveConnection = objConnection

strBase = "<LDAP://" & strConfig & ">"
strFilter = "(objectClass=nTDSDSA)"
strAttributes = "AdsPath"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"

objCommand.CommandText = strQuery
objCommand.Properties("Page Size") = 100
objCommand.Properties("Timeout") = 60
objCommand.Properties("Cache Results") = False

Set objRecordSet = objCommand.Execute

' Enumerate parent objects of class nTDSDSA. Save Domain Controller
' AdsPaths in dynamic array arrstrDCs.
k = 0
Do Until objRecordSet.EOF
  Set objDC = _
    GetObject(GetObject(objRecordSet.Fields("AdsPath")).Parent)
  ReDim Preserve arrstrDCs(k)
  arrstrDCs(k) = objDC.DNSHostName
  k = k + 1
  objRecordSet.MoveNext
Loop
' Retrieve lastLogon attribute for each user on each Domain Controller.
For k = 0 To UBound(arrstrDCs)
  strBase = "<LDAP://" & arrstrDCs(k) & "/" & strDNSDomain & ">"
 strFilter = "(&(objectCategory=computer)(objectClass=computer))"
  strAttributes = "CN,lastLogon"
  strQuery = strBase & ";" & strFilter & ";" & strAttributes _
    & ";subtree"
  objCommand.CommandText = strQuery
  On Error Resume Next
  Err.Clear
  Set objRecordSet = objCommand.Execute
  If Err.Number <> 0 Then
    Err.Clear
    On Error GoTo 0
    '!!!! You can comment this line below out if you would like to
    'Wscript.Echo "Domain Controller not available: " & arrstrDCs(k)
  Else
  Dim i 'As Integer
    On Error GoTo 0
    Do Until objRecordSet.EOF
   
      strDN = objRecordSet.Fields("CN")
      lngDate = objRecordSet.Fields("lastLogon")
      On Error Resume Next
      Err.Clear
      Set objDate = lngDate

      If Err.Number <> 0 Then
        Err.Clear
        dtmDate = #1/1/1601#
      Else
        lngHigh = objDate.HighPart
        lngLow = objDate.LowPart
        If lngLow < 0 Then
          lngHigh = lngHigh + 1
        End If
        If (lngHigh = 0) And (lngLow = 0) Then
          dtmDate = #1/1/1601#
        Else
          dtmDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
            + lngLow) / 600000000 - lngBias) / 1440
        End If
      End If
      On Error GoTo 0
      If objList.Exists(strDN) Then
        If dtmDate > objList(strDN) Then
          objList(strDN) = dtmDate
        End If
      Else
        objList.Add strDN, dtmDate
      End If
      objRecordSet.MoveNext
    Loop
  End If
Next

' Output latest lastLogon date for each user.
For Each strUser In objList
'        If objList(strUser) < (Date - 35) Then
         If objList(strUser) > (Date - 35) Then
            objTextFile.Write strUser & ";" & objList(strUser)
            objTextFile.Writeblanklines (1)
        End If

       
Next
MsgBox "done"
' Clean up.
objConnection.Close
Set objRootDSE = Nothing
Set objConnection = Nothing
Set objCommand = Nothing
Set objRecordSet = Nothing
Set objDC = Nothing
Set objDate = Nothing
Set objList = Nothing
Set objShell = Nothing
__________________________________
The 2nd script you list is querying for the lastLogon attribute, which is present in a 2003 domain that is not at the 2003 DFL.  Your original code was querying for lastLogonTimestamp, which is only present in 2003 DFL.
Yes, I know.  Is there a way with the second script to query for last log on for a particular group?  Or actually, 2 groups?  CN=group1 and CN=group2 but also showing the PC name?
So right now I changed the objectclass to USER.  But I'd also like to see what PC the user logged onto.
Know of a way to do that?
> But I'd also like to see what PC the user logged onto.

This information is not stored in Active Directory and would only be available by querying for Account Logon Events on the security logs of your domain controllers.  If your security logs do not extend back as far as a particular user's last logon, this information will not be available to you.
So rather than using the scripts in the post, I should use a totally different script which queries the DC security logs for a specific event code (540?)?
If you are looking for the name/IP address of the PC that was logged onto by a particular user, that is where it will be stored, correct.  Querying Active Directory will only produce the time of the last logon; the name of the workstation that the user logged onto is not captured or stored in AD.
Hmmb, do you know where I can get a sample script?
ASKER CERTIFIED SOLUTION
Avatar of LauraEHunterMVP
LauraEHunterMVP
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hello,

Regarding your first script, in addition to what LauraEHunterMVP commented on,
please verify that this users OU is really an OU that you created. If it is the default users container try changing your script to this. The change is ou=users to cn=users
Set objUser = GetObject("LDAP://cn=Fred Flinstone,ou=Stone,cn=Users,dc=Testing,dc=com")

Regarding the 2nd script. In order to query users you will need to change the line that says
strFilter = "(&(objectCategory=computer)(objectClass=computer))"
to
strFilter = "(&(objectCategory=person)(objectClass=user))"
approx. line 104
sorry, nevemind my first comment....can't have an OU inside a container !
Actually you -can-, technically, it just requires a modification to the AD schema that maybe 3 people in the world can tell you about (me and the 2 people I learned it from) because there's no real reason to do it.  :-)
I am looking at the link.  What is the event code?  Is it 540 for logon?
Here are my goals for this script.  Use C:\output as the destination file path.  Event ID should be 540 and 528.  But only for the current day.  So if I run this today, only event logs with todays date should show up in the txt file output.  Also should only work for successful audits, not failures.  
The main reason for all this is to see what time users log on in the morning.  I really only need this for one group 'terminal_users_desk'.  This is what I have, but haven't really changed much.  I am not sure what I would need to add/change.
____________
'EventIDSecurity.vbs
' Sample WMI to find and Event ID in the Security Log
' Author Guy Thomas http://computerperformance.co.uk/
' Version 1.7 - May 2006
' -----------------------------------------------------------'
Option Explicit

Dim objFSO, objFolder, objFile, objWMI, objItem ' Objects
Dim strComputer, strFileName, strFileOpen, strFolder, strPath
Dim intEvent, intNumberID, intRecordNum, colLoggedEvents

' --------------------------------------------------------
' Set the folder and file name
strComputer = "."
strFileName = "\Event672.txt"
strFolder = "C:\Scripts\Output"
strPath = strFolder & strFileName

' Set numbers
intNumberID = 540 ' Event ID Number
intRecordNum = 0

' -----------------------------------------------------
' Section to create folder and hold file.
' Create the File System Object
Set objFSO = CreateObject("Scripting.FileSystemObject")

' Check that the strFolder folder exists
If objFSO.FolderExists(strFolder) Then
Set objFolder = objFSO.GetFolder(strFolder)
Else
Set objFolder = objFSO.CreateFolder(strFolder)
WScript.Echo "Just created " & strFolder
End If

If objFSO.FileExists(strFolder & strFileName) Then
Set objFolder = objFSO.GetFolder(strFolder)
Else
Set objFile = objFSO.CreateTextFile(strFolder & strFileName)
Wscript.Echo "Just created " & strFolder & strFileName
End If
' --------------------------------------------------
' Two tiny but vital commands (Try script without)
set objFile = nothing
set objFolder = nothing

' ----------------------------------------------------
' Write the information to the file
Wscript.Echo " Press OK and Wait 30 seconds (ish)"
Set strFileOpen = objFSO.CreateTextFile(strPath, True)

' ----------------------------------------------------------
' WMI Core Section
Set objWMI = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate,(Security)}!\\" _
& strComputer & "\root\cimv2")
Set colLoggedEvents = objWMI.ExecQuery _
("Select * from Win32_NTLogEvent Where Logfile = 'Security'" )

' ----------------------------------------------------------
' Next section loops through ID properties

For Each objItem in colLoggedEvents
If objItem.EventCode = intNumberID Then
If objItem.EventType=5 then
strFileOpen.WriteLine("Category: " & objItem.Category _
& " string " & objItem.CategoryString)
strFileOpen.WriteLine("ComputerName: " & objItem.ComputerName)
strFileOpen.WriteLine("Logfile: " & objItem.Logfile _
& " source " & objItem.SourceName)
strFileOpen.WriteLine("EventCode: " & objItem.EventCode)
strFileOpen.WriteLine("EventType: " & objItem.EventType)
strFileOpen.WriteLine("Type: " & objItem.Type)
strFileOpen.WriteLine("User: " & objItem.User)
strFileOpen.WriteLine("Message: " & objItem.Message)
strFileOpen.WriteLine (" ")
intRecordNum = intRecordNum +1
End If
End If
Next
Wscript.Echo "Check " & strPath & " for " & intRecordNum & " events"

WScript.Quit

' End of Guy's FSO sample VBScript
______________