welcome to 's blog...


公告

我的分类(专题)

日志更新

最新评论

留言板

链接

搜索


Blog信息




Finding Idle Users(摘录)
aku1 发表于 2006-6-23 11:08:43

Finding Idle Users

A domain security script for pre-Windows Server 2003 domains.
by Don Jones  
5/26/2004 -- My business partner, Derek Melber, was recently asked by one of our consulting clients to create a script that would aid them in auditing their domain security. He wanted to create a report that would display users' account status, including lockout, disabled, last time their password was changed, and so forth. He also wanted to display users' last logon date.

All that information is in the domain, but the last logon date in particular can be difficult to figure out in pre-Win2003 domains. That's because, prior to Win2003, the "lastLogon" attribute isn't replicated, meaning only the last DC that a user authenticated to will have an accurate value. So to display an accurate value, you'd have to query every domain controller in the domain—perfect gruntwork for a script, but not the easiest thing to code. I had some free time this past Saturday, though (I have no life), and decided to take a whack at it, using Derek's original script as a starting point.

One thing I wanted to do was be able to check every user in the domain, which is a less-than-straightforward task in AD, where users can be buried under a tree of organizational units (OUs). Fortunately, AD is backward-compatible with Windows NT domains, meaning AD can present its user list as a flat list, ignoring OU membership. Windows NT also provides an easier-to-use LastLogin property for users; AD's native lastLogon property (note the subtle name difference) is a complex integer value that includes both a time and date, and it's a pain to fuss with in VBScript.

On the other hand, NT doesn't make it very easy to enumerate a list of all DCs in the domain. AD doesn't exactly make it easier, but it does have a helpful Domain Controllers OU, which, nine times out of ten, contains every DC in the domain. In fact, pretty much nobody recommends ever moving DCs out of the Domain Controllers OU, so it's a safe bet that all the DCs' computer accounts can be found there.

Given the relative strengths and weaknesses of NT and AD, then, I resolved to use both. The result is the following script, which uses Active Directory Services Interface (ADSI) and VBScript to query an AD domain both from an NT standpoint (using the WinNT: moniker) and from a native AD standpoint (using the LDAP: moniker). Check it out:

'get domain and container
Dim sTarget, sOU
sTarget = InputBox("Target what domain?", "Domain?", _
   "techmentorevent")

sLDAP = InputBox("LDAP version of domain name?", "LDAP Domain?", _
  "dc=techmentorevents,dc=pri")

'iterate through all DCs?
Dim bCheckAll
bCheckAll = MsgBox("Check all DCs for last logon date?", _
  4, "Check all DCs?")
If bCheckAll = 6 Then
  bCheckAll = True
Else
  bCheckAll = False
End If

'objects for output file
Dim sOutput, oFSO, oTS
sOutput = InputBox("Output path and filename?", "Filename?", _
  "c:\domainreport.csv")
Set oFSO = CreateObject("Scripting.FileSystemObject")

'see if file exists
If oFSO.FileExists(sOutput) Then
  MsgBox("File exists. Please select another.")
  WScript.Quit
End If

'create output file
Set oTS = oFSO.CreateTextFile(sOutput)
Const cComma = ","

'output header
oTS.WriteLine "User" & _
  cComma & "Disabled" & cComma & "Locked" & _
  cComma & "IdleDays"

'get destination container
On Error Resume Next
Set oOU = GetObject("WinNT://" & sTarget)
If Err <> 0 Then
  MsgBox "Error: " & vbCrLf & Err.Description
  WScript.Quit
End If
On Error Goto 0

'filter for user objects
oOU.Filter = Array("User")

'get the Default Domain Controllers container
Dim oDCs, oDC
Set oDCs = GetObject("LDAP://ou=Domain Controllers," & _
  sLDAP)

'go through container objects
Dim oObject
For Each oObject In oOU

      'get attributes
      Dim bDisabled, bLocked, iIdleDays
      Dim dLastLogin, dLocalLastLogin

      'reset last login date
      dLastLogin = CDate("1/1/1900")

      'is user disabled?
      If oObject.accountdisabled Then
        bDisabled = True
      Else
        bDisabled = False
      End If

      'is user locked out?
        If oObject.isaccountlocked Then
        bLocked = True
       Else
        bLocked = False
       End If

      'output basic info
      oTS.Write oObject.name & cComma & _
        bDisabled & cComma & _
        bLocked & cComma

      'check for last login?
      If bCheckAll Then

        'checking last login on all DCs
        For Each oDC In oDCs

          'get last login from this DC
          Dim oLocalUser
          Set oLocalUser = GetObject("WinNT://" & _
               Replace(oDC.sAMAccountName,"$","") & "/" & _
               oObject.name & ",user")

            On Error Resume Next
            dLocalLastLogin = oLocalUser.LastLogin
            If Err <> 0 Then
               dLocalLastLogin = CDate("1/1/1900")
            End If
            On Error Goto 0

            'is that more recent?
            If dLocalLastLogin > dLastLogin Then
               dLastLogin = dLocalLastLogin
            End If

          Next

          'output last login
          oTS.Write DateDiff("d",dLastLogin,Date) & vbcrlf

      Else
         'not checking last login
         oTS.Write "n/a" & vbcrlf
      End If

Next

'close text file and notify
oTS.Close
MsgBox "Finished. Output is at " & sOutput


When you run this script, you'll have to enter the domain name twice. First, enter a normal NT domain name (the default is TECHMENTOREVENT). Next, enter the LDAP-style domain name. The default is "dc=techmentorevents,dc=pri" for my example domain, techmentorevents.pri. If your domain was braincore.net, you'd enter "dc=braincore,dc=net" for the domain name. You can decide whether or not to check the LastLogin property, because doing so will cause the script to run for a lot longer since it has to query a bunch of computers. The script will, by the way, bomb out if any of your DCs are unreachable. I left it that way on purpose, but you can have it ignore missing DCs by using the following code:

      'get last login from this DC
      Dim oLocalUser
      On Error Resume Next
      Set oLocalUser = GetObject("WinNT://" & _
         Replace(oDC.sAMAccountName,"$","") & "/" & _
         oObject.name & ",user")

The boldfaced line of code tells VBScript to ignore any errors and keep right on processing.

I hope this will be a useful script for you. Those of you lucky enough to work in a native Windows Server 2003 domain don't need this trickery, because Win2003 repliates the lastLogin property to all DCs; just use the Saved Queries feature in 2003's AD Users and Computers console to query for user accounts that haven't logged in during a specified number of days. Note that it's impossible for me to test this script in the myriad environments out there; it's possible your environment may contain something that causes the script to bomb unexpectedly. If it does, please feel free to send me a short e-mail describing the problem and I'll do my best to help you figure it out. This script outputs a file that can easily be imported into Excel so that you can sort by column or do whatever other fancy stuff you like.


Don Jones is the owner and operator of ScriptingAnswers.com, a speaker at national technical IT conferences, and the author of nearly twenty books on information technology. His latest book is "Managing Windows with VBScript and WMI" (Addison-Welsey) and he's completing "Windows Administrator's Automation Toolkit" (Microsoft Press). You can reach Don at his Web site or at don@scriptinganswers.com.

阅读全文 | 回复(0) | 引用通告 | 编辑


发表评论:

    昵称:
    密码: (游客无须输入密码)
    主页:
    标题:



Powered by Oblog.