Link to home
Start Free TrialLog in
Avatar of SnAkEhIpS
SnAkEhIpSFlag for United States of America

asked on

Home dir from Active Directory compared to server share - VBScript

In short, the plan is to compare the user directories on a server to the mapped home directories of all users in a given OU. If it turns out that there are user directories on the server that no user in the OU is mapped to then those are considered orphaned directories. Orphaned directories will be moved to an archive directory for a period of time prior to deletion. The entire process is going to be repeated for 12 servers.

One part of the process involves finding all users in an OU who have their home directory mapped to a particular server . Second, for every user who is mapped to that server the base name of their home directory must be extracted. For example:
If their full home directory path = \\ServerName\Users\JohnDoe\Home
then the base directory name = JohnDoe
I'm assuming that the results would be placed into an array or dictionary.

Another part of the process involves extracting the base name of the user directories on the server in reference and comparing them with the other array. Attached is a script that might do that with some modification. Any guidance?

strComputer = "."
Dim arrFolders()
i = 0
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

strFolderName = "C:\Temp"


Set colSubfolders = objWMIService.ExecQuery _
    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
        & "Where AssocClass = Win32_Subdirectory " _
            & "ResultRole = PartComponent")

For Each objFolder in colSubfolders
	strName = objFolder.name
	arrNames = Split(strName, "\")
	intIndex = Ubound(arrNames)
	Redim Preserve arrFolders (i)
 	arrFolders (i) = arrNames(intIndex)
	'Wscript.Echo arrNames(intIndex)
	i = i + 1
Next

Const FOR_WRITING = 2 
 
Dim objFso 
Dim objOutputFile 
Dim strOutputFile 
 
Dim arrFirst 
Dim arrSecond 
Dim strElementFirst 
Dim strElementSecond 
Dim blnExistsInSecond 
 
strOutputFile = "C:\temp\Differences.txt" 
 
Set objFso = CreateObject("Scripting.FileSystemObject") 
If objFso.FileExists(strOutputFile) Then 
Set objOutputFile = objFso.OpenTextFile(strOutputFile,FOR_WRITING) 
Else 
Set objOutputFile = objFso.CreateTextFile(strOutputFile) 
End If 
 
'arrFirst consists of the user directories in the server's user share
arrFirst = arrFolders
'arrSecond consists of user hmdir mappings found in AD for that specific server (currently psuedo entries) 
arrSecond = Array("F","G","H","a","B","Z","foLdEr12") 
 
For Each strElementFirst In arrFirst 
blnExistsInSecond = False 
For Each strElementSecond In arrSecond 
'If strElementFirst = strElementSecond Then 
If lcase(strElementFirst) = lcase(strElementSecond) Then 
blnExistsInSecond = True 
Exit For 
End If 
Next 
If Not blnExistsInSecond Then 
objOutputFile.WriteLine strElementFirst & " does not exist in the second array" 
End If 
Next

Open in new window

Avatar of RobSampson
RobSampson
Flag of Australia image

Hi there, give this a shot.  I just built this up, and found five orphaned folders myself ;-)
Set the top three parameters and you should be good to go.

Regards,

Rob.
strOU = "OU=Sites,"
strHomeDirectoryBase = "\\YourFileServer\Users\"
strOutputFile = "OrphanedServerFolders.txt"

If Trim(strOU) <> "" Then
	If Right(strOU, 1) <> "," Then strOU = strOU & ","
End If
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain & ">"
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
adoCommand.ActiveConnection = adoConnection

strFilter = "(&(objectCategory=person)(objectClass=user)(homeDirectory=" & Replace(strHomeDirectoryBase, "\", "\5c") & "*))"
strAttributes = "samAccountName,cn,adsPath,homeDirectory"

' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 1000
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") = False

' Run the query.
Set adoRecordset = adoCommand.Execute

Set objOUUsers = CreateObject("Scripting.Dictionary")
' Enumerate the resulting recordset.
Do Until adoRecordset.EOF
	strHome = adoRecordset.Fields("homeDirectory")
	strUserBase = Replace(LCase(strHome), LCase(strHomeDirectoryBase), "")
	If InStr(strUserBase, "\") > 0 Then strUserBase = Left(strUserBase, InStr(strUserBase, "\") - 1)
	If objOUUsers.Exists(strUserBase) = True Then
		MsgBox "OU User " & strUserBase & " already exists"
	Else
		objOUUsers.Add LCase(strUserBase), 0
	End If
	adoRecordset.MoveNext
Loop
adoRecordset.Close

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolderUsers = CreateObject("Scripting.Dictionary")
For Each objSubFolder In objFSO.GetFolder(strHomeDirectoryBase).SubFolders
	strFolderBase = objSubFolder.Name
	If objFolderUsers.Exists(strFolderBase) = True Then
		MsgBox "Folder user " & strFolderBase & " already exists"
	Else
		objFolderUsers.Add LCase(strFolderBase), 0
	End If	
Next

Set objOutput = objFSO.CreateTextFile(strOutputFile, True)
For Each strFolderBase In objFolderUsers
	If objOUUsers.Exists(strFolderBase) = False Then objOutput.WriteLine strFolderBase
Next
objOutput.Close

MsgBox "Done. Please see " & strOutputFile

Open in new window

Avatar of SnAkEhIpS

ASKER

Thanks for the rapid reply! My results are the exact user directories on the server, not just the orphans. So if there are 100 user directories on the server and only 5 are actual orphans, my results are all 100 user directories.
Cut me off when I start to get annoying ;)

I see a 3-part problem as follows:

1. identify users in AD mapped to YourServerName
2. identify user folders on the server itself (YourServerName)
3. compare the two and render the difference (orphaned folders or #2 - #1)

The first part of the script I previously attached is attached again. This is the portion that sirbounty nailed (#2). The portion I thought that you had nailed this morning was #1, or so it appeared to me until I executed it. I wound up getting the same result as sirbounty's script; seemed implausible considering that you were both connecting to 2 different service providers and ADO shouldn't have any clue about "excess directories" on a given server.

This is how I'm checking the results: When I execute a query in ADUC (from the GUI) for all users with a home folder that starts with "\\YourServerName\Users" I get 269 returns. When I run either your's or sirbounty's script I get 399 returns. When I path to "\\YourServerName\Users" of the actual server itself, Windows Explorer reports 399 user directories. A cursory check of a few known users both past and present confirms that some users are now mapped to other servers or have been deleted from AD altogether.

Ultimately, 399-269 or 130 is the number of orphaned user directories on "\\YourServerName". That's who I need to identify and as non-politically correct as this may sound... I must delete the orphans :O !!!

strComputer = "."
Dim arrFolders()
i = 0
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

strFolderName = "\\YourServerName\Users"


Set colSubfolders = objWMIService.ExecQuery _
    ("Associators of {Win32_Directory.Name='" & strFolderName & "'} " _
        & "Where AssocClass = Win32_Subdirectory " _
            & "ResultRole = PartComponent")

For Each objFolder in colSubfolders
	strName = objFolder.name
	arrNames = Split(strName, "\")
	intIndex = Ubound(arrNames)
	Redim Preserve arrFolders (i)
 	arrFolders (i) = arrNames(intIndex)
	'Wscript.Echo arrNames(intIndex)
	i = i + 1
Next

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of RobSampson
RobSampson
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Maybe I'm missing something... I'm still getting the same result: all folders physically on the server, not just the orphans.
Am I understanding this correctly?

The script connects to ADO and creates a dictionary object (objOUUsers) consisting of objects that meet the specified criteria. It then creates another dictionary object (objFolderUsers) consisting of objects that meet the specified criteria. Last it ouputs the discrepancies, specifically where there is NOT a strFolderBase (subfolder of strHomeDirectoryBase) found in objOUUsers.

      If objOUUsers.Exists(strFolderBase) = False Then objOutput.WriteLine strFolderBase

Would it be accurate to say that if I changed the output statement to "TRUE" then it should output all subfolders on the server that have a matching objOUUser? In practice though, whether I make the statement "TRUE" or leave it as "FALSE" I still get the same exact result, which is an ouput file containing all of objFolderUsers (all subfolders on the server).
When I count the elements of both dictionaries I get 0 elements for objOUUsers.

strFolderBase count on drive: 399
strUserBase count in AD: 0
Next
objOutput.Close

Wscript.Echo "strFolderBase count on drive: " & objFolderUsers.count
wscript.Echo "strUserBase count in AD: " & objOUUsers.count

MsgBox "Done. Please see " & strOutputFile

Open in new window

Clarification: Onscreen it echoes all user folders in either case (TRUE or FALSE). In the actual output file there are 0 entries when set to TRUE. This is consistent with the objOUUsers count being 0.  Seems like I'm not connecting to AD. I've tried the distinguished name of the OU as the value for strOU. I've also tried "".
Rob - you answered this in a separate question:

Change strBase = "<LDAP://" & strDNSDomain & ">"
to strBase = "<LDAP://" & strOU & strDNSDomain & ">"

It's working now. Thank you.
Rob - you answered this in a separate question:

Change strBase = "<LDAP://" & strDNSDomain & ">"
to strBase = "<LDAP://" & strOU & strDNSDomain & ">"

It's working now. Thank you.
Hi, that's good news.  Thanks for the grade.

It's a bit odd that that would prevent it from working correctly though....because without strOU it should search the whole domain anyway.

Regards,

Rob.