Home dir from Active Directory compared to server share - VBScript

SnAkEhIpS
SnAkEhIpS used Ask the Experts™
on
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

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Top Expert 2014

Commented:
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

Author

Commented:
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.

Author

Commented:
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

Introduction to R

R is considered the predominant language for data scientist and statisticians. Learn how to use R for your own data science projects.

Most Valuable Expert 2012
Top Expert 2014
Commented:
OK, seeing as my code works for me, do you mind if I stick with that?

I've added some output, so hopefully you will be able to see what's going on with it.  The strUserBase values will pull out the <USER> portion of a home folder path like this:
\\YOURSERVER\Users\<USER>\AnyFolder

and the strFolderBase is each subfolder directly under strHomeDirectoryBase

Regards,

Rob.
If LCase(Right(Wscript.FullName, 11)) = "wscript.exe" Then
    strPath = Wscript.ScriptFullName
    strCommand = "%comspec% /k cscript  """ & strPath & """"
    Set objShell = CreateObject("Wscript.Shell")
    objShell.Run(strCommand), 1, True
    Wscript.Quit
End If

strOU = "OU=Sites,"
strHomeDirectoryBase = "\\YourServer\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
		WScript.Echo "strUserBase from profiles in OU: " & strUserBase
	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
	WScript.Echo "strFolderBase from folders on drive: " & strFolderBase
	If objOUUsers.Exists(strFolderBase) = False Then objOutput.WriteLine strFolderBase
Next
objOutput.Close

MsgBox "Done. Please see " & strOutputFile

Open in new window

Author

Commented:
Maybe I'm missing something... I'm still getting the same result: all folders physically on the server, not just the orphans.

Author

Commented:
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).

Author

Commented:
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

Author

Commented:
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 "".

Author

Commented:
Rob - you answered this in a separate question:

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

It's working now. Thank you.

Author

Commented:
Rob - you answered this in a separate question:

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

It's working now. Thank you.
Most Valuable Expert 2012
Top Expert 2014

Commented:
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.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial