Skip to content

Instantly share code, notes, and snippets.

@morgansimonsen
Created December 19, 2013 14:34
Show Gist options
  • Save morgansimonsen/8040007 to your computer and use it in GitHub Desktop.
Save morgansimonsen/8040007 to your computer and use it in GitHub Desktop.
Find outdated computer Objects in Active Directory
' FindOutdatedComputers.vbs
' by Morgan Simonsen
' http://morgansimonsen.wordpress.com
'
' This script will search an Active Directory domain for computer accounts that have
' not logged on the domain in the specified time limit (default 60 days).
'
' 600 000 000 100-nanosecond intervals in 1 minute
' 1440 minutes in 24-hours
' 30 * 1440 * 600 000 000 = time in 100-nanosecond intervals since 1.1.1601
'
' For Windows 2000, Windows XP and Windows Server 2003, the default computer account password change is 30 days,
' on Windows NT-based computers, the machine account password automatically changes every seven days
'
' http://support.microsoft.com/kb/q154501/
' http://support.microsoft.com/default.aspx?scid=kb;en-us;q175468
' http://www.microsoft.com/technet/scriptcenter/topics/win2003/lastlogon.mspx
'
' USAGE:
' cscript.exe FindOutdatedComputerObjects_vXXX.vbs <0|1 only list or list and move objects, default is list only>
'
' Changelog:
'
' Version 1.7 (20120228)
' - Added detection of script host (WSCRIPT.EXE/CSCRIPT.EXE)
'
' Version 1.6 (20120213)
' - Added back ability to specify domain and DC.
' Autodetection worked well for forests with only one domain.
'
' Version 1.5 (20111215)
' - Removed version from script name, this is now stored in the strScriptVersion attribute.
' - Added automatic discovery of DC
' - Added detection for FSMO roles
'
' Version 1.4 (20111201)
' - Added automatic discovery of domain
'===========================
' User changeable variables
'===========================
'Should computers be listed only or listed and moved?
strMove = 0 '1 or 0; 0 lists outdated computers only, 1 lists and moves (Default value, can be overridden with command line parameters)
'Time limit in number of days
intTimeLimit = 180
strDomain = "dc=mydomain,dc=com"
strDC = "dc1.mydomain.com"
'====================================
' Make no changes beyond this point!
'====================================
strScriptVersion = "1.7"
strScriptDate = "2012-02-28"
' See if we are running with WSCRIPT.EXE or CSCRIPT.EXE
DetectScriptEngine()
Set objRootDSE = GetObject("LDAP://RootDSE")
'strDomain = objRootDSE.Get("defaultNamingContext")
'strDC = objRootDSE.Get("dnsHostName")
strOutdatedObjectsOURDN = "OU=Outdated computer objects"
strSearchFilter = "(objectClass=computer)"
strAttributes = "name,distinguishedName,operatingSystem,dNSHostName" 'Comma separated
strLevel = "subtree"
strReboot = 0
Dim count
Set objArgs = WScript.arguments
If objArgs.Count = 0 Then
WScript.Echo "No arguments submitted, using default values"
Else
strMove = objArgs.item(0)
End If
Set objWSHShell = WScript.CreateObject("WScript.Shell")
' Echo script info
WScript.Echo "FindOutdatedComputers.vbs"
WScript.Echo "by Morgan Simonsen (www.simonsen.bz)"
WScript.Echo "Script version : " & strScriptVersion
WScript.Echo "Script date : " & strScriptDate
WScript.Echo
If CheckDomainLevel(strDomain) = False Then
WScript.Echo "Your domain is not in Windows 2003 Mode. This mode is required to check last logon time."
WScript.Echo "Attribute: lastLogonTimestamp"
WScript.Quit
End If
Dim strFSMOSchemaMaster
Dim strFSMOInfrastructureMaster
Dim strFSMOPDCEmulator
Dim strFSMORIDMaster
Dim strFSMODomainNamingMaster
Call ConfigureOU()
Call FindFSMOOwners()
'WScript.Echo "time is:" & Now - #1/1/1601#
Set objADODBConnection = CreateObject("ADODB.Connection")
objADODBConnection.Provider = "ADsDSOObject"
objADODBConnection.Open
Set objADODBCommand = CreateObject("ADODB.Command")
Set objADODBCommand.ActiveConnection = objADODBConnection
objADODBCommand.Properties("Page Size") = 500
'objADODBCommand.CommandText = "<LDAP://" & strDC & "/" & strDomain & ">;" & strSearchFilter & ";" & strAttributes & ";" & strLevel
objADODBCommand.CommandText = "<LDAP://" & strDomain & ">;" & strSearchFilter & ";" & strAttributes & ";" & strLevel
Set objRecordSet = objADODBCommand.Execute
count = 0
While Not objRecordset.EOF
'Wscript.Echo objRecordset.Fields("name")
Call GetLastLogonTime(objRecordset.Fields("distinguishedName"))
objRecordset.MoveNext
Wend
WScript.Echo "Total number of computers in domain: " & objRecordset.RecordCount
WScript.Echo "Number of computers that have not logged on in " & intTimeLimit & " days: " & count
objADODBConnection.Close
Function GetLastLogonTime(strComputerDN)
On Error Resume Next
set objComputer = GetObject("LDAP://" & strDC & "/" & strComputerDN)
'WScript.Echo "Computer: " & objComputer.cn
set objLogon = objComputer.Get("lastLogonTimestamp")
intLogonTime = objLogon.HighPart * (2^32) + objLogon.LowPart
intLogonTime = intLogonTime / (60 * 10000000)
intLogonTime = intLogonTime / 1440
'WScript.Echo "Approx last logon timestamp: " & intLogonTime + #1/1/1601#
If intLogonTime + #1/1/1601# < Now - intTimeLimit Then
Wscript.Echo "Computer: " & objRecordset.Fields("name")
WScript.Echo " Has not logged on in " & intTimeLimit & " days"
WScript.Echo " Approx last logon timestamp: " & intLogonTime + #1/1/1601#
If strMove = 1 Then
WScript.Echo " Moving computer object..."
Call MoveComputer(objComputer.distinguishedName,objComputer.Name)
End If
If strReboot = 1 Then
WScript.Echo " Attempting reboot..."
Call Restart(objComputer.dNSHostName)
End If
count = count + 1
End If
Set objComputer = Nothing
End Function
Function MoveComputer(ComputerDN,ComputerName)
On Error Resume Next
Err.Clear
Set objNewOU = GetObject("LDAP://" & strOutdatedObjectsOURDN & "," & strDomain)
Set objMoveComputer = objNewOU.MoveHere ("LDAP://" & ComputerDN, ComputerName)
WScript.Echo "Computer object moved (" & Err.Number & ")"
End Function
Function ConfigureOU()
On Error Resume Next
Err.Clear
Set objNewOU = GetObject("LDAP://" & strOutdatedObjectsOURDN & "," & strDomain)
If Err.Number <> 0 Then
WScript.Echo "OU for outdated computer objects does not exist; creating it..."
Set objDomain = GetObject("LDAP://" & strDomain)
Set objNewOU = objDomain.Create("organizationalUnit", strOutdatedObjectsOURDN)
objNewOU.SetInfo
Else
WScript.Echo "Outdated computer objects OU exists; continuing..."
End If
End Function
Function CheckDomainLevel(domain)
Set objDomain = GetObject("LDAP://" & domain)
objDomain.GetInfo
If objDomain.Get("msDS-Behavior-Version") >= 2 AND objDomain.Get("nTMixedDomain") = 0 Then
CheckDomainLevel = True
Else
CheckDomainLevel = False
End If
End Function
Function Restart(comp)
'On Error Resume Next
Err.Clear
Set objPing = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecQuery("select * from Win32_PingStatus where address = '" & comp & "'")
For Each objStatus in objPing
If objStatus.StatusCode = 0 Then
'Host was reachable
' Connect to computer
Set OpSysSet = GetObject("winmgmts:{(Shutdown)}//" & comp & "/root/cimv2").ExecQuery("select * from Win32_OperatingSystem where Primary=true")
' Actual shutdown
for each OpSys in OpSysSet
OpSys.Reboot()
next
Set OpSysSet = nothing
If Err <> 0 Then
WScript.Echo " Reboot failed." & Err.Number & " " & Err.Description
Else
WScript.Echo " Reboot initiated..."
End If
Else
WScript.Echo " Host unreachable."
'Host was unreachable
End If
Next
' WScript.Quit
End Function
Function findFSMOOwners()
Set objSchema = GetObject ("LDAP://" & objRootDSE.Get("schemaNamingContext"))
strSchemaMaster = objSchema.Get("fSMORoleOwner")
Set objNtds = GetObject("LDAP://" & strSchemaMaster)
Set objComputer = GetObject(objNtds.Parent)
strFSMOSchemaMaster = objComputer.Name
Set objPartitions = GetObject("LDAP://CN=Partitions," & objRootDSE.Get("configurationNamingContext"))
strDomainNamingMaster = objPartitions.Get("fSMORoleOwner")
Set objNtds = GetObject("LDAP://" & strDomainNamingMaster)
Set objComputer = GetObject(objNtds.Parent)
strFSMODomainNamingMaster = objComputer.Name
Set objDomain = GetObject ("LDAP://" & objRootDSE.Get("defaultNamingContext"))
strPdcEmulator = objDomain.Get("fSMORoleOwner")
Set objNtds = GetObject("LDAP://" & strPdcEmulator)
Set objComputer = GetObject(objNtds.Parent)
strFSMOPDCEmulator = objComputer.Name
Set objRidManager = GetObject("LDAP://CN=RID Manager$,CN=System," & objRootDSE.Get("defaultNamingContext"))
strRidMaster = objRidManager.Get("fSMORoleOwner")
Set objNtds = GetObject("LDAP://" & strRidMaster)
Set objComputer = GetObject(objNtds.Parent)
strFSMORIDMaster = objComputer.Name
Set objInfrastructure = GetObject("LDAP://CN=Infrastructure," & objRootDSE.Get("defaultNamingContext"))
strInfrastructureMaster = objInfrastructure.Get("fSMORoleOwner")
Set objNtds = GetObject("LDAP://" & strInfrastructureMaster)
Set objComputer = GetObject(objNtds.Parent)
strFSMOInfrastructureMaster = objComputer.Name
End Function
Function DetectScriptEngine()
ScriptHost = WScript.FullName
ScriptHost = Right(ScriptHost, Len(ScriptHost) - InStrRev(ScriptHost, "\"))
If (UCase(ScriptHost) = "WSCRIPT.EXE") Then
WScript.Echo ("This script does not work with WScript." & vbNewLine & "Please run it using CSCRIPT.EXE")
WScript.Quit
End If
End Function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment