Did you find this useful?
Socialize it today.

Locating Inactive Computer Accounts in AD

Friday, October 28, 2011

When it comes to computer accounts, unfortunately, they seem to linger around even when computers are removed from the domain and retired. Overtime, the number of computer accounts in an Active Directory (AD) domain will get larger and larger. It is very difficult, using 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 removes 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 now 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 travelling 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 LIST***
'***DELETE mode will delete computer objects!!! Use Caution***
'***LIST mode will output to a file. However, it is easy to modify the script to***
'***write to a database.***

'***Set the scope of the script by using the correct starting point in the structure***
'***Change 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)
txtStream.WriteLine objRSADSI.Fields("name") & "," & dtmPwdLastSet & "," & DiffADate & ",NOT DELETED"
End If

'***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
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


Please help us spread the word by socializing it today!

email contact us

Did you find something wrong with the information on this page? Please take a moment to report it to us so that we can continue to improve the quality of the information on this site. Click here to report an issue with this page.

Recommended Books & Training Resources

MCITP Windows Server 2008 Enterprise Administrator: Training Kit 4-Pack: Exams 70-640 70-642 70-643 70-647 Windows Server 2008 R2 Unleashed