Created
December 19, 2013 14:34
-
-
Save morgansimonsen/8040007 to your computer and use it in GitHub Desktop.
Find outdated computer Objects in Active Directory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
' 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