src/Public/Get-OrphanedUserFolders.ps1
|
#TODO Properly implement logging #TODO Add progress to deletion process #TODO Test logic for checking against homedirectory and profilepath, as this will reduce the likelihood of false positives (hopefully to zero) #TODO Take UserFoldersPath from pipeline and accept multiple paths #TODO Add param to output files to gridview so user can select which ones to delete. Alternatively, make this the default action and make the param bypass it and just remove all folders. <# .SYNOPSIS Finds home or profile folders in the specified directory that do not have a corresponding AD user. .DESCRIPTION This script will search the specified directory for folders that do not have a corresponding AD user with a matching username. If the -Delete switch is used, the script will also delete the orphaned folders. .COMPONENT ActiveDirectory .PARAMETER UserFoldersPath The path to the directory containing the user folders to check. This can be a folder that holds either home directories, profiles directories, or both. .PARAMETER Delete If specified, the script will delete the orphaned folders after a confirmation message. .PARAMETER Logfile The path to a log file to write the results of the operation to. .EXAMPLE Get-OrphanedUserFolders -UserFoldersPath "D:\Users\Home" -Delete This will search the "D:\Users\Home" directory for orphaned user folders and delete them. .EXAMPLE Get-OrphanedUserFolders -UserFoldersPath "D:\Users\Profile" -Logfile "C:\Logs\OrphanedUserFolders.log" This will search the "D:\Users\Profile" directory for orphaned user folders and write the results to "C:\Logs\OrphanedUserFolders.log". .EXAMPLE Get-OrphanedUserFolders "D:\Users\Home" -Delete -Logfile "C:\Logs\OrphanedUserFolders.log" This will search the "D:\Users\Home" directory for orphaned user folders, delete them, and write the results to "C:\Logs\OrphanedUserFolders.log". .NOTES None #> function Get-OrphanedUserFolders { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNullOrEmpty()] [String]$UserFoldersPath, [Parameter(Mandatory = $false)] [Switch]$Delete, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String]$Logfile ) begin { $temp = $ErrorActionPreference $ErrorActionPreference = 'Stop' # Validate that AD module is available and import it if (-not (Get-Module -Name ActiveDirectory)) { Throw "ActiveDirectory module is not available. Please run on a DC or install RSAT tools." } else { Import-Module ActiveDirectory -Verbose:$false } # Validate that the folder path exists if (-not (Test-Path -Path $UserFoldersPath -PathType Container)) { Throw "Folder path '$UserFoldersPath' does not exist." } # Validate log path, create if it doesn't exist If ($LogPath) { If ($LogPath -notmatch '\.\w+$') { Throw "Logfile must be a file with an extension. Any extension can be used." } if (-not (Test-Path -Path $Logfile)) { New-Item -Path $Logfile -ItemType File | Out-Null } } # Confirm that the user wants to delete orphaned folders if ($Delete) { $ConfirmMessage = "Are you sure you want to delete orphaned folders in '$UserFoldersPath'? This cannot be undone. `nMake sure to double check that all folders are actually orphaned before running. `n[y/yes to accept]" $ConfirmResult = Read-Host -Prompt $ConfirmMessage -OutVariable ConfirmResult if ($ConfirmResult -notmatch 'yes|y') { Throw "User cancelled operation." } } # Initialize an array to store orphaned folders $OrphanedFolders = @() $ErrorActionPreference = $temp } process { $StartTime = Get-Date $UserFolders = Get-ChildItem -Path $UserFoldersPath -Directory -Force $Users = Get-ADUser -Filter * | Select-Object samaccountname, homedirectory, profilepath ForEach ($Folder in $UserFolders) { $User = $null # Use regex to strip ".v*" from the folder name $FolderName = $Folder.Name -replace '\.v\d+$' try { #$User = Get-ADUser $FolderName | Select-Object samaccountname $User = $Users | Where-Object { ($_.samaccountname -eq $FolderName) -or ($_.homedirectory -eq $Folder.FullName) -or ($_.profilepath -eq $Folder.FullNam) } if (!$User) { Write-Verbose "User $FolderName not found" } } catch { Write-Output "$FolderName is orphaned" $OrphanedFolders += [PSCustomObject]@{ FolderName = $FolderName FolderPath = $Folder.FullName } If ($Delete) { Try { Remove-Folder -Path $Folder.FullName -Force -WhatIf:$WhatIfPreference -Verbose:$VerbosePreference } Catch { Write-Output "Failed to delete $FolderName. This may be due to lack of permissions: $_" } } } } } end { $FinishedTime = Get-Date $TimeTaken = $FinishedTime - $StartTime Write-Output "Operation completed in $($TimeTaken.Hours) hours, $($TimeTaken.Minutes) minutes, and $($TimeTaken.Seconds) seconds." Return $OrphanedFolders } } <# ORIGINAL VERSION AS A BACKUP IF DESPERATELY NEEDED - THIS DEFINITELY WORKS $Script:Logfile = "$psscriptroot\DeletedUsersFolders.txt" #! THESE MUST BE SET BEFORE RUNNING #! If either of these are not needed, just leave the variable and delete the relevant loop further down #!-------------------------------- $HDs = "D:\Users\Home\Students\" $PPs = "D:\Users\Profile\Students" #! ------------------------------- #* Function to write logs as well as write to the terminal window. Function Log { Param ([string]$logstring) $timestamp = Get-Date -Format "dd/MM/yyyy HH:mm:ss" Add-Content $Logfile -Value "[$timestamp]: $logstring" Write-Host $logstring } #! Delete from here to the next Log "-----" if there are no home directories Log "------------------------------" Log "" Log "Scanning all home folders in Directory: $HDs" Log "" #* For each home folder, use a try/catch to test if the user exists #* If the user doesn't exist, log and write-host the folder name $HomeFolders = Get-ChildItem -Path $HDs -Directory -Force -ErrorAction SilentlyContinue -Name ForEach ($hd in $HomeFolders) { $error.clear() try { $User = Get-ADUser $hd | Select-Object samaccountname } catch { Log "$hd is orphaned" $path = "$HDs\$hd" Remove-Item -Path $path -Recurse -Force -WhatIf } } #!Delete from here if there are no profile folders (leave the last } ) Log "" Log "------------------------------" Log "" Log "Scanning all profile folders in directory $PPs" Log "" #* For each profile folder, use a try/catch to test if the user exists #* If the user doesn't exist, log and write-host the folder name $ProfileFolders = Get-ChildItem -Path $PPs -Directory -Force -ErrorAction SilentlyContinue -Name ForEach ($pp in $ProfileFolders) { #* Remove .v6 etc from folder names for checking purposes $p = $pp.substring(0, $pp.length - 3) #* Clear any previous errors $error.clear() try { $User = Get-ADUser $p | Select-Object samaccountname } catch { Log "$pp is orphaned" $path = "$PPs\$pp" #remove-item -Path $path -Recurse -Force -whatif } } #> |