One of the challenges in Active Directory is making sure that regular cleanup occurs in AD looking for inactive users and computers. In the past, for me, this has been an entirely manual process. It meant that I would take time once a quarter to generate reports, review the large number of users/computers and disable these accounts.
We have a process in our domain when it comes to disabling users. First, we disable the account. Second, we move the account to a new OU – called Pending Delete in our situation. Third, we update the description with information on when the account was disabled and who disabled it. Finally, we add some information to a field with the business justification – aka reason – that this account was disabled so that others can view this information prior to reenabling it. In our domain, we would look for user accounts that had not logged on to the domain for 90 days or more and computer accounts that had not logged onto the domain for 60 days or more. We did this by using a third-party tool to query all of our domain controllers looking at the lastlogon field. It would then look for the most recent logon time for each account and see if it was older than our criteria.
All of this has taken time and has essentially been a snapshot of AD on the day I ran the report. I know that it is most likely that the day after I run my report, another account will meet my criteria, but will not be disabled for another quarter. Also, this has always been a fairly time consuming process. Generating the reports and making all of the necessary updates usually took a couple of days to process (in the midst of other daily tasks I work on).
In reviewing my processes, I have decided to automate this process. In doing so, I knew I would need to change some of our methods. First, I didn’t want to query every single domain controller to find the last logon time for a user account. So, I decided to approach this in a different way. Since we upgraded to 2003 Functional Level, we have had a new attribute related to users logon. The field is called lastlogontimestamp and, unlike the lastlogon attribute, it is replicated throughout the domain. To prevent large churn on this attribute that could cause problems with excessive bandwidth usage, this attribute is updated every 14 days minus 5 random days. This means that this attribute will be updated every 9-14 days when a user logs on. (Note that this attribute does not get updated with simple binds such as might happen with a service account.) For more details on how this process works, I would recommend reading the following from AD guru joe: http://blog.joeware.net/2007/05/01/864/ .
Using the new field, I can now have a script run every night to look for user accounts that are older than 104 days (90 days + the 14 day lag time on the lastlogontimestamp field) and computers that are older than 74 days (60 days + 14 days). I have the script so that it only runs against user accounts, not our service or administrative accounts. We have also implemented a proxy system for making changes to AD. This is a “home-built” proxy system following instructions from the excellent book by Dan Holme Windows Administration Resource Kit: Productivity Solutions for IT Professionals . In this case, this script creates a text file for each user that needs to be disabled. We have a process that monitors this folder for new files and then processes any files dropped there. This script also looks for accounts where a user has never logged on. If a user has never logged on, the date in the lastlogonTimestamp field will show a date of 1/1/1601. Since this would obviously always be more than our timeframe, we want to handle these accounts specially (in case the account was just created in the last couple of days for a new user, but they haven’t logged on yet.) In this situation, we look for the create date. Then, if the create date is older than our timeframe, it will get disabled. Here is the initial script that runs automatically:
'==========================================================================
'
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2009
'
' NAME: AutoDisableInactiveCMP.vbs
'
' AUTHOR: Doug Neely , The Salvation Army
' DATE : 9/11/2009
'
' COMMENT: This script is set to be run automatically against user accounts.
' It will find user accounts with a lastLogonTimeStamp (which is a
' replicated value) greater than 104 days (90 days +14 days variance
' for when the value is updated). It will then generate a request
' and place it into the Requests folder to be processed by the proxy.
' It is also set to ignore the commissioner's account.
'
'==========================================================================
Dim strRootDomain, LastLogonTimeFrame, sRequestPath, sCommand
Dim objRootDSE, adoConnection, adoCommand, strQuery
Dim adoRecordset, strDNSDomain, objShell, lngBiasKey
Dim lngBias, k, strDN, dtmDate, objDate
Dim strBase, strFilter, strAttributes, lngHigh, lngLow
Dim intLogonAge, strCreated, intCreated, sNeverLoggedOn
' ============================================================================
' CONFIGURATION BLOCK
strRootDomain="ou=Workstations,dc=contoso,dc=org"
LastLogonTimeFrame=104
sRequestPath = "C:\proxy\test"
sCommand = "AutoDisableUSR_CMP_ByProxy"
' ============================================================================
' 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
Set objShell = Nothing
' Determine DNS domain from RootDSE object.
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
Set objRootDSE = Nothing
' Use ADO to search Active Directory.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection
' Search entire domain.
strBase = "<LDAP://" & strRootDomain & ">"
' Filter on all user objects.
strFilter = "(&(objectCategory=computer)(objectClass=computer))"
' Comma delimited list of attribute values to retrieve.
strAttributes = "distinguishedName,lastLogonTimeStamp,whencreated"
' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
' Run the query.
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 100
adoCommand.Properties("Timeout") = 60
adoCommand.Properties("Cache Results") = False
Set adoRecordset = adoCommand.Execute
' Enumerate resulting recordset.
Do Until adoRecordset.EOF
' Retrieve attribute values for the user.
strDN = adoRecordset.Fields("distinguishedName").Value
' Convert Integer8 value to date/time in current time zone.
strCreated = adoRecordset.Fields("whencreated").value
On Error Resume Next
Set objDate = adoRecordset.Fields("lastLogonTimeStamp").Value
If (Err.Number <> 0) Then
On Error GoTo 0
dtmDate = #1/1/1601#
Else
On Error GoTo 0
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
intLogonAge = DateDiff("d", dtmDate, Now)
intCreated = DateDiff("d", strCreated, Now)
' Display values for the user.
If (dtmDate = #1/1/1601#) Then
If intCreated > LastLogonTimeFrame Then
sNeverLoggedOn = intCreated & "(never logged on)"
Call disableUserProxy(strDN, sRequestPath, sCommand, sNeverLoggedOn)
Wscript.Echo strDN & sNeverLoggedOn
End If
Else
If intLogonAge > LastLogonTimeFrame Then
Call disableUserProxy(strDN, sRequestPath, sCommand, intLogonAge)
'WScript.Echo strDN & ";" & intLogonAge
End If
'Wscript.Echo strDN & ";" & intLogonAge
End If
adoRecordset.MoveNext
Loop
Sub disableUserProxy(strDN, sRequestPath, sCommand, intLogonAge)
Dim sJustification, strUserAdmin, strDescription, dtmDescriptionDate, strLDAPConnection, sRequestor
Set objNetwork = CreateObject("Wscript.Network")
sJustification = "Auto Disabled by Proxy - Inactive " & intLogonAge & " days."
strUserAdmin = "Auto Proxy"
dtmDescriptionDate = MonthName(Month(Now())) &" " & Day(Now()) &" " & Year(Now())
strDescription = "Disabled on " & dtmDescriptionDate & " by " & strUserAdmin
strLDAPConnection = "LDAP://" & strDN
Dim sParameters, sRequestorLine
'sRequestPath = "C:\Proxy\Requests"
'sCommand = disableUserProxy
sParameters = "/user:""" & strLDAPConnection & """ " & _
"/description:""" & strDescription & """ " & _
"/justification:""" & sJustification & """"
Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
Dim sFilename, sFilespec, oFile
sFilename = FSO.GetTempName
sFilename = Replace(sFilename, ".tmp", ".txt")
sFilespec = sRequestPath & "\" & sFilename
'On Error Resume Next
Set oFile = FSO.CreateTextFile(sFilespec)
If Err.Number <> 0 Then
sMessage = "An error occurred while attempting to submit the request " & _
"to disable " & strUserName & ". " & vbCrLf & _
"Error " & Err.Number & ": " & Err.Description & VbCrLf & _
"This is probably due to a permissions problem. Check to see that you " & _
"have permission to create files in the folder " & sRequestPath & "."
MsgBox sMessage
Err.Clear
Exit Sub
End If
oFile.WriteLine sCommand
oFile.WriteLine sParameters
oFile.Close
Dim sMessage
If Err.Number <> 0 Then
sMessage = "An error occurred while attempting to submit the request " & _
"to disable " & strUserName & ". " & VbCrLf & _
"Error " & Err.Number & ": " & Err.Description
MsgBox sMessage
Err.Clear
Else
sMessage = "Successfully submitted the request " & _
"to disable " & strUserName & ". " & VbCrLf & _
"There will be a delay before the request is processed by " & _
"the proxy service."
WScript.Echo sMessage
End If
End Sub
' Clean up.
adoRecordset.Close
adoConnection.Close
Set adoConnection = Nothing
Set adoCommand = Nothing
Set adoRecordset = Nothing
Set objDate = Nothing
It then disables the user/computer account and updates a database with information about this change. The process that disables the user/computer account moves the account to a new OU (we call ours Pending Delete), updates the description with the date the account was disabled and who disabled it and it adds information to the user account detailing that this account was disabled and how many days it was inactive. Upon successful completion, it then creates another text file that we use for recording these changes. Here is the script run by the proxy:
'==========================================================================
'
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 2009
'
' NAME: AutoDisableUSR_CMP_ByProxy.vbs
'
' AUTHOR: Doug Neely
' DATE : 7/17/2009
' MODIFIED : 09/12/2009
' Modified this script to handle the Autodisable by the Proxy service.
'
' COMMENT: This script is set to be run by the proxy account. It will disable
' the user/computer and move it to the Pending Delete OU. It also
' places the justification into the notes field.
'
'==========================================================================
Option Explicit
' DERIVE DOMAIN INFORMATION
Dim adSys, sComputerDN, sDomainDNS, sDomainNetBIOS, sDomainDN
Set adSys = CreateObject("ADSystemInfo")
sComputerDN = adSys.ComputerName
sDomainDN = Mid(sComputerDN, InStr(lcase(sComputerDN),",dc=") +1 )
sDomainDNS = adSys.DomainDNSName
sDomainNetBIOS = adSys.DomainShortName
Dim sUser, sDescription, sJustification, sRequestorSAM, bInteractive, objUser, objOU
Dim objNetwork, sLogPath, sTask
' ============================================================================
' CONFIGURATION BLOCK
bInteractive = True ' TRUE: Script can be run from command prompt.
' FALSE: Limits the output of the script (for running unattended).
Const ADS_PROPERTY_CLEAR = 1
Const ADS_PROPERTY_UPDATE = 2
Const ADS_PROPERTY_APPEND = 3
Const ADS_PROPERTY_DELETE = 4
Const pendingDeletionLDAP = "LDAP://ou=Pending Delete,dc=contoso,dc=org"
sLogPath = "C:\IT\Proxy\Logs"
sTask = "AutoDisableUSR_CMP"
' ============================================================================
Dim sGroupADsPath, sMemberADsPath, sRequestorDN, oGroup, oUser, oRequestor
Call MainRoutine() ' Code is contained in a subroutine to make it easier to port to an HTA
WScript.Quit
Sub MainRoutine()
Call Arguments() ' Take what was passed to the script
Call BusinessLogic() ' Apply business logic
If Not Validate() Then WScript.Quit(500)
If Not Validate_Requestor() Then WScript.Quit(500)
If DisableUser(sUser, sDescription, sJustification) Then
Call DatabaseRecord()
WScript.Echo "Successfully disabled user."
WScript.Echo "User: " & sUser
Else
WScript.Echo "Failed to disable user."
WScript.Echo "User: " & sUser
WScript.Quit(500)
End If
End Sub
Sub Arguments()
Dim oArgs
' Check for "HELP"
If (WScript.Arguments.Count = 0) Or _
(WScript.Arguments.Named.Exists("?")) Then Call Usage()
Set oArgs = WScript.Arguments.Named
sUser = oArgs("user")
sDescription = oArgs("description")
sJustification = oArgs("justification")
sRequestorSAM = oArgs("requestedBy")
End Sub
Sub BusinessLogic()
End Sub
Function Validate()
Validate = False
If (sUser="") Then
WScript.Echo "sUser missing."
Exit Function
End If
If (sDescription="") Then
WScript.Echo "sDescription missing."
Exit Function
End If
If (sJustification="") Then
WScript.Echo "sJustification missing."
Exit Function
End If
If (sRequestorSAM="") Then
WScript.Echo "sRequestorSAM missing."
Exit Function
End If
Validate = True
End Function
Function Validate_Requestor()
Dim dMembers
Dim sTemp
Validate_Requestor = False
Dim sManagedBy, oManagedBy
If sRequestorSAM = "BUILTIN\Administrators" Then
sRequestorSAM = "Contoso\SVC_ADProxy"
End If
' Get requestor's DN
sRequestorSAM = Replace(sRequestorSAM, sDomainNetBIOS & "\", "")
sRequestorDN = Replace(ADObject_Find_Generic(sRequestorSAM, sDomainDN), "LDAP://", "")
Set oRequestor = ADObject_Get_Generic(sRequestorDN, sDomainDN)
If oRequestor Is Nothing Then
WScript.Echo "RequestedBy invalid."
Exit Function
End If
Validate_Requestor = True
End Function
Sub Usage()
If bInteractive Then
WScript.Echo
WScript.Echo "GROUP_ADDMEMBER.VBS"
WScript.Echo "Adds a member to a group"
WScript.Echo "Usage: cscript.exe " & WScript.ScriptName & VbCrLf & _
" /groupadspath:""LDAP://CN=...""" & VbCrLf & _
" /memberadspath:""LDAP://CN=...""" & VbCrLf & _
" /justification:""business justification""" & VbCrLf & _
" /requestedby:""cn=user name,ou=...,dc=contoso,dc=com""" & VbCrLf
End If
WScript.Quit(500)
End Sub
' #region Active Directory object find routines
Function ADObject_Get_Generic(ByVal sObject, ByVal sSearchDN)
' Version 071204
' Takes any input (name, DN, or ADsPath) of a user, computer, or group, And
' returns an object reference to the object, or nothing if it does not exist
' This is a wrapper around ADObject_Find_Generic.
' Requires ADObject_Find_Generic.
' See notes for ADObject_Find_Generic
Dim oADObject, sADsPath
sADsPath = ADObject_Find_Generic(sObject, sSearchDN)
If sADsPath > "" Then
Set ADObject_Get_Generic = GetObject(sADsPath)
Else
Set ADObject_Get_Generic = Nothing
End If
End Function
Function ADObject_Find_Generic(ByVal sObject, ByVal sSearchDN)
' Version 071130
' Takes any input (name, DN, or ADsPath) of a user, computer, or group, and
' returns the ADsPath of the object as a way of validating that the object exists
'
' INPUTS: sObject DN or ADsPath to an object
' sAMAccountName (pre-Windows 2000 logon name) of a user or group
' computer name of a computer
' sSearchDN the DN within which to search (often, the DN of the domain, e.g. dc=contoso, dc=com)
'
' RETURNS: ADObject_Find_Generic ADsPath (LDAP://...) of the object
' blank if object was not found
'
' NOTES: ASSUMPTION: computers, users & groups have unique names. See note inline.
'
' REQUIRES AD_Search_Array routine
' AD_Search_RS routine
' ADObject_Validate routine
Dim aResults, sLDAPQuery
Select Case ADObject_NameType(sObject)
Case ""
ADObject_Find_Generic = ""
Case "adspath"
ADObject_Find_Generic = ADObject_Validate(sObject)
Case "distinguishedname"
ADObject_Find_Generic = ADObject_Validate("LDAP://" & sObject)
Case "name"
' Assumption: No computer has the same name as a user's or group's sAMAccountName
' otherwise, this query will return more than one result
sLDAPQuery = "<LDAP://" & sSearchDN & ">;" & _
"(|(samAccountName=" & sObject & ")(samAccountName=" & sObject & "$));" & _
"aDSPath;subtree"
aResults = AD_Search_Array (sLDAPQuery)
If Ubound(aResults) = -1 Then
ADObject_Find_Generic = ""
Else
ADObject_Find_Generic = aResults(0)
End If
End Select
End Function
Function ADObject_NameType(ByVal sObjectName)
' Version 071130
' Evaluates sObjectName to determine what type of name it is
' Returns ADObject_NameType adspath
' distinguishedname
' name
' blank if sObjectName = ""
Dim sNameType
If Len(sObjectName) = 0 Then
sNameType = ""
ElseIf Len(sObjectName) < 3 Then
' can't be a DN or an ADsPath - must be a name
sNameType = "name"
ElseIf lcase(Left(sObjectName,3)) = "cn=" Then
' is a DN
sNameType = "distinguishedname"
ElseIf Len(sObjectName) < 8 Then
' too short to be an ADsPath and isn't a DN, so it must be a name
sNameType = "name"
ElseIf ucase(Left(sObjectName, 7)) = "LDAP://" Then
' is already an ADsPath
sNameType = "adspath"
Else
' must be a name
sNameType = "name"
End If
ADObject_NameType = sNameType
End Function
Function ADObject_Validate(ByVal sObjectADsPath)
' Version 071122
' Returns ADsPath of object as a way of validating that the object exists
'
' INPUTS: sObjectADsPath ADsPath of object to test
' RETURNS: ADObject_Validate Path of object (if it exists) or blank
Dim oObject
On Error Resume Next
Set oObject = GetObject(sObjectADsPath)
If Err.Number <> 0 Then
ADObject_Validate = ""
Err.Clear
Else
ADObject_Validate = oObject.ADsPath
End If
End Function
Function AD_Search_Array(sLDAPQuery)
' Version 071130
' Returns an array of AD objects
' Inputs: sLDAPQuery The query to perform
' Returns AD_Search_Array Array of Field(0) of recordset
' or empty array if recordset is empty
Dim rs, aResults
aResults = array()
Set rs = AD_Search_RS(sLDAPQuery)
If Not(rs Is Nothing) Then
If Not (rs.BOF And rs.EOF) Then
rs.MoveFirst
Do While Not rs.EOF
ReDim Preserve aResults(ubound(aResults) + 1)
aResults(ubound(aResults)) = rs.Fields(0)
rs.MoveNext
Loop
End If
Else
' Error handling: query returned Nothing
End If
AD_Search_Array = aResults
End Function
Function AD_Search_RS(sLDAPQuery)
' Version 071130
' Returns a recordset of AD objects
' Inputs: sLDAPQuery The query to perform
' Returns: AD_Search_RS Recordset
Dim oConnection
' Open an ADO connection using null credentials
Set oConnection = CreateObject("ADODB.Connection")
oConnection.Provider = "ADsDSOObject"
oConnection.Open "", vbNullString, vbNullString
If oConnection.State = 0 Then ' 0 = adStateClosed
' Error handling code: can't connect to AD
Set AD_Search_RS = Nothing
Else
Set AD_Search_RS = oConnection.Execute(sLDAPQuery)
End If
End Function
' #endregion
Function DisableUser(sUser, sDescription, sJustification)
'I'm adding this here to see if it will get my description right
Set objNetwork = CreateObject("Wscript.Network")
Set objUser = GetObject(sUser)
objUser.Put "userAccountControl", 2
objUser.PutEx ADS_PROPERTY_UPDATE, "description", Array(sDescription)
objUser.Put "msnpAllowDialin", "FALSE"
objUser.Put "info", "Disabled: " & sJustification
objUser.SetInfo
Set objOU = GetObject(pendingDeletionLDAP)
objOU.MoveHere sUser, vbNullString
objUser.SetInfo
If Err.Number > 0 Then
' Handle error: could not add member
WScript.Echo "Error creating user: " & Err.Description
DisableUser = False
Err.Clear
Else
DisableUser = True
End If
On Error GoTo 0
End Function
Sub DatabaseRecord()
'This section creates a log for the database
Dim strUserAdmin, sMessage2
Dim FSO : Set FSO = CreateObject("Scripting.FileSystemObject")
strUserAdmin = sRequestorSAM
Dim FSO2 : Set FSO2 = CreateObject("Scripting.FileSystemObject")
Dim sFilename2, sFilespec2, oFile2
sFilename2 = FSO.GetTempName
sFilename2 = Replace(sFilename2, ".tmp", ".txt")
sFilespec2 = sLogPath & "\" & sFilename2
On Error Resume Next
Set oFile2 = FSO.CreateTextFile(sFilespec2)
If Err.Number <> 0 Then
sMessage2 = "An error occurred while attempting to submit the request " & _
"to create a log for the creation of " & sGroupName & ". " & vbCrLf & _
"Error " & Err.Number & ": " & Err.Description & VbCrLf & _
"This is probably due to a permissions problem. Check to see that you " & _
"have permission to create files in the folder " & sLogPath & "."
MsgBox sMessage2
Err.Clear
Exit Sub
End If
'Note that we need 5 lines for this txt file. The first is the task, the second is the date,
'the third is user/computer this was done on, the forth is who did it and finally any notes
'(justification, etc...).
oFile2.WriteLine sTask
oFile2.WriteLine Now
oFile2.WriteLine sUser
oFile2.WriteLine strUserAdmin
oFile2.WriteLine sJustification
oFile2.Close
'Dim sMessage
If Err.Number <> 0 Then
sMessage2 = "An error occurred while attempting to submit the request " & _
"to create a log for " & sGroupName & ". " & VbCrLf & _
"Error " & Err.Number & ": " & Err.Description
MsgBox sMessage2
Err.Clear
Else
sMessage2 = "Successfully submitted the request " & _
"to create a log for" & sGroupName & ". " & VbCrLf & _
"There will be a delay before the request is processed by " & _
"the proxy service."
MsgBox sMessage2
End If
End Sub
Now, we have an automatic way to keep on top of stale accounts. If we find the occasional account that is performing a simple bind and shouldn’t be disabled, we are able to move those accounts to a new OU where we are able to treat it differently.
Dave
Thanks for this script.
I am a bit stuck though.
As I understand it I run the first script and it identifies users that should be disabled and creates a text file.
Then I run the second script and it looks at those text files and disables the corresponding user account.
The first script is working after some modification for my environment and some extra code to cause it to ignore DNs containing certain text strings.
I can’t figure out how the second script utilizes those text files to know what to disable.
Am I way off base?
Thanks,
Dave
Doug
In our environment, I have implemented a proxy system. This enables me to restrict access rights for our technicians so that they can only make most changes using a front end script. I have front end scripts for things such as resetting passwords (we have to use a randomly created password for all password resets to comply with PCI rules), creating users, disabling users, etc… These front end scripts all write to a text file in a designated location.
The next step of the process is to handle these files. I have an automated process that looks for new files created (it finds new files created in about five seconds or so). When it finds a new file, it grabs the information from the file and writes it to a database. It then processes the script using the fields it wrote to a database and allows me to document who requested what change in AD when without an expensive AD monitoring tool.
I know this sounds like a very complicated method, but it has worked really well for us and allowed me to create a poor man’s method for managing AD without giving techs too many permissions. (It also enables me to make sure that things such as new users or disabling users happens in a very consistent manner by forcing techs to use my front end scripts to do the work.) You can find out more about this proxy method by reading the book referenced above (Windows Administration Resource Kit: Productivity Solutions for IT Professionals)
I have recently moved a lot of this over to PowerShell now, so I will do a blog article shortly about my new PowerShell proxy system, as well as the new scripts I use now for disabling users automatically. It is much simpler (and a lot less code) using PowerShell now.
Dave
Thanks Doug.
The second script is the one that I am trying to understand.
I don’t need to bother with the proxy stuff I don’t think.
How does the second script know where to look for the text files created by the first script?
The text files are created in C:\Proxy\Test but in the second script I don’t see any reference to that directory.
Thanks Much 🙂
Dave
Doug
Well, this vbscript method really only works well with the proxy system because it writes straight to file. You could combine the two scripts into a single script by replacing the write to file section with the script that actually makes the modification.
Honestly today, I much prefer PowerShell for this as it is much easier to accomplish what I want to accomplish. I have written a new blog article today about using PowerShell to disable inactive users. Let me know if that helps.
Dave
I have yet to bite into Powershell but it is the way of the future.
I am modifying the first script to disable and move the accounts as long as they are not in several OUs I have specified and log it to a file.
I also am modding it to ignore disabled accounts and accounts where the password never expires (Service accounts)
I appreciate the work you put into this and making it available.
Send me an E-Mail at my yahoo account and I can give you a link to my download page for Copyrite XP for free.
It’s a really nice GUI for Robocopy and a $19.95 value.
It works well with all MS OS from 2000 up to Server 2012, Windows 8 32/64.
Your script saved me well over that amount 🙂
Thanks Doug,
Dave