Pete Long
asked on
More Script Requirements
Thanks to all the Expers who have helped this far. Next Item on the agenda
I need a vbscript that will output the following (from my Active Directory)
-------
The following user accounts are disabled
GivenName LoginName
etc........
The following user accounts are locked out
GivenName LoginName
etc.........
------
Pete
I need a vbscript that will output the following (from my Active Directory)
-------
The following user accounts are disabled
GivenName LoginName
etc........
The following user accounts are locked out
GivenName LoginName
etc.........
------
Pete
Hi Pete,
Kind of nice to be able to help you out ;)
Best run with cscript...
Option Explicit
' Global Constants
Const ADS_UF_LOCKOUT = &H10
Const ADS_UF_ACCOUNTDISABLE = &H2
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = objUser.Get("givenName")
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = objUser.Get("sAMAccountNam e")
On Error Goto 0
intUAC = objUser.Get("userAccountCo ntrol")
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis abled)
arrUserDisabled(intUserDis abled) = strFullName & Chr(9) & strLogonName
End If
If ADS_UF_LOCKOUT and intUAC Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke d)
arrUserLocked(intUserLocke d) = strFullName & Chr(9) & strLogonName
End If
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath )
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
End Sub
'
' Main Code
'
' Connect to the Root of the Domain
Set objRootDSE = GetObject("LDAP://rootDSE" )
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam ingContext "))
' These will be incremented to 0 the first time they are used
' They represent the size of the array of users
intUserDisabled = -1
intUserLocked = -1
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
wscript.echo "Disabled Accounts"
wscript.echo "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
wscript.echo strLine
Next
wscript.echo ""
wscript.echo "Locked Out Accounts"
wscript.echo "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
wscript.echo strLine
Next
HTH
Chris
Kind of nice to be able to help you out ;)
Best run with cscript...
Option Explicit
' Global Constants
Const ADS_UF_LOCKOUT = &H10
Const ADS_UF_ACCOUNTDISABLE = &H2
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = objUser.Get("givenName")
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = objUser.Get("sAMAccountNam
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
If ADS_UF_LOCKOUT and intUAC Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
End Sub
'
' Main Code
'
' Connect to the Root of the Domain
Set objRootDSE = GetObject("LDAP://rootDSE"
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' These will be incremented to 0 the first time they are used
' They represent the size of the array of users
intUserDisabled = -1
intUserLocked = -1
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
wscript.echo "Disabled Accounts"
wscript.echo "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
wscript.echo strLine
Next
wscript.echo ""
wscript.echo "Locked Out Accounts"
wscript.echo "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
wscript.echo strLine
Next
HTH
Chris
ASKER
:) Hi Chris
Where is this outputting to? is it dynamically getting my domain details?
Where is this outputting to? is it dynamically getting my domain details?
Screen at present although that's just because you didn't specify where you wanted it dumped to ;)
And yes it figures it's own way around your domain.
ASKER
OIC its outputting to screen :)
Can It output to c:\disabled_locked.txt
and I sort of answered my own second question there :)
Can It output to c:\disabled_locked.txt
and I sort of answered my own second question there :)
ASKER
typing at the same time :)
Yep, give me a moment, I'll write in what it needs to output to file.
Currently tab delimited (that's what Chr(9) is) would you prefer something else?
ASKER
Not fussy at the moment - as you can see Ive allready aked one script question then Ive got this one to do then one more and the last question will be how to knit all three together and produce one output file (in txt or csv)
Here we go... writes output to a file (as specified above). File Name / Path are set in the Constants section at the top of the script - it overwrites the file if it exists.
Option Explicit
' Global Constants
Const ADS_UF_LOCKOUT = &H10
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\disabled_locked.txt"
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = objUser.Get("givenName")
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = objUser.Get("sAMAccountNam
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
If ADS_UF_LOCKOUT and intUAC Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
End Sub
'
' Main Code
'
' Connect to the Root of the Domain
Set objRootDSE = GetObject("LDAP://rootDSE"
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' These will be incremented to 0 the first time they are used
' They represent the size of the array of users
intUserDisabled = -1
intUserLocked = -1
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
' Initialize the File System Object and create a text file
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
objFile.WriteLine "Disabled Accounts"
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "Locked Out Accounts"
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
Although I really should remember to close off all these objects I'm using... ah well.. it's only a little script...
ASKER
Works!
Is it possible to autodetect the domain details and domain controllers in the same way? if so can that be applied to
https://www.experts-exchange.com/questions/21410672/Script-Error-Please-Advise.html
Is it possible to autodetect the domain details and domain controllers in the same way? if so can that be applied to
https://www.experts-exchange.com/questions/21410672/Script-Error-Please-Advise.html
Yes it should be.. give me a little time to rework it all.
ASKER
OK
so in summary it should
give the following output
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- -
Accounts that have not logged on in the last 30 days
GivenName LoginName LastLoginTime
etc.........
The following user accounts are disabled
GivenName LoginName
etc........
The following user accounts are locked out
GivenName LoginName
etc.........
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- --
There will be another bit on the end too (But thats another Question)
Pete
so in summary it should
give the following output
--------------------------
Accounts that have not logged on in the last 30 days
GivenName LoginName LastLoginTime
etc.........
The following user accounts are disabled
GivenName LoginName
etc........
The following user accounts are locked out
GivenName LoginName
etc.........
--------------------------
There will be another bit on the end too (But thats another Question)
Pete
Hope I didn't break it:
Option Explicit
' Global Constants
Const FILE_NAME = "C:\LastLogon.txt"
' Global Variables
Dim objDict, objRootDSE, objDomainControllers, objDomainController, objServer
Dim objUser, objFileSystem, objFile, objTemp
Dim strDomainController
Dim intDays, intCounter
Dim datLastLogon
Dim arrKeys, arrItems
'
' Functions
'
Function Pad(varString, intLength)
Dim intSize
intSize = Len(Trim(varString))
Pad = Trim(varString) & Space(intLength - intSize)
End Function
'
' Main Code
'
' Read in the number of days from the command line
If Wscript.Arguments.Count > 0 Then
intDays = CInt(Wscript.Arguments(0))
If intDays = 0 Then
intDays = 30
End If
Else
intDays = 30
End If
Set objDict = CreateObject("Scripting.Di
' Get the DC List - Assumes OU is still called Domain Controllers
Set objRootDSE = GetObject("LDAP://rootDSE"
Set objDomainControllers = GetObject("LDAP://ou=domai
objDomainControllers.Filte
' Reconnect to each DC and grab the Last Logon time
For Each objDomainController In objDomainControllers
strDomainController = Mid(objDomainController.Na
Wscript.Echo "Reading Domain Controller: " & strDomainController
Set objServer = GetObject("WinNT://" & strDomainController)
' Get the User list
objServer.Filter = Array("User")
For Each objUser In objServer
' This is where Error Handling is needed
On Error Resume Next
datLastLogon = objUser.Get("lastLogon")
On Error Goto 0
If datLastLogon <> "" Then
If objDict.Exists(Trim(objUse
If CDate(datLastLogon) > CDate(objDict.Item(Trim(ob
objTemp = objDict.Item(Trim(objUser.
objTemp(1) = datLastLogon
objDict.Item(Trim(objUser.
End If
Else
objDict.Add Trim(objUser.Name), Array(objUser.Get("FullNam
End If
End If
Set objUser = Nothing
Next
Set objServer = Nothing
Set objDomainController = Nothing
Next
Set objDomainControllers = Nothing
' Write the output to the file
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
objFile.WriteLine "Accounts that have not logged in within the last " & intDays & " days." & vbCrLf
arrKeys = objDict.Keys
arrItems = objDict.Items
For intCounter = 0 To objDict.Count - 1
If DateDiff("d", CDate(arrItems(intCounter)
objFile.WriteLine Pad(arrItems(intCounter)(0
End If
Next
objFile.Close
Set objDict = Nothing
Set objFileSystem = Nothing
Set objFile = Nothing
Set objTemp = Nothing
Oops... one moment... brb with the full set.
Is the last bit particularly complicated?
ASKER
As I dont know diddly squat about code I dont know?
basically the next bit returns anyone with administrative access either directly or through Group membership
with regards to this Q this is my output
-------------------------- ---------- -------
Disabled Accounts
Given Name Logon Name
MPCT-000344$
MPCT-0010$
MPCT-001022$
MPCT-001715$
Greig Sharman Guest
Kay Weatherell weatherk
Kirsty White krbtgt
Richard Gill gillr
Sundaram Janakiraman SUPPORT_388945a0
Locked Out Accounts
Given Name Logon Name
-------------------------- ---------- ----------
Grieg Sharmans account is NOT Disabled (and guest is listed as his logon name)
Guest account is disabled (as usuall)
Sundaram Janakiraman account is NOT disabled and (SUPPORT_388945a0 is listed as his login name)
SUPPORT_388945a0 is disabled (as usuall)
Also
Its not displaying any locked out accounts? Theres at least one (I locked a an account out to test it)
basically the next bit returns anyone with administrative access either directly or through Group membership
with regards to this Q this is my output
--------------------------
Disabled Accounts
Given Name Logon Name
MPCT-000344$
MPCT-0010$
MPCT-001022$
MPCT-001715$
Greig Sharman Guest
Kay Weatherell weatherk
Kirsty White krbtgt
Richard Gill gillr
Sundaram Janakiraman SUPPORT_388945a0
Locked Out Accounts
Given Name Logon Name
--------------------------
Grieg Sharmans account is NOT Disabled (and guest is listed as his logon name)
Guest account is disabled (as usuall)
Sundaram Janakiraman account is NOT disabled and (SUPPORT_388945a0 is listed as his login name)
SUPPORT_388945a0 is disabled (as usuall)
Also
Its not displaying any locked out accounts? Theres at least one (I locked a an account out to test it)
ASKER
MPCT-000344$
MPCT-0010$
MPCT-001022$
MPCT-001715$
are computer accounts by the way but thats OK I can leave them in :)
MPCT-0010$
MPCT-001022$
MPCT-001715$
are computer accounts by the way but thats OK I can leave them in :)
Okay here we go... I've glued them all together. I hope I have't broken LastLogon - I never liked that attribute of AD anyway ;)
Option Explicit
' Global Constants
Const ADS_UF_LOCKOUT = &H10
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\disabled_locked.txt"
Const LOGON_PERIOD = "30"
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim objDict, objDomainControllers, objDomainController, objServer
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim strDomainController
Dim arrKeys, arrItems
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC, intCounter
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = objUser.Get("givenName")
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = objUser.Get("sAMAccountNam
strLastLogon = objUser.Get("lastLogin")
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
If ADS_UF_LOCKOUT and intUAC Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
Set objUser = Nothing
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objOrgUnit = Nothing
Set objFirst = Nothing
End Sub
Sub GetLastLogon(strDCName)
' Retrieves the Last Logon Time from a DC
Dim objServer, objUser, objTemp
Dim datLastLogon
Wscript.Echo "Reading Domain Controller: " & strDCName
Set objServer = GetObject("WinNT://" & strDCName)
objServer.Filter = Array("User")
For Each objUser In objServer
' This is where Error Handling is needed
On Error Resume Next
datLastLogon = objUser.Get("lastLogon")
On Error Goto 0
If datLastLogon <> "" Then
If objDict.Exists(Trim(objUse
If CDate(datLastLogon) > CDate(objDict.Item(Trim(ob
objTemp = objDict.Item(Trim(objUser.
objTemp(1) = datLastLogon
objDict.Item(Trim(objUser.
End If
Else
objDict.Add Trim(objUser.Name), Array(objUser.Get("FullNam
End If
End If
Set objTemp = Nothing
Set objUser = Nothing
Next
Set objServer = Nothing
End Sub
'
' Main Code
'
' Allow us to get the naming context
Set objRootDSE = GetObject("LDAP://rootDSE"
' These will be incremented to 0 the first time they are used
intUserDisabled = -1
intUserLocked = -1
' Get Accounts that haven't logged on recently
Set objDict = CreateObject("Scripting.Di
' Get the DC List
Set objDomainControllers = GetObject("LDAP://ou=domai
objDomainControllers.Filte
For Each objDomainController In objDomainControllers
strDomainController = Mid(objDomainController.Na
Set objDomainController = Nothing
GetLastLogon(strDomainCont
Next
Set objDomainControllers = Nothing
' Get Accounts that are Disabled or Locked Out
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objDomainRoot = Nothing
Set objRootDSE = Nothing
' Initialize the File System Object, create a text file and write the report
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
arrKeys = objDict.Keys
arrItems = objDict.Items
objFile.WriteLine "Accounts that have not logged on in the last 30 days"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name" & Chr(9) & "Last Logon Time"
objFile.WriteLine ""
For intCounter = 0 To objDict.Count - 1
If DateDiff("d", CDate(arrItems(intCounter)
objFile.WriteLine arrItems(intCounter) & Chr(9) & arrKeys(intCounter) & Chr(9) & arrItems(intCounter)
End If
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Disabled"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Locked Out"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
objFile.Close
Set objDict = Nothing
Set objFileSystem = Nothing
Ahh knew I should have hit refresh first...
Checking all the bits..
ASKER
gives this
Accounts that have not logged on in the last 30 days
Given Name Logon Name Last Logon Time
The following User Accounts are Disabled
Given Name Logon Name
MPCT-000344$
MPCT-0010$
MPCT-001022$
MPCT-001715$
Greig Sharman Guest
Kay Weatherell weatherk
Kirsty White krbtgt
Richard Gill gillr
Sundaram Janakiraman SUPPORT_388945a0
The following User Accounts are Locked Out
Given Name Logon Name
its listing no accounts in 30 days and the second bit (see comments above)
Accounts that have not logged on in the last 30 days
Given Name Logon Name Last Logon Time
The following User Accounts are Disabled
Given Name Logon Name
MPCT-000344$
MPCT-0010$
MPCT-001022$
MPCT-001715$
Greig Sharman Guest
Kay Weatherell weatherk
Kirsty White krbtgt
Richard Gill gillr
Sundaram Janakiraman SUPPORT_388945a0
The following User Accounts are Locked Out
Given Name Logon Name
its listing no accounts in 30 days and the second bit (see comments above)
ASKER
>>Ahh knew I should have hit refresh first...
LOL that makes two of us
LOL that makes two of us
I have a working base for the Last Logon attribute, I probably just broke the original.
Disabled and Lockout checks are now working correctly and I'll post the full thing in a minute or two when I've finished breaking it all some more.
Sorry for the wait... this lot works for me, let me know if it doesn't for you :)
Option Explicit
' Global Constants
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\disabled_locked.txt"
Const INACTIVE_PERIOD = 30
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim objList, objDomainControllers, objDomainController, objServer
Dim objLockout
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim strLockedOut, strEntry
Dim arrKeys, arrItems
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC, intCounter
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = ""
strFirstName = objUser.Get("givenName")
strLastName = ""
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = ""
strLogonName = objUser.Get("sAMAccountNam
strLastLogon = ""
strLastLogon = objUser.Get("lastLogin")
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
On Error Resume Next
Set objLockOut = objUser.lockoutTime
If Err.number <> 0 Then
' Do Nothing
Else
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
On Error Goto 0
Set objUser = Nothing
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objOrgUnit = Nothing
Set objFirst = Nothing
End Sub
Sub GetLastLogon(objDomainCont
' Retrieves the Last Logon Time from a DC
Dim objUsers, objUser, objTemp
Dim datLastLogon
Dim strDCName, strUserName
strDCName = Mid(objDomainController.Na
Set objUsers = GetObject("WinNT://" & strDCName)
objUsers.Filter = Array("user")
For Each objUser In objUsers
' This is where Error Handling is needed
On Error Resume Next
strLogonName = ""
strLogonName = objUser.Name
strFullName = ""
strFullName = objUser.FullName
datLastLogon = ""
datLastLogon = objUser.LastLogin
On Error Goto 0
If datLastLogon <> "" Then
If objList.Exists(strLogonNam
If datLastLogon > objList(strLogonName) Then
objList(strLogonName) = datLastLogon
End If
Else
objList.Add strLogonName, Array(strFullName, datLastLogon)
End If
End If
Set objUser = Nothing
Next
Set objServer = Nothing
End Sub
'
' Main Code
'
' Allow us to get the naming context
Set objRootDSE = GetObject("LDAP://rootDSE"
' These will be incremented to 0 the first time they are used
intUserDisabled = -1
intUserLocked = -1
' Get Accounts that haven't logged on recently
Set objList = CreateObject("Scripting.Di
' Get the DC List
Set objDomainControllers = GetObject("LDAP://ou=domai
objDomainControllers.Filte
For Each objDomainController In objDomainControllers
GetLastLogon(objDomainCont
Next
Set objDomainControllers = Nothing
' Get Accounts that are Disabled or Locked Out
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objDomainRoot = Nothing
Set objRootDSE = Nothing
' Initialize the File System Object, create a text file and write the report
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
objFile.WriteLine "Accounts that have not logged on in the last 30 days"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name" & Chr(9) & "Last Logon Time"
objFile.WriteLine ""
For Each strEntry In objList
If (CDate(objList(strEntry)(1
objFile.WriteLine objList(strEntry)(0) & Chr(9) & strEntry & Chr(9) & objList(strEntry)(1)
End If
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Disabled"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Locked Out"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
objFile.Close
Set objList = Nothing
Set objFileSystem = Nothing
As a small side-note. If you did prefer CSV format just do a Find and Replace on the vbs file:
Find: Chr(9)
Replace with (including the quotes - but no spaces in either case): ","
And that should do it.
:)
ASKER
Hi Chris - Im back at home now (17:49 hrs GMT) I wont get a chance to test it till tommorow (my home domain doesnt have the capacity to test it :)
Where did you learn vbscript? your an OS/Nethead like myself - writing code is a skill that has allways escaped me :(
Where did you learn vbscript? your an OS/Nethead like myself - writing code is a skill that has allways escaped me :(
Home... I remember that place... work is almost done then it's time for the pub (also GMT) ;)
I've been messing around with vbscript and perl on and off for about a year, most of the really useful bits in the last 6 months or so. I learnt the vast majority of it from Google ( :) ), but I was taught to program in Pascal during my brief stay at university which helps a lot with seeing what the hell is going on.
ADSI is quite a nice system though, once you know how to do one bit (like LDAP) then IIS and all the other services are dead easy - although it takes ages to get around the lack of any kind of predictable naming of values (WinNT's FullName doesn't exist in LDAP etc etc).
Hopefully there will be a nice Windows Scripting TA for me to hang around in when the site redesign is done :)
Going to the pub now :)
Chris
ASKER
Have a stella for me........................ .......... .......... ...
ASKER
Line: 116
Char:21
Error: Type mismatch
Code: 800A000D
Source: Microsoft VBScript runtime error
Is all I get :(
Char:21
Error: Type mismatch
Code: 800A000D
Source: Microsoft VBScript runtime error
Is all I get :(
Oops sorry... could you change line 116 to:
If datLastLogon > objList(strLogonName)(1) Then
Should have added, line 116 should currently have:
If datLastLogon > objList(strLogonName) Then
Unfortunately I don't have enough DCs to test that bit works correctly - rebuilding everything. So it may have to be rewritten slightly.
ASKER
changed line 16 to
If datLastLogon > objList(strLogonName)(1) Then
error now reads
Line 116
Char 21
Error Type mismatch 'objlist(...)'
Code 800A00D
Source Microsoft VBScript Error
If datLastLogon > objList(strLogonName)(1) Then
error now reads
Line 116
Char 21
Error Type mismatch 'objlist(...)'
Code 800A00D
Source Microsoft VBScript Error
Hmm sorry about this...
Could you change line 111 to:
datLastLogon = CDate(objUser.LastLogin)
Now the two values it compares should be the same, anything that's blank has already been discarded.
I really need more DCs to test this on. 3 domains, but only one DC in each while I fix them all...
ASKER
Hi Chris -
Ive been out on site most of today - hence the lack of movement on this, I'll give it a try in the morning - now get yer-sel down the pub :)
Ive been out on site most of today - hence the lack of movement on this, I'll give it a try in the morning - now get yer-sel down the pub :)
No problem, I should have a little more time to check into the bit that's breaking tomorrow anyway.
Fortunately the pub is only an hour away :)
ASKER
Game on :)
ASKER
Hi Chris
Sorry for the late follow up Ive changed line 111 qas recommended in http:Q_21410743.html#13927854 still it dont like line 116
Line 116
Char 21
Error Type mismatch 'objlist(...)'
Code 800A00D
Source Microsoft VBScript Error
Sorry for the late follow up Ive changed line 111 qas recommended in http:Q_21410743.html#13927854 still it dont like line 116
Line 116
Char 21
Error Type mismatch 'objlist(...)'
Code 800A00D
Source Microsoft VBScript Error
Hmm okay... I'll have another look, give me an hour or so :)
ASKER
cheers Chris
Pete
Pete
Okay, finished playing with it now. Hopefully all the bugs ironed out - tested against 2057 user accounts over 2 DCs with no errors and what seems to be relatively sane output - although it's one of those "start it and get a coffee" type scripts.
To avoid having annoying popup boxes appear (well 4 of them) run it with:
cscript <script name>
And it'll dump the wscript.echo's to the command line instead of through a message box - they're only there so you can see it's actually doing stuff in broad terms anyway.
Constants at the top as usual for file name and account inactivity period - I can make it request these or have them as command line parameters if you prefer though.
As a minor point of interest... it's also possible to pull out the lockout time if you wanted it.
Chris
Option Explicit
' Global Constants
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\disabled_locked.txt"
Const INACTIVE_PERIOD = 30
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim objList, objDomainControllers, objDomainController, objServer
Dim objLockout, objDate
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim strLockedOut, strEntry
Dim arrKeys, arrItems, arrTemp
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC, intCounter
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = ""
strFirstName = objUser.Get("givenName")
strLastName = ""
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = ""
strLogonName = objUser.Get("sAMAccountNam
strLastLogon = ""
strLastLogon = objUser.Get("lastLogin")
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
On Error Resume Next
Set objLockOut = objUser.lockoutTime
If Err.number <> 0 Then
' Do Nothing
Else
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
On Error Goto 0
Set objUser = Nothing
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objOrgUnit = Nothing
Set objFirst = Nothing
End Sub
Sub GetLastLogon(objDomainCont
' Retrieves the Last Logon Time from a DC
Dim objUsers, objUser, objTemp
Dim datLastLogon
Dim strDCName, strUserName
strDCName = Mid(objDomainController.Na
wscript.echo "Checking Last Logon for accounts on " & strDCName
Set objUsers = GetObject("WinNT://" & strDCName)
objUsers.Filter = Array("user")
For Each objUser In objUsers
' This is where Error Handling is needed
On Error Resume Next
strLogonName = ""
strLogonName = objUser.Name
strFullName = ""
strFullName = objUser.FullName
datLastLogon = ""
datLastLogon = objUser.LastLogin
On Error Goto 0
datLastLogon = Trim(datLastLogon)
If datLastLogon <> "" Then
arrTemp = Split(datLastLogon, " ")
datLastLogon = arrTemp(0)
datLastLogon = CDate(datLastLogon)
If objList.Exists(strLogonNam
If datLastLogon > objList(strLogonName)(1) Then
objList(strLogonName)(1) = datLastLogon
End If
Else
objList.Add strLogonName, Array(strFullName, datLastLogon)
End If
Set objUser = Nothing
End If
Next
Set objServer = Nothing
End Sub
'
' Main Code
'
' Allow us to get the naming context
Set objRootDSE = GetObject("LDAP://rootDSE"
' These will be incremented to 0 the first time they are used
intUserDisabled = -1
intUserLocked = -1
' Get Accounts that haven't logged on recently
Set objList = CreateObject("Scripting.Di
' Get the DC List
Set objDomainControllers = GetObject("LDAP://ou=domai
objDomainControllers.Filte
For Each objDomainController In objDomainControllers
GetLastLogon(objDomainCont
Next
Set objDomainControllers = Nothing
' Get Accounts that are Disabled or Locked Out
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
wscript.echo "Fetching Account status for all users - this may take some time"
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objDomainRoot = Nothing
Set objRootDSE = Nothing
' Initialize the File System Object, create a text file and write the report
wscript.echo "Writing Report"
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
objFile.WriteLine "Accounts that have not logged on in the last 30 days"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name" & Chr(9) & "Last Logon Time"
objFile.WriteLine ""
For Each strEntry In objList
If objList(strEntry)(1) < (Date() - INACTIVE_PERIOD) Then
objFile.WriteLine objList(strEntry)(0) & Chr(9) & strEntry & Chr(9) & objList(strEntry)(1)
End If
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Disabled"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Locked Out"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & Chr(9) & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
objFile.Close
Set objList = Nothing
Set objFileSystem = Nothing
ASKER
trying now
ASKER
Sweet Progress :)
not logged in for =>30 days works hurrah!!!
disabled account bit works, tip top!
locked out account's bit returned a LOT so I went and investigated
its listing accounts that are NOT locked
and its also listing Mail anabled contacts (which is strange)
not logged in for =>30 days works hurrah!!!
disabled account bit works, tip top!
locked out account's bit returned a LOT so I went and investigated
its listing accounts that are NOT locked
and its also listing Mail anabled contacts (which is strange)
Fun stuff... if an account has ever been locked out it populates the LockoutTime field - so that one is no good as a test for it. Let me find another way to do that and update it all.
Mail enabled contacts is a bit of an odd one, I assume they're coming up as user class, let me have a look around and see if I can filter them out.
Chris
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Tip top
I changed it to csv output and its running sweet as a nut - heres the full code just for PAQ Value
Option Explicit
' Global Constants
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\audit.csv"
Const INACTIVE_PERIOD = 30
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim objList, objDomainControllers, objDomainController, objServer
Dim objLockout, objDate
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim strLockedOut, strEntry
Dim arrKeys, arrItems, arrTemp
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC, intCounter
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = ""
strFirstName = objUser.Get("givenName")
strLastName = ""
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = ""
strLogonName = objUser.Get("sAMAccountNam e")
strLastLogon = ""
strLastLogon = objUser.Get("lastLogin")
On Error Goto 0
intUAC = objUser.Get("userAccountCo ntrol")
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis abled)
arrUserDisabled(intUserDis abled) = strFullName & "," & strLogonName
End If
If objUser.IsAccountLocked = True Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke d)
arrUserLocked(intUserLocke d) = strFullName & "," & strLogonName
End If
Set objUser = Nothing
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath )
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objOrgUnit = Nothing
Set objFirst = Nothing
End Sub
Sub GetLastLogon(objDomainCont roller)
' Retrieves the Last Logon Time from a DC
Dim objUsers, objUser, objTemp
Dim datLastLogon
Dim strDCName, strUserName
strDCName = Mid(objDomainController.Na me, 4, Len(objDomainController.Na me))
wscript.echo "Checking Last Logon for accounts on " & strDCName
Set objUsers = GetObject("WinNT://" & strDCName)
objUsers.Filter = Array("user")
For Each objUser In objUsers
' This is where Error Handling is needed
On Error Resume Next
strLogonName = ""
strLogonName = objUser.Name
strFullName = ""
strFullName = objUser.FullName
datLastLogon = ""
datLastLogon = objUser.LastLogin
On Error Goto 0
datLastLogon = Trim(datLastLogon)
If datLastLogon <> "" Then
arrTemp = Split(datLastLogon, " ")
datLastLogon = arrTemp(0)
datLastLogon = CDate(datLastLogon)
If objList.Exists(strLogonNam e) Then
If datLastLogon > objList(strLogonName)(1) Then
objList(strLogonName)(1) = datLastLogon
End If
Else
objList.Add strLogonName, Array(strFullName, datLastLogon)
End If
Set objUser = Nothing
End If
Next
Set objServer = Nothing
End Sub
'
' Main Code
'
' Allow us to get the naming context
Set objRootDSE = GetObject("LDAP://rootDSE" )
' These will be incremented to 0 the first time they are used
intUserDisabled = -1
intUserLocked = -1
' Get Accounts that haven't logged on recently
Set objList = CreateObject("Scripting.Di ctionary")
' Get the DC List
Set objDomainControllers = GetObject("LDAP://ou=domai n controllers," & objRootDSE.Get("defaultNam ingContext "))
objDomainControllers.Filte r = Array("computer")
For Each objDomainController In objDomainControllers
GetLastLogon(objDomainCont roller)
Next
Set objDomainControllers = Nothing
' Get Accounts that are Disabled or Locked Out
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam ingContext "))
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
wscript.echo "Fetching Account status for all users - this may take some time"
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objDomainRoot = Nothing
Set objRootDSE = Nothing
' Initialize the File System Object, create a text file and write the report
wscript.echo "Writing Report"
Set objFileSystem = CreateObject("Scripting.Fi leSystemOb ject")
Set objFile = objFileSystem.CreateTextFi le(FILE_NA ME, True, False)
objFile.WriteLine "Accounts that have not logged on in the last 30 days"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name" & "," & "Last Logon Time"
objFile.WriteLine ""
For Each strEntry In objList
If objList(strEntry)(1) < (Date() - INACTIVE_PERIOD) Then
objFile.WriteLine objList(strEntry)(0) & "," & strEntry & "," & objList(strEntry)(1)
End If
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Disabled"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Locked Out"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
objFile.Close
Set objList = Nothing
Set objFileSystem = Nothing
Many thanks for all your Help Chris - Ill repost here when I get round to asking the next Question :)
Pete
I changed it to csv output and its running sweet as a nut - heres the full code just for PAQ Value
Option Explicit
' Global Constants
Const ADS_UF_ACCOUNTDISABLE = &H2
Const FILE_NAME = "c:\audit.csv"
Const INACTIVE_PERIOD = 30
' Global Variable Declaration
Dim objRootDSE, objDomainRoot, objArg, objItem, objFileSystem, objFile
Dim objList, objDomainControllers, objDomainController, objServer
Dim objLockout, objDate
Dim strFirstName, strLastName, strFullName, strLogonName, strLine
Dim strLockedOut, strEntry
Dim arrKeys, arrItems, arrTemp
Dim arrUserDisabled()
Dim arrUserLocked()
Dim intUserLocked, intUserDisabled, intUAC, intCounter
'
' Subroutines
'
Sub ProcessUsers(objUsers)
' This routine takes an OU from OURecurse and checks the users to
' see if we find what we're looking for
Dim objUser
Dim strUserName, strTelephoneNumber, strFirstName, strInitials
Dim strSurname, strFullName, strOutputLine
objUsers.Filter = Array("user")
For Each objUser in objUsers
On Error Resume Next
strFirstName = ""
strFirstName = objUser.Get("givenName")
strLastName = ""
strLastName = objUser.Get("sn")
strFullName = strFirstName & " " & strLastName
strLogonName = ""
strLogonName = objUser.Get("sAMAccountNam
strLastLogon = ""
strLastLogon = objUser.Get("lastLogin")
On Error Goto 0
intUAC = objUser.Get("userAccountCo
If ADS_UF_ACCOUNTDISABLE and intUAC Then
intUserDisabled = intUserDisabled + 1
ReDim Preserve arrUserDisabled(intUserDis
arrUserDisabled(intUserDis
End If
If objUser.IsAccountLocked = True Then
intUserLocked = intUserLocked + 1
ReDim Preserve arrUserLocked(intUserLocke
arrUserLocked(intUserLocke
End If
Set objUser = Nothing
Next
End Sub
Sub OURecurse(objFirst)
' This OU is responsible for going through the AD Structure
Dim objOrgUnit, objItem
Set objOrgUnit = GetObject(objFirst.ADSPath
For Each objItem in objOrgUnit
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objOrgUnit = Nothing
Set objFirst = Nothing
End Sub
Sub GetLastLogon(objDomainCont
' Retrieves the Last Logon Time from a DC
Dim objUsers, objUser, objTemp
Dim datLastLogon
Dim strDCName, strUserName
strDCName = Mid(objDomainController.Na
wscript.echo "Checking Last Logon for accounts on " & strDCName
Set objUsers = GetObject("WinNT://" & strDCName)
objUsers.Filter = Array("user")
For Each objUser In objUsers
' This is where Error Handling is needed
On Error Resume Next
strLogonName = ""
strLogonName = objUser.Name
strFullName = ""
strFullName = objUser.FullName
datLastLogon = ""
datLastLogon = objUser.LastLogin
On Error Goto 0
datLastLogon = Trim(datLastLogon)
If datLastLogon <> "" Then
arrTemp = Split(datLastLogon, " ")
datLastLogon = arrTemp(0)
datLastLogon = CDate(datLastLogon)
If objList.Exists(strLogonNam
If datLastLogon > objList(strLogonName)(1) Then
objList(strLogonName)(1) = datLastLogon
End If
Else
objList.Add strLogonName, Array(strFullName, datLastLogon)
End If
Set objUser = Nothing
End If
Next
Set objServer = Nothing
End Sub
'
' Main Code
'
' Allow us to get the naming context
Set objRootDSE = GetObject("LDAP://rootDSE"
' These will be incremented to 0 the first time they are used
intUserDisabled = -1
intUserLocked = -1
' Get Accounts that haven't logged on recently
Set objList = CreateObject("Scripting.Di
' Get the DC List
Set objDomainControllers = GetObject("LDAP://ou=domai
objDomainControllers.Filte
For Each objDomainController In objDomainControllers
GetLastLogon(objDomainCont
Next
Set objDomainControllers = Nothing
' Get Accounts that are Disabled or Locked Out
Set objDomainRoot = GetObject("LDAP://" & objRootDSE.Get("defaultNam
' Objects like the Users OU is actually a container so it must be caught with the container check
' Other objects we're interested in are real OUs
' Not recursing through container objects (too messy, User Objects are containers)
wscript.echo "Fetching Account status for all users - this may take some time"
For Each objItem in objDomainRoot
If (objItem.Class = "container") Then
ProcessUsers objItem
End If
If (objItem.Class = "organizationalUnit") Then
ProcessUsers objItem
OURecurse objItem
End If
Next
Set objDomainRoot = Nothing
Set objRootDSE = Nothing
' Initialize the File System Object, create a text file and write the report
wscript.echo "Writing Report"
Set objFileSystem = CreateObject("Scripting.Fi
Set objFile = objFileSystem.CreateTextFi
objFile.WriteLine "Accounts that have not logged on in the last 30 days"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name" & "," & "Last Logon Time"
objFile.WriteLine ""
For Each strEntry In objList
If objList(strEntry)(1) < (Date() - INACTIVE_PERIOD) Then
objFile.WriteLine objList(strEntry)(0) & "," & strEntry & "," & objList(strEntry)(1)
End If
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Disabled"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name"
For Each strLine in arrUserDisabled
objFile.WriteLine strLine
Next
objFile.WriteLine ""
objFile.WriteLine "The following User Accounts are Locked Out"
objFile.WriteLine ""
objFile.WriteLine "Given Name" & "," & "Logon Name"
For Each strLine in arrUserLocked
objFile.WriteLine strLine
Next
objFile.Close
Set objList = Nothing
Set objFileSystem = Nothing
Many thanks for all your Help Chris - Ill repost here when I get round to asking the next Question :)
Pete
Pleasure Pete, glad it's all working :-D
I loaded hyena v8.1 to pull reports, but I keep getting RPC server not available. I have checked my services and the correct ones seems to be working. Any clues as why? I am logged in the domain controller and trying to hit my local computer (windows 7).
strDomainDN = "<DomainDN>" ' e.g. dc=rallencorp,dc=com
strBase = "<LDAP://" & strDomainDN & ">;"
strFilter = "(&(objectclass=user)(obje
"(useraccountcontrol:1.2.8
strAttrs = "name;"
strScope = "subtree"
set objConn = CreateObject("ADODB.Connec
objConn.Provider = "ADsDSOObject"
objConn.Open "Active Directory Provider"
set objRS = objConn.Execute(strBase & strFilter & strAttrs & strScope)
objRS.MoveFirst
while Not objRS.EOF
Wscript.Echo objRS.Fields(0).Value
objRS.MoveNext
wend
and here is an example of a locked account
https://www.experts-exchange.com/questions/20578437/Query-locked-out-accounts-in-AD.html?query=vbscript+locked+user+account&clearTAFilter=true
Hope this points you in the right direction
Manderson