Using powershell to find orphaned home folders.

Recently, I was tasked with cleaning up space on the file server used to store users Home drives. In an effort to keep the drive from completely filling up, I used the following steps:

  • I ran some scripts to find iTunes libraries, cleaned up 40GB of policy violations with that one.

https://svn.as35684.net/joey/Public/SO/8729014/foo.ps1

  • Using foldersize to find the largest home folders and worked with end users to clean up another 100GB.

http://foldersize.sourceforge.net/

  • Next I ran this powershell scripts to audit the folders for the following:
    • no AD user account
    • disabled AD user account

https://gallery.technet.microsoft.com/scriptcenter/Script-to-get-orphaned-ea7b3075#content

I used the Export cmdlet from here:  (to save the output to a CSV)

http://technet.microsoft.com/en-us/library/ee176825.aspx

The outcome of this work was freeing up 500 GB of disk space!

Here is the structure of the command I used to execute the command and send the output to CSV:

 .\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Servername\share -FolderSize | Export-CSV c:\scripts\orphaned.csv

Here is a copy of the Powershell code to find the orphaned folders in case the link goes bad:

<#
.SYNOPSIS
Checks if home folder still has an enabled AD account and list size of the folder

.DESCRIPTION
This script queries AD with the name of the home folder. If this query does not result in an account or a disabled account the script will list the folder size with the folder path and error message. The script will output an array of PSObject which can be piped into various Format-* and Export-* Cmdlets.

.PARAMETER HomeFolderPath
This parameter determines which folder should be scanned. A list of all folders will be checked for matching samaccountnames in Active Directory. Any folders that do not have a name that matches a samaccount in AD or that match a disabled account are listed.

.PARAMETER FolderSize
This parameter determines if the folder size should be retrieved for orphaned home folders. Not specifying this parameter will significantly increase speed of execution.

.PARAMETER MoveFolderPath
Specifying this parameter will move all orphaned folders to the specified folder.

.PARAMETER MoveDisabled
This switch parameter works in combination with the MoveFolderPath parameter, it will also move the homefolders of disabled accounts.

.NOTES
Name: Get-OrphanHomeFolder.ps1
Author: Jaap Brasser
Version: 1.5.1
DateCreated: 2012-10-19
DateUpdated: 2013-05-24

.LINK
http://www.jaapbrasser.com

.EXAMPLE
.\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Server01\Home -FolderSize

Description:
Will list all the folders in the \\Server01\Home path. For each of these folders it will query AD using the foldername, if the query does not return an AD account or a disabled AD account an error will be logged and the size of the folder will be reported

.EXAMPLE
.\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Server02\Fileshare\Home | Format-Table -AutoSize

Description:
Will list all the folders in the \\Server02\Fileshare\Home. Will wait until all folders are processed before piping the input into the Format-Table Cmdlet and displaying the results in the console.

.EXAMPLE
.\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Server02\Fileshare\Home -MoveFolderPath \\Server03\Fileshare\MovedHomeFolders

Description:
Will list all the folders in the \\Server02\Fileshare\Home folder and will move orphaned folders to \\Server03\Fileshare\MovedHomeFolders while displaying results to console.
#>
param(
[Parameter(Mandatory=$true)]
$HomeFolderPath,
$MoveFolderPath,
[switch]$FolderSize,
[switch]$MoveDisabled
)
# Check if HomeFolderPath is found, exit with warning message if path is incorrect
if (!(Test-Path -LiteralPath $HomeFolderPath)){
Write-Warning “HomeFolderPath not found: $HomeFolderPath”
exit
}

# Check if MoveFolderPath is found, exit with warning message if path is incorrect
if ($MoveFolderPath) {
if (!(Test-Path -LiteralPath $MoveFolderPath)){
Write-Warning “MoveFolderPath not found: $MoveFolderPath”
exit
}
}

# Main loop, for each folder found under home folder path AD is queried to find a matching samaccountname
Get-ChildItem -LiteralPath “$HomeFolderPath” -Force | Where-Object {$_.PSIsContainer} | ForEach-Object {
$CurrentPath = Split-Path -Path $_ -Leaf
$ADResult = ([adsisearcher]”(samaccountname=$CurrentPath)”).Findone()

# If no matching samaccountname is found this code is executed and displayed
if (!($ADResult)) {
$HashProps = @{
‘Error’ = ‘Account does not exist and has a home folder’
‘FullPath’ = $_.FullName
}
if ($FolderSize) {
$HashProps.SizeinBytes = [long](Get-ChildItem -LiteralPath $_.Fullname -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue | Select-Object -Exp Sum)
$HashProps.SizeinMegaBytes = “{0:n2}” -f ($HashProps.SizeinBytes/1MB)
}

if ($MoveFolderPath) {
$HashProps.DestinationFullPath = Join-Path -Path $MoveFolderPath -ChildPath (Split-Path -Path $_.FullName -Leaf)
Move-Item -LiteralPath $HashProps.FullPath -Destination $HashProps.DestinationFullPath -Force
}

# Output the object
New-Object -TypeName PSCustomObject -Property $HashProps

# If samaccountname is found but the account is disabled this information is displayed
} elseif (([boolean]($ADResult.Properties.useraccountcontrol[0] -band 2))) {
$HashProps = @{
‘Error’ = ‘Account is disabled and has a home folder’
‘FullPath’ = $_.FullName
}
if ($FolderSize) {
$HashProps.SizeinBytes = [long](Get-ChildItem -LiteralPath $_.Fullname -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue | Select-Object -Exp Sum)
$HashProps.SizeinMegaBytes = “{0:n2}” -f ($HashProps.SizeinBytes/1MB)
}

if ($MoveFolderPath -and $MoveDisabled) {
$HashProps.DestinationFullPath = Join-Path -Path $MoveFolderPath -ChildPath (Split-Path -Path $_.FullName -Leaf)
Move-Item -LiteralPath $HashProps.FullPath -Destination $HashProps.DestinationFullPath -Force
}

# Output the object
New-Object -TypeName PSCustomObject -Property $HashProps

# Reserved for future use, folders that do have active user accounts
} else {
}
}