«

»

Nov 09 2013

HOWTO: Ultimate Powershell Home Drive Verification Script

The script below was designed to solve the following problem in my environment.  We have 32 discrete home servers scattered across Canada and thousands of employees that use them.  The problem was appeared to have many times more folders than we did employees.  This turned out to be the result of having an incomplete take down process for terminated employees that ran for an extended period of time.  I therefore needed to write a script to identify all of the folders that were no longer used and could be archived or deleted.

This proved to be far more complex than I first suspected for at least the following reasons:

– Manually providing a list of home servers had the potential to miss entire file servers in a larger environment
– The folder names of the home drives did not always (often) match the username either due to spelling mistakes or varying conventions (firstname.lastname, firstnamelastinitial, lastnameonly, etc)
– The ACLs on the folders may not have been configured correctly and so the user couldn’t access it anyway
– The home drive was not configured in active directory for the user so they couldn’t use the folder even if it existed
– The folder ACL only contained SIDs as the employee account was deleted
– The ACL on the folder only contained the username from an obsolete but still around domain
– The user was terminated but their account was never disabled
– Someone else was given access to a folder and so even though the employee is terminated, we don’t want to remove it
– The user account had been deleted from Active Directory entirely
– The user account was created but the employee never started and so the account was never logged into
– The folder is only accessible to administrators
– The user moved to a new site and their OU was updated but their home drive was not so they are now accessing data over the WAN
– The account is in fact disabled or the account expiry is set
– and still more

I wanted to write a script that could progmatically and automatically identify all of these issues and create an easy to read report that tells me what’s going on in my environment with respect to home drives.

After many, many hours of research and head banging, I’m very proud to announce that I have written a script to accomplish everything above.  Three disclaimers though before we go any further:

1) This was written for my environment.  It does not make any changes to anything and so it should be safe to run anywhere.  However it was only tested in my environment and so there are absolutely no guarantees for what it will do for you
2) The comments could stand to be further refined and expanded upon but I quite frankly can’t stand to look at this thing anymore.  I want it out of my head so I’m publishing it as is.
3) A good chunk of the code is optimized.  Creating this required a very large amount of trial and error.  I fully intend to one day go back and see how best I can optimize this code but for now I’m choosing to call it functional rather than ideal.

Known issues:

– I have error handling for all known situations but I do not have any generic try catch error handling as I wanted the errors for debugging.  I wanted to add this… but I’m sick of it.  Maybe another time
– The script makes several assumptions about the layout of your network that must be true for this script to function.  Those include but are not limited to:

* Your home drives must follow the convention \\<servername>\<home> where “servername” and “home” can be any values but it can not go any deeper than this
* Your OUs are in the format <mainOU>, <subOU>, <Useraccount>.  Any other combination will not allow that functionally to work
* I’m sure there are others…

 

– It’s also worth noting that this script can take a while to run depending on the size of the environment.  To that end, I’ve included some fairly robust progress bars and countdown timers to keep track as it goes as shown below:

 

2013-11-09 12_56_42-progressbar

Ok, that’s it.  I’m done.  Maybe I’ll revisit this in 2014…

### **** THIS SCRIPT COMES WITHOUT ANY WARRANTY OF ANY KIND EITHER EXPRESSED OR IMPLIED. **** ###
### **** WHILE THIS SCRIPT DOES NOT ATTEMPT ANY SET OR CHANGE OPERATIONS, IT SHOULD STILL BE HANDLED WITH CARE **** ###
### **** THIS SCRIPT HAS ONLY EVER BEEN TESTED ON THE DOMAIN IT WAS WRITTEN FOR.  YOUR MILAGE MAY VARY.

<#
DETAILED HOME DRIVE REPORTING TOOL 
NOTE: THIS SCRIPT DOES NOT REQUIRE YOU TO PROVIDE ANY PARAMATERS -- IT WILL FIGURE OUT EVERYTHING IT NEEDS JUST BY RUNNING IT

THIS SCRIPT DOES THE FOLLOWING:
    - SCANS ACTIVE DIRECTORY AND GENERATES A LIST OF *ALL* USERS ANY SEVERAL KEY ATTRIBUTES AND STORES THEM IN AN OBJECT
	- SCANS THIS OBJECT FOR A LIST OF ALL HOME DIRECTORIES USED IN THE DOMAIN
	- EXTRACTS A LIST OF UNIQUE HOME SERVERS
	- SCANS ALL OF THOSE HOME SERVERS AND GENERATES A LIST OF ALL HOME FOLDERS THAT EXIST ON THE FILE SYSTEM
	- PERFORMS A TON OF DIFFERENT CHECKS TO ATTEMPT TO VALIDATE IF EACH HOME DRIVE IS IN FACT STILL IN USE OR NOT
	- THESE CHECKS INCLUDE BUT ARE NOT LIMITED TO:
		- DOES THE NAME OF THE FOLDER MATCH UP EXACTLY WITH THE NAME OF AN AD USER ACCOUNT OF THE SAME NAME?
		- DOES THE USER MATCHED HAVE A HOME DRIVE CONFIGURED IN ACTIVE DIRECTORY TO POINT TO THIS PATH?
		- DOES THE SECURITY ACL ON THE FOLDER MATCH WITH USERNAME ON THE FOLDER?
		- IS THE USER'S HOME DRIVE ON THE APPROPRIATE FILE SERVER FOR THE ORGANIZATIONAL UNIT THEY ARE IN?
		- IS THE ACCOUNT ENABLED?
		- HAS THE ACCOUNT BEEN LOGGED INTO IN THE LAST 60 DAYS?
		- IS THE ACCOUNT SET TO EXPIRE?
		- ... AND MORE

	- WHEN IT'S DONE, IT'LL SAVE THE RESULTS TO THE CLIPBOARD AUTOMATICALLY AS A CSV SO YOU CAN PASTE IT DIRECTLY INTO EXCEL
#>

# CONTROLS IF THE SYSTEM WILL GO RESCAN ALL OF ACTIVE DIRECTORY AND ALL FILE SERVERS AGAIN
# AS THE BASE ACTIVE DIRECTORY DATA WILL NOT CHANGE FROM EXECUTION TO EXECUTION, RUNNING THIS ONCE AND THEN SETTING TO FALSE MEANS THE SCRIPT RUNS ARE FASTER
$ReloadAllData = $true

# IF VALUES BELOW ARE NOT SET TO $NULL, THEY WILL OVERRIDE ANY DETECTED FOLDERS AND ONLY SCAN THIS ONE -- USED FOR TESTING SPECIFIC SERVERS AND FOLDERS
$ManualHomeFolderToScan = $null
$ManualHomeFolderToScan = $null # Must be a UNC path in the format of \\servername\homefolder

# IF NOT NULL WILL ONLY SCAN THESE FOLDERS.  MUST BE USED WITH ONE OF THE MANUAL VARIABLES ABOVE
#$ManualFoldersToCheck = EG: 1 USER: "joe.smith" EG: MANY USERS: "joe.smith" -or $_.Name -eq "jill.simpson"} 
$ManualFoldersToCheck = $null

# FOR TESTING PURPOSES -- ALLOWS YOU TO LIMIT THE NUMBER OF RETURNED RECORDS TO SAVE TIME FOR TESTING
# IF SET TO 0, WILL RETURN ALL RECORDS
$LimitReturnedRecords = 0

#region [001] === *DEFINE VARIABLES*
	
	# STOPWATCH OBJECT IS USED TO CALCULATE TIME REMAINING FOR LONG RUNNING PROCESSES
	$StopWatch = New-Object system.Diagnostics.Stopwatch 
	
	# MESSAGE TO DISPLAY INCSV IF A RECORD IS NOT FOUND.  ERR_ PREFIX IS TO PREVENT SCANNING OF RECORDS WE KNOW DON'T EXIST
	$NotFoundMessage = "*ERR_NOT_FOUND*"

	$objFinalResults = @()
	$objFinalResultsTemplate = New-Object psobject
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name FileSystemHomeDirectory -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name FinalStatus -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name distinguishedName -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name HomeServer -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name FS_ADHomeFolderMatches -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name OU_ServerMatches -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name ADMappedHomeDirectory -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name SAMUserAccount -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name DisplayName -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name AccountEnabled -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name User_ACLMatches -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name FolderACL -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name EffectiveFolderACL -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name AccountExpires -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name LastLogon -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name WhenUserCreated -Value $null
	$objFinalResultsTemplate | Add-Member -MemberType NoteProperty -Name DateFolderCreated -Value $null
	
#endregion [/001] ===


# THIS CODE BLOCK USES THE DIRECTORYSERVICES.DIRECTORYSEARCHER METHOD TO GET ALL USERS AND DESIRED ATTRIBUTES FROM ACTIVE DIRECTORY AND STORE THEM IN $ADUSERDATA
#region [002] === *GATHER ALL DATA NEEDED FROM ACTIVE DIRECTORY AND THE FILE SYSTEM*
if($ReloadAllData -eq $true)
{
	#region [002-001] === * GATHER ALL DATA FROM ACTIVE DIRECTORY AND STORE IN SINGLE OBJECT FOR LATER LOOKUP
	$CollectingADData = $true # Reset it to true for each new execution
	while ($CollectingADData -eq $true)
	{
		Write-Progress -Activity "Scanning Active Directory. This may take a while. Please wait..." -Status "Step 1/4" -Id 1
		$adobjroot = [adsi]''
		$objstalesearcher = New-Object System.DirectoryServices.DirectorySearcher($adobjroot)
		$objstalesearcher.PageSize=4000
		$objstalesearcher.filter = "(&(objectCategory=person)(objectClass=user))"

		$ADUserData = $objstalesearcher.findall() | select `
		@{e={[string]$_.properties.cn};n='DisplayName'},`
		@{e={[string]$_.properties.samaccountname};n='Username'},`
		@{e={[string]$_.properties.homedirectory};n='HomeDirectory'},`
		@{e={[string]$adspath=$_.properties.adspath;$account=[ADSI]$adspath;$account.psbase.invokeget('distinguishedName')};n='distinguishedName'}, `
		@{e={[string]$_.properties.whencreated};n='WhenUserCreated'},`
		@{e={[datetime]::fromfiletime($_.properties.accountexpires[0])};n='AccountExpires'},`
		@{e={[datetime]::FromFileTimeUtc([int64]$_.properties.lastlogontimestamp[0])};n='LastLogon'},`
		@{e={[string]$adspath=$_.properties.adspath;$account=[ADSI]$adspath;$account.psbase.invokeget('AccountDisabled')};n='AccountDisabled'}
		 
	  	Write-Progress -Activity "Scanning Active Directory. This may take a while. Please wait..." -Status "Complete" -Id 1
		$CollectingADData = $false
	}
	#endregion [/002-001] ===

	# THIS SECION WILL GENERATE A UNIQUE LIST OF ALL HOME SERVERS THAT ARE REFERENCED TO ANY USER IN ACTIVE DIRECTORY
	#region [002-002] === *GENERATE LIST OF HOME SERVERS*
		$HomeServersList = @()
		$i = $null
		$FilteredForHomeDirectories = $ADUserData.HomeDirectory | ? {$_ -ne ""}
		$TotalUsers = $FilteredForHomeDirectories.Length

		ForEach($ADHomeFolder in $ADUserData.HomeDirectory)
		{ 
			if ($ADHomeFolder -ne "")
			{
				$i++
				$ProgressHomeServer = $ADHomeFolder.Split("\")[2]
				Write-Progress -Activity "Identifying Home Servers: [$ProgressHomeServer] ($i of $TotalUsers)" -PercentComplete (($i / $TotalUsers) * 100) -Status "Step 2/4" -Id 2
				$HomeServersList += $ADHomeFolder.ToLower() | Split-Path # eg: \\FSRVCIT1\CITHOME$ #
			}
		}
		$HomeServersList = $HomeServersList | Sort-Object | Get-Unique
	#endregion [/002-002]  ===

	#region [002-003] === *GENERATE LIST OF HOME FOLDERS FROM THE LIST OF HOME SERVERS*
	
	# NOW THAT WE HAVE A LIST OF ALL KNOWN HOME FILE SERVERS, WE NEED TO FIGURE OUT WHICH ONES STILL EXIST AND ARE ONLINE #
	# ONCE WE KNOW THIS WE CAN QUERY TO GRAB ALL HOME FOLDERS THAT EXIST ON THESE SERVERS #
	$i = $null
	$TotalServers = $HomeServersList.length

	$FSHomeFolders = $null

	ForEach($HomeServer in $HomeServersList)
	{
		$i++
		Write-Progress -Activity "Identifying Home Folders: [$HomeServer] ($i of $TotalServers)" -PercentComplete (($i / $TotalServers) * 100) -Status "Step 3/4" -Id 3
		if(Test-Connection $HomeServer.Split("\")[2] -Quiet -Count 1)
		{
			if (Test-Path $HomeServer) 
			{ $FSHomeFolders += get-childitem $HomeServer | select fullname, CreationTime }
		}
	}
	$TotalFolderstoScan = $FSHomeFolders.Length
	#endregion [/002-003]

} # END RELOAD ALL DATA
#endregion [/002] ===

#region [003] === *HOME FOLDER PROCESSING*

	#region [003-001] === * PREPERATIONS FOR THE SCAN*
		# THE VARIABLES BELOW ARE SET AT THE TOP AND ARE USED TO OVERRIDE SEARCHES FOR TESTING
		if ($ManualHomeFoldertoScan -ne $null) {$FSHomeFolders = Get-ChildItem $ManualHomeFolderToScan ; $TotalFolderstoScan = $FSHomeFolders.Length } 
		if ($ManualFoldersToCheck -ne $null) { $FSHomeFolders = Get-ChildItem $ManualHomeFolderToScan | Where-Object { $_.Name -eq $ManualFolderstoCheck } ; $TotalFolderstoScan = $FSHomeFolders.length }
		if($LimitReturnedRecords -gt 0) { $TotalFolderstoScan = $LimitReturnedRecords }
		
		# RESET $I TO NULL ON EACH EXECUTION 
		$i = $null
		
		# DEFAULT TIME IN SECONDS IT'S ASSUMED TO TAKE TO A SCAN A FOLDER.  MUST BE GREATER THAN ZERO AS WE DIVIDE BY IT
		$TimeToGo = 1  
		
		# START RECORDING THE TIME IT TAKES TO RUN THE FOLLOWING OPERATIONS.  USED TO CALCULATE TIME REMAINING
		$stopWatch.Start()

		# CYCLE THROUGH EVERY HOME DRIVE FOUND ON THE FILE SYSTEM OF EVERY FILE SERVER
		ForEach($FSHomeFolder in $FSHomeFolders)
		{
			#ONLY RUN THE STOPWATCH EVERY 10 EXECUTIONS SO YOU GET A KIND OF AVERAGE THAT MAKES THE TIME REMAINING MORE ACCURATE
			if ($i % 10 -eq 0) { $stopWatch.Start() }
			
			#INCREMENT THIS VARIABLE FOR USE WITH THE PROGRESS BAR
			$i++
			
			$TempHomePath = $FSHomeFolder.FullName
			Write-Progress -Activity "Home Folder: [$TempHomePath] ($i of $TotalFolderstoScan Complete)" -Status "Step 4/4" -PercentComplete (($i / $TotalFolderstoScan) * 100) -SecondsRemaining $TimeToGo -Id 4 
			
			#CREATE A NEW OBJECT WITH THE PROPERTIES WE DECLARED ABOVE
			$objFinalResultsTemp = $objFinalResultsTemplate | Select-Object *

			# RESET ALL VARIABLES FOR EACH EXECUTION
			$TempAccountExpires = $null
			$TempADHomeFolder = $null
			$TempADUserData = $null
			$TempADUsers = $null
			$TempDisplayName = $null
			$TempEnabled = $null
			$TempFolderACLFinal = $null
			$TempFolderACLString = $null
			$TempFolderDisplayName = $null
			$TempFolderUsername = $null
			$TempHomeServer = $null	
			$TempSamAccountName = $null
			$TempWhenUserCreated = $null
			$TempFolderACLFinalNoSID = $null
			$TempFolderACLDPHCheck = $null
			$TempEffectiveUserName = $null
			$TempHomeDirTest = $null
			$TempSamAccountNameTest = $null
			
			# SET THE HOME FOLDER NAME TO OUR EXPORT VARIABLE #
			$objFinalResultsTemp.FileSystemHomeDirectory = $FSHomeFolder.FullName
			
			# GET THE HOME SERVER FOR EASY PROCESSING #
			if ($FSHomeFolder.Equals -ne $null)  
			{
				$TempHomeServer = $FSHomeFolder.FullName.split("\")[2]
				$objFinalResultsTemp.HomeServer = $TempHomeServer.ToString().ToUpper()
			}
			else {  $objFinalResultsTemp.HomeServer = $NotFoundMessage }
			
			# EXTRACT THE "USER NAME" FROM THE FOLDER PATH #
			$TempFolderUserName = $FSHomeFolder.FullName.split("\")[4] 
			$TempFolderDisplayName = $TempFolderUserName.Replace(".", " ")
		#endregion [003-001] ===

	#region [003-002] ===*ACL FOLDER PROCESSING*

		if(Test-Path $FSHomeFolder.FullName)
		{ 
			$TempFolderACL = Get-Acl $FSHomeFolder.FullName | % {$_.Access} | Select-Object IdentityReference | ? `
			{$_ -notmatch "Administrators" -and `
			$_ -notmatch "dl-ntfs-home-fc" -and `
			$_ -notlike '*SYSTEM*' -and `
			$_ -notlike '*CREATOR OWNER*' }
			
			# MULTIPLE OBJECTS ARE SOMETIMES RETURNED FOR THE SAME USER -- FILTER THESE OUT
			$TempFolderACL = $TempFolderACL.identityreference | select -Unique
			
			# REMOVE THE DOMAIN PREFIX TO MAKE THE NAMES EASIER TO WORK WITH
			$TempFolderACL = $TempFolderACL | ForEach-Object { $_ -replace 'FOCUSED\\', '' }
			
			# WE NEED TO TRANSPOSE THE LIST OF ARRAY ITEMS INTO A SINGLE LINE. WE DO THAT WITH THE JOIN COMMAND
			if($TempFolderACL -ne $null) { 
				$TempFolderACLDPHCheck = $TempFolderACL | ? {$_ -notlike("DPH\*") }
				if($TempFolderACLDPHCheck -eq $null) { $TempFolderACLDPHCheck = "ONLY_DPH" }
				
				$TempFolderACLFinalNoSID = $TempFolderACL | ? {$_ -notlike("S-1*") }
				$TempFolderACLFinal = ([string]::Join(",",($TempFolderACL))) 
			}
			
			if( $TempFolderACLFinal -eq $TempSamAccountName) { $objFinalResultsTemp.User_ACLMatches = "YES" } Else { $objFinalResultsTemp.User_ACLMatches = "NO" }
			$objFinalResultsTemp.FolderACL = $TempFolderACLFinal
		}
	#endregion [/003-002] ===

	#region [003-003] ==== *ACTIVE DIRECTORY DATA PROCESSING*
		
		# GET THE SAMACCOUNTNAME FROM AD BASED ON THE FOLDER NAME #
		# IF THAT FAILS (LIKELY BECAUSE THE FOLDER NAME ALREADY IS THE SAM ACCOUNT NAME, TRY PASSING IT DIRECTLY)
		$UserCount = $TempSamAccountName = $ADUserData | ? {$_.DisplayName -eq $TempFolderDisplayName}
		if($UserCount.length -gt 1) { $TempSamAccountName = "*ERR_MULTIPLE_USERS*" }
		Else
		{
			if ($TempFolderDisplayName.Contains(" ")) { $TempSamAccountName = $ADUserData | ? {$_.DisplayName -eq $TempFolderDisplayName} | select UserName }
		
			if ($TempSamAccountName -eq $null) { $TempSamAccountName = $ADUserData | ? {$_.Username -eq $TempFolderUserName} | select UserName }
			if ($TempSamaccountName -ne $null) { $TempSamAccountName = $TempSamAccountName.UserName } #else { $TempSamAccountName = $NotFoundMessage }
		}
			
		$TempEffectiveUserName = $TempFolderACLFinalNoSID | ? {$_ -notlike "DPH\*"}
		
		if($TempEffectiveUserName -eq $null) { $TempEffectiveUserName = "" }

		# FIGURE OUT IF THE ACL ON THE USER IS VALID IF THE USERNAME WASN'T
		if($TempSamAccountName -eq $null -and $TempFolderACLFinalNoSid -ne $null -and -not $TempEffectiveUserName.Contains(",") )
		{ 
		
			$TempSamAccountNameTest = $ADUserData | ? {$_.UserName -eq $TempEffectiveUserName} | select UserName	
			if ($TempEffectiveUserName -eq "") { $objFinalResultsTemp.FinalStatus = "INVALID_ACL" }
			
			if($TempSamAccountNameTest -ne $null) 
			{
				$TempHomeDirTest = $ADUserData | ? {$_.UserName -eq $TempSamAccountNameTest.Username} | select homedirectory
				# THIS FINALSTATUS CAN'T BE AT THE BOTTOM BECAUSE WE ONLY KNOW THE SPELLING IS BAD IF WE ARE IN THIS LOOP
				if($TempHomeDirTest.HomeDirectory -eq $FSHomeFolder.FullName) 
					{ $TempSamAccountName = $TempEffectiveUserName ; $objFinalResultsTemp.FinalStatus = "BAD_SPELLING" }
			}
		}
		
		if($TempEffectiveUserName -ne $null) { $objFinalResultsTemp.EffectiveFolderACL = ([string]::Join(",",($TempEffectiveUserName )))  }
		if ($TempsamaccountName -ne "" -and $TempSamAccountName -ne $null) { $objFinalResultsTemp.SAMUserAccount = $TempSamAccountName } Else { $objFinalResultsTemp.SAMUserAccount = $NotFoundMessage }

		if ($TempSamAccountName -ne $null -or $TempSamAccountName -ne $NotFoundMessage -or $TempSamAccountName -notcontains "*ERR_")
		{
			# EXTRACT THE DATA FOR THE CURRENT USER AND SAVE IT INTO A VARIABLE TO PREVENT CONSTANTLY RESCANNING THE MASTER OBJECT
			$TempADUserData = $ADUserData | ? {$_.Username -eq $TempSamAccountName } 

			# EXTRACT "DISPLAY NAME"
			$TempDisplayName = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select DisplayName
			if ($TempDisplayName -ne $null) { $objFinalResultsTemp.DisplayName = $TempDisplayName.DisplayName } Else { $objFinalResultsTemp.DisplayName = $NotFoundMessage }
			
			# EXTRACT "WHEN CREATED"
			$TempWhenUserCreated = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select WhenUserCreated
			if ($TempWhenUserCreated -ne $null) { $objFinalResultsTemp.WhenUserCreated = $TempWhenUserCreated.whenusercreated } Else { $objFinalResultsTemp.WhenUserCreated = $NotFoundMessage }	
		
			# EXTRACT "ACCOUNT STATE"
			$TempAccountState = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select AccountDisabled
			# AD RETURNS IF THE USER IS DISABLED OR NOT.  I PERFER TO RETURN IF THE USER IS ENABLED OR NOT SO WE SWAP WHATEVER I GET BACK HERE
			switch($TempAccountState.AccountDisabled) {	$true { $TempAccountState = $false }	$false { $TempAccountState = $true } }
			if ($TempAccountState -ne $null) { $objFinalResultsTemp.AccountEnabled = $TempAccountState } Else { $objFinalResultsTemp.AccountEnabled = $NotFoundMessage }	
			
			# EXTRACT AD HOME DIRECTORY 
			$TempADMappedHomeDirectory = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select HomeDirectory
			if ($TempADMappedHomeDirectory -ne $null) { $TempADMappedHomeDirectory = $TempADMappedHomeDirectory.homedirectory } Else { $TempADMappedHomeDirectory = $NotFoundMessage }	
			$objFinalResultsTemp.ADMappedHomeDirectory = $TempADMappedHomeDirectory
			if ($TempADMappedHomeDirectory -eq $FSHomeFolder.FullName) { $objFinalResultsTemp.FS_ADHomeFolderMatches = "YES" } Else { $objFinalResultsTemp.FS_ADHomeFolderMatches = "NO" }
			
			# EXTRACT "DISTINGUSHED NAME"
			$TempDN = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select DistinguishedName
			$TempDN = $TempDN -replace "OU=", "" -split(",") | select -Index 2
			if ($TempDN -ne $null) { $objFinalResultsTemp.DistinguishedName = $TempDN} Else { $objFinalResultsTemp.DistinguishedName = $NotFoundMessage }
			
			# EXTRACT "LAST LOGON TIME"
			$TempLastLogon = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select LastLogon
			if ($TempLastLogon -ne $null) { $TempLastLogon = $TempLastLogon.lastlogon} Else { $TempLastLogon = $NotFoundMessage }	
			if ($TempLastLogon -eq $null) { $TempLastLogon = "NEVER_LOGGED_IN" }
			$objFinalResultsTemp.LastLogon = $TempLastLogon
		
			# EXTRACT "ACCOUNT EXPIRATION TIME"
			$TempAccountExpires = $TempADUserData | ? {$_.Username -eq $TempSamAccountName} | select AccountExpires
			if ($TempAccountExpires -ne $null -and $TempAccountExpires.AccountExpires -gt (Get-Date "12/31/1600 5:00:00 PM")) { $TempAccountExpires = $TempAccountExpires.accountexpires} Else { $TempAccountExpires = "N/A" }	
			$objFinalResultsTemp.accountexpires = $TempAccountExpires

			# GET DATE HOME FOLDER CREATED"
			if ($FSHomeFolder.CreationTime -ne $null) { $objFinalResultsTemp.DateFolderCreated = $FSHomeFolder.CreationTime }
		}
		Else
		{ 
			#IF WE ARE HERE IT MEANS WE COULDN'T FIND THIS USER IN ACTIVE DIRECTORY SO SET ALL THE OTHER VARIABLES TO NOT FOUND
			$objFinalResultsTemp.AccountEnabled = $NotFoundMessage 
			$objFinalResultsTemp.DisplayName = $NotFoundMessage
			$objFinalResultsTemp.ADMappedHomeDirectory = $NotFoundMessage
			$objFinalResultsTemp.AccountExpires = $NotFoundMessage
			$objFinalResultsTemp.LastLogon = $NotFoundMessage
			$objFinalResultsTemp.WhenUserCreated = $NotFoundMessage
			$objFinalResultsTemp.FS_ADHomeFolderMatches = "NO"
		}
		
	#endregion [/003-003] ===

	# THIS SECTION USES A HASH TABLE TO CAPTURE THE THREE LETTER SITE CODES FROM THE DESCRPITION FIELD OF EACH OU
	# IT THEN USES THE HASH TABLE TO CONFIRM THAT THE OU THE USER IS IN MATCHES WHAT HOME SERVER THEY SHOULD BE USING
	#region [003-004] === *CALCULATE SITE CODES FROM OUS*
		$ListofOUs = @{}
		$Results = $null
		$TempOUs = Get-ADOrganizationalUnit -SearchScope OneLevel -Filter * -Properties Description | select DistinguishedName, Description | ? { $_.Description -like '???' }
		$TempOUs | % { $_.DistinguishedName = $_.DistinguishedName.tolower() -replace "OU=", "" -split(",") | select -Index 0 }

		ForEach($OU in $TempOUs) { $ListofOUs.Add($OU.DistinguishedName, $OU.description) }

		$ListofOUs.Set_Item("calgarypipeline", "PIP")
		$ListofOUs.Set_Item("calgarysoutheast", "CSE")
		$ListofOUs.Set_Item("calgarytranstech", "CIT")
		$ListofOUs.Set_Item("calgary", "CAL")
		$ListofOUs.Set_Item("grande_prairie", "DGA")
		$ListofOUs.Set_Item("victoria", "DVA")
		$ListofOUs.Set_Item("dph", "DPH")
		$ListofOUs.add("Victoria2","LAN") # TWO DIFFERENT OUS USE THE SAME HOME SERVER.  BECAUSE THE KEYWORD VICTORIA WILL APPEAR IN BOTH AND WE PERFORM A LIKE COMPARISON, THEY WILL MATCH

		if($TempDN -ne $null)
		{	$Results = $ListofOUs.Keys | ?{$_ -match "$TempDN*"} | %{$ListofOUs.$_}

			ForEach($OULocation in $Results)
			{
				if ($TempHomeServer.ToUpper() -like "*$OULocation*")
				{ $TempOUMatch = "YES" ; break }
				Else {  $TempOUMatch = "NO" }
			}
			$objFinalResultsTemp.OU_ServerMatches = $TempOUMatch
		}
	#endregion [/003-004] ===
		
	if($TempDN -eq $null) { $objFinalResultsTemp.OU_ServerMatches = $NotFoundMessage }

	if($LimitReturnedRecords -gt 0) { if ($i -eq $LimitReturnedRecords) { break } }

	# RUN A SERIES OF CHECKS TO VERIFY THE STATUS OF EACH HOME DRIVE AND WRITE THOSE RESULTS TO THE "FINALSTATUS" COLUMN
	# NOTE A HOME FOLDER CAN HAVE MORE THAN ONE VALUE.  HOWEVER BECAUSE ONLY ONE WILL BE SET, THE LAST ONE WINS.
	# AS A RESULT, THE MOST CRITICAL STATES ARE FOUND AT THE END
	$FailState = $false # ASSUME THINGS ARE WORKING UNTIL SHOWN OTHERWISE
	
	# CHECK IF USER HAS LOGGED IN IN THE LAST 60 DAYS
	if ($objFinalResultsTemp.LastLogon -ne $null -and $objFinalResultsTemp.LastLogon -ne $NotFoundMessage)
		{ if ($objFinalResultsTemp.LastLogon -lt (Get-Date).adddays(-60)) { $objFinalResultsTemp.FinalStatus = "LAST_LOGON_60_DAYS" ; $Failtate = $true} }
	
	# CHECK IF THE HOME DRIVE ON THE FILE SYSTEM MATCHES THE HOME DRIVE CONFIGURED IN ACTIVE DIRECTORY
	if ($objFinalResultsTemp.FS_ADHomeFolderMatches -eq "NO") { $objFinalResultsTemp.FinalStatus = "FS_AD_HOMEFOLDER_NO_MATCH" ; $FailState = $true }
	
	# CHECK IF THE USERNAME ON THE FOLDER ACL MATCHES THE NAME ON THE FOLDER.  IF NOT, THERE IS SOME KIND OF SPELLING MISTAKE ON THE FOLDER NAME
	# EITHER THAT OR IT IS USING A DIFFERENT CONVENTION (FOR EXAMPLE, ONLY LAST NAME)
	if ($TempEffectiveUserName -ne $TempFolderUsername) { $objFinalResultsTemp.FinalStatus = "WRONG_NAME" ; $FailState = $true }

	# CHECK IF THE USER'S HOME DRIVE IS ON THE APPRORIRATE SERVER FOR THE OU THEY ARE IN (AKA ARE THEY COPYING DATA OVER THE WAN OR NOT)
	if ($objFinalResultsTemp.OU_ServerMatches -eq "NO") { $objFinalResultsTemp.FinalStatus = "OU_SERVER_NO_MATCH" ; $FailState = $true }

	# IF $TEMPFOLDERACL DOESN'T CONTAIN ANYTHING, IT MEANS THE ONLY THING IN THERE WAS EITHER NOTHING OR THE OBJECTS LISTED ABOVE
	if($TempFolderACL -eq $null -or $TempFolderACL -eq "")  { $objFinalResultsTemp.FolderACL = "ONLY_EXPECTED_SECURITY_OBJECTS" ; $objFinalResultsTemp.FinalStatus = "ONLY_EXPECTED_SECURITY_OBJECTS" ; $FailState = $true}
	
	# WE EXCLUDED FROM THE FILTER "SYSTEM", AND "ADMINISTRATORS"  IF NOTHING ELSE IS RETURNED IT MEANS THAT THESE ARE THE ONLY PERMISSIONS SET AND NO USERS CAN ACCESS THE FOLDER
	if($TempEffectiveUserName -eq "") { $objFinalResultsTemp.EffectiveFolderACL = "*BLANK*" ; $objFinalResultsTemp.FinalStatus = "ONLY_EXPECTED_SECURITY_OBJECTS" ; $FailState = $true}
	
	# CHECK IF THE USER EVEN HAS A HOME DRIVE PROPERTY CONFIGURED IN ACTIVE DIRECTORY
	if ($TempADMappedHomeDirectory -eq "") { $TempADMappedHomeDirectory = "*ERR_NO_AD_HOMEFOLDER*" ; $objFinalResultsTemp.ADMappedHomeDirectory = "NO_AD_HOME_FOLDER" ; $objFinalResultsTemp.FinalStatus = "NO_AD_HOME_FOLDER" ; $FailState = $true}
	
	# CHECK IF THE USER IS ONLY PART OF THE NOW OBSOLETE DPH DOMAIN
	if ($TempFolderACLDPHCheck -eq "ONLY_DPH") { $objFinalResultsTemp.FinalStatus = "ONLY_DPH_ACL" ; $FailState = $true}
	
	# CHECK IF THE ACLS ON THE FOLDER ONLY CONTAIN A SINGLE SID (THE LACK OF A COMMA MEANS THERE IS NO OTHER ENTRY)
	if ($TempFolderACLFinal -like("*S-1*") -and $TempFolderACLFinal -notlike ("*,*")) { $objFinalResultsTemp.FinalStatus = "ONLY_SID_IN_ACL" ; $FailState = $true }	

	# CHECK IF THE USER HAS EVER LOGGED IN BEFORE
	if($TempLastLogon -eq "NEVER_LOGGED_IN") { $objFinalResultsTemp.FinalStatus = "NEVER_LOGGED_IN" ;$FailState = $true}
	
	# CHECK IF THE ACCOUNT IS ENABLED
	if ($TempAccountState -eq $false) { $objFinalResultsTemp.FinalStatus = "ACCOUNT_DISABLED" ; $FailState = $true }		
	
	# CHECK IF THE NAME OF THE FOLDER (WHICH SHOULD BE THE NAME OF THE AD USER ACCOUNT) EVEN EXISTS IN ACTIVE DIRECTORY AT ALL
	if( $TempEffectiveUserName = "") { $objFinalResultsTemp.FinalStatus = "FOLDER_USER_NOT_IN_AD"  ; $FailState = $true }

	# ASSUMING ALL OF THE CONDITIONS ABOVE ARE FALSE, IT MEANS THE USER AND HOME FOLDER ARE CONFIGURED CORRECTLY
	if($FailState -eq $false ) { $objFinalResultsTemp.FinalStatus = "OK" }
			
	# COMMIT THE RESULTS FOR THIS HOME FOLDER TO OUR MAIN OBJECT
	$objFinalResults += $objFinalResultsTemp

	# STOP THE STOPWATCH THAT IS USED TO RECORD HOW LONG EACH HOME FOLDER TAKES TO PROCESS TO USE FOR THE TIME REMAINING CALCULATION
	$StopWatch.Stop()
	
	# GET THE ELAPSED SECONDS FROM THE STOPWATCH FOR HOW LONG THIS HOME DRIVE TOOK TO PROCESS
	$ts = $StopWatch.Elapsed.TotalSeconds
	# IF IT'S LESS THAN 5, SET IT TO 1 AS WE ARE ROUNDING AND IF A FOLDER IS PROCESSED IN 0.4 SECONDS, WE'LL ATTEMPT TO DIVIDE BY 0
	if($ts -lt 0.5) { $ts = 1 }
		$TimeToGo = ($TotalFolderstoScan - $i) / [decimal]::round($ts) 
	$StopWatch.Reset()
} # END RELOADALLDATA IF STATEMENT
	
# SAVE THE RESULTS OF THIS SCAN TO THE CLIPBOARD SO IT CAN BE PASTED INTO EXCEL
$objFinalResults | sort FinalStatus, FS_ADHomeFolderMatches, AccountEnabled, User_ACLMatches, OU_ServerMatches | ConvertTo-Csv -NoTypeInformation | clip.exe

#endregion [/003] ===

2 comments

  1. Jason

    If I ever find myself on an interstellar voyage where it is likely we will need someone on board that is very resourceful…I am taking this man with me.

  2. Annie

    Hello,
    Have you re-vamped this at all? Looks like a great script.

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">