When it comes to computer accounts, unfortunately, they seem to linger around even when computers are removed from the domain and retired. Over time, the number of computer accounts in an Active Directory (AD) domain will get larger and larger. It is very difficult to use the out-of-box tools available when you install AD to manage these inactive accounts.

There are no built-in processes monitoring these stale objects and removing them when they are no longer being used. So the question you may be asking is how I effectively remove these inactive objects. First, we need to understand what categorizes an “inactive computer” object. Well, it turns out that these Windows computers will automatically reset their “computer password” at least once every thirty days.

While this number can be modified, it is generally not recommended. Since we know that the computer’s password age attribute is automatically modified at least once every thirty days, all we have to do is query Active Directory and apply some logic in code. There are various ways to extract and handle the data from AD, but I think using VBScript for this solution works quite nicely.

Here is an example of code that will query your domain, at any level (OU) of the structure, and allows you to choose the maximum age, and whether or not to run in DELETE or LIST mode. Choosing 60 or 90 days for a maximum age is pretty safe as it accounts for staff that may be on vacation or traveling for an extended period of time.

This script can be used to locate inactive computer accounts in an Active Directory domain

The script can be run in 2 modes, DELETE and LISTDELETE mode will delete computer objects!!! Use CautionLIST mode will output to a file. However, it is easy to modify the script towrite to a database.

Set the scope of the script by using the correct starting point in the structureChange the cnPath to your needs. It can be set to the domain object level or OU cnPath = “OU=ouName,DC=domain,DC=com”

Computers in the domain by default update their passwords once every thirty days ‘***When you run the program, this number should be greater than 30 ** ‘targetting systems that may still be active. limit = inputbox(“Enter maximum password age”) DeleteMode = Msgbox(“Do you wish to run in DELETE mode?”, vbYesNo)

Setup Log file Set fso = CreateObject(“Scripting.FileSystemObject”)

The 8 in this line will append to an existing file, replace with a 2 to override set txtStream = fso.OpenTextFile(“System.txt”, 8, True) txtStream.WriteLine “*** Ran on " & Date & " ***”

Setup ADSI connection and populate ADSI Collection Set objADOconnADSI = CreateObject(“ADODB.Connection”) objADOconnADSI.Open “Provider=ADsDSOObject;” Set objCommandADSI = CreateObject(“ADODB.Command”) objCommandADSI.ActiveConnection = objADOconnADSI

There is a 1000 object default if these next 2 lines are omitted objCommandADSI.Properties(“Size Limit”)= 10000 objCommandADSI.Properties(“Page Size”)= 10000 objCommandADSI.Properties(“Sort on”) = “sAMAccountName” objCommandADSI.CommandText = “<LDAP://” & cnPath & “>;(objectClass=computer);sAMAccountName,pwdLastSet,name,distinguishedname;subtree” Set objRSADSI = objCommandADSI.Execute

Loop through record set and compare password age do while NOT objRSADSI.EOF if not isnull(objRSADSI.Fields(“distinguishedname”)) and objRSADSI.Fields(“distinguishedname”) <> "" then objDate = objRSADSI.Fields(“PwdLastSet”)

Go to the function to translate the PwdLastSet value from AD for the machine account. dtmPwdLastSet = Integer8Date(objDate, lngBias)

calculate the current age of the password. DiffADate = DateDiff(“d”, dtmPwdLastSet, Now)

Is the password older than the specified age? if DiffADate > int(limit) then

Is the script running in DELETE mode? if DeleteMode = vbYes then

Ask if this machine account should be deleted from AD. ‘*** Uncomment next line and comment the following line to not be prompted for deletion of each object.*** ‘intReturn = vbYes intReturn = Msgbox(“Are you sure you want to DELETE this computer account?”, vbYesNo, “Delete " & objRSADSI.Fields(“name”))

If YES, then write to log file and then DELETE it else just log it. If intReturn = vbYes Then Set objComputer = GetObject(“LDAP://” & objRSADSI.Fields(“distinguishedname”)) strComputer = objComputer.CN

txtStream.WriteLine objRSADSI.Fields(“name”) & “,” & dtmPwdLastSet & “,” & DiffADate & “,DELETED” objComputer.DeleteObject (0) else txtStream.WriteLine objRSADSI.Fields(“name”) & “,” & dtmPwdLastSet & “,” & DiffADate & “,NOT DELETED” End If else

If running in LIST mode then just write entry to log file and move on to next record. txtStream.WriteLine objRSADSI.Fields(“name”) & “,” & dtmPwdLastSet & “,” & DiffADate & “,LIST ONLY” end if end if end if objRSADSI.MoveNext loop wscript.echo “Process Completed.”

‘*** Function to convert Integer8 (64-bit) value to a date, adjusted for local time zone.*** Function Integer8Date(objDate, lngBias) Dim lngAdjust, lngDate, lngHigh, lngLow lngAdjust = lngBias lngHigh = objDate.HighPart lngLow = objdate.LowPart

Account for IADslargeInteger property methods. If lngLow < 0 Then lngHigh = lngHigh + 1 End If If (lngHigh = 0) And (lngLow = 0) Then lngAdjust = 0 End If lngDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _

  • lngLow) / 600000000 - lngAdjust) / 1440 Integer8Date = CDate(lngDate) End Function

EndofScript