├── LICENSE ├── .github └── workflows │ └── powershell-analysis.yml ├── README.md └── Audit-UserShare.ps1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Foyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/powershell-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # https://github.com/microsoft/action-psscriptanalyzer 7 | # For more information on PSScriptAnalyzer in general, see 8 | # https://github.com/PowerShell/PSScriptAnalyzer 9 | 10 | name: PSScriptAnalyzer 11 | 12 | on: 13 | push: 14 | branches: [ main ] 15 | pull_request: 16 | branches: [ main ] 17 | schedule: 18 | - cron: '38 15 * * 4' 19 | 20 | jobs: 21 | build: 22 | name: PSScriptAnalyzer 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - name: Run PSScriptAnalyzer 28 | uses: microsoft/psscriptanalyzer-action@2044ae068e37d0161fa2127de04c19633882f061 29 | with: 30 | # Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options. 31 | # The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules. 32 | path: .\ 33 | recurse: true 34 | # Include your own basic security rules. Removing this option will run all the rules 35 | includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"' 36 | output: results.sarif 37 | 38 | # Upload the SARIF file generated in the previous step 39 | - name: Upload SARIF results file 40 | uses: github/codeql-action/upload-sarif@v1 41 | with: 42 | sarif_file: results.sarif 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Audit-UserShare 2 | 3 | Audit your users home directory by finding folders that are no longer needed. 4 | 5 | 6 | ## Purpose 7 | Quickly review your users roaming home directory and provide output to make decisions on the accounts and folders. 8 | 9 | This can be pointed at any directory with subdirectories with Active Directory SamAccountNames. 10 | 11 | ## Usage 12 | Parameter |Explanation 13 | ----------|-------------- 14 | -folderSize| Calculates the folder sizes for the accounts found. 15 | -Remove| Request to remove a group of users folders. [OPTIONS] “NoAccount”,”DisabledUser”,"Old" 16 | -Move| Request to move a group of users to another folder. [OPTIONS] “NoAccount”,”DisabledUser”,"Old" 17 | -MovePath| The value used when the Move option is specfied at the Delete parameters. 18 | -daysSinceLogin| Looks for the users in the user share who haven't logged in the the past X days. [Default: 60 Days] 19 | -searchDir| Specifes the directory to search in through. [Default: The running users home directory queried from AD] 20 | -saveFolder| The folder inwhich you would like to save output to. If not specfied, it will be logged to the console. 21 | -Exclude| Pass in any folder name seperated by a comma you would like to exclude from the search. 22 | -logPath| Path to the log if required. 23 | 24 | 25 | ## Example of Usage: 26 | 27 | ### Locate old user, and nonexistant user who still have files stored on your fileserver: 28 | Audit-UserShare -searchDir \\server\userFolder 29 | 30 | ### Calculate the folder sizes and deleted those that are nolonger needed: 31 | Audit-UserShare -folderSize -searchDir \\server\userFolder 32 | Audit-UserShare -Move ”DisabledUser” -searchDir \\server\userFolder 33 | Audit-UserShare -Remove NoAccount -searchDir \\server\userFolder 34 | 35 | 36 | ## Example of output: 37 | 38 | SamAccountName Enabled No Account LastLogonDate 39 | -------------- ------- ---------- ------------- 40 | TestUser1 True False 11-24-2021 9:08:03 AM 41 | TestUser1 True False 11-10-2021 9:44:51 AM 42 | TestUser1 True False 10-27-2021 7:27:43 PM 43 | TestUser1 True False 11-22-2021 1:48:54 PM 44 | TestUser1 False False 11-22-2021 6:25:20 AM 45 | TestUser1 True False 12-09-2021 9:51:31 AM 46 | TestUser1 False False 11-25-2021 10:34:46 AM 47 | TestUser1 True False 11-05-2021 8:34:55 AM 48 | TestUser1 False False 12-02-2021 1:00:17 PM 49 | -------------------------------------------------------------------------------- /Audit-UserShare.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Audit your user share to identify potentially unneeded folders. 4 | 5 | .PARAMETER Remove 6 | Request to remove a group of users folders. 7 | [OPTIONS] “NoAccount”,”DisabledUser”,"Old" 8 | 9 | .PARAMETER Move 10 | Request to move a group of users to another folder. 11 | [OPTIONS] “NoAccount”,”DisabledUser”,"Old" 12 | 13 | .PARAMETER MovePath 14 | The value used when the Move option is specfied to provide the path to move the folder to. 15 | 16 | .PARAMETER daysSinceLogin 17 | Looks for the users in the user share who haven't logged in the the past X days. 18 | [Default: 60 Days] 19 | 20 | .PARAMETER folderSize 21 | Calculates the folder sizes for the accounts found. 22 | 23 | .PARAMETER searchDir 24 | Specifes the directory to search in through. 25 | [Default: The running users home directory] 26 | 27 | .PARAMETER saveFolder 28 | The folder inwhich you would like to save output to. 29 | If not specfied, it will be logged to the console. 30 | .PARAMETER Exclude 31 | Pass in any folder name seperated by a comma you would like to exclude. 32 | .PARAMETER logPath 33 | Path to the log if required. 34 | 35 | .EXAMPLE 36 | Audit-UserShare -folderSize -searchDir \\server\users -daysSinceLogin 30 37 | SamAccountName Enabled No Account LastLogonDate Size/GB 38 | -------------- ------- ---------- ------------- ------- 39 | testUser1 True False 11-11-2021 3:19:49 PM 0.12 40 | testUser2 True False 11-12-2021 7:32:26 AM 0.25 41 | testUser3 False False 11-12-2021 10:05:16 AM 0.68 42 | testUser4 False False 11-14-2021 5:23:28 AM 0.64 43 | testUser5 True False 11-10-2021 9:44:51 AM 0.09 44 | testUser6 True False 10-27-2021 7:27:43 PM 0.02 45 | testUser7 True False 11-05-2021 8:34:55 AM 1.03 46 | testUser8 N/A True 0.57 47 | testUser9 True False 11-12-2021 3:17:10 PM 0.6 48 | .EXAMPLE 49 | Audit-UserShare -daysSinceLogin 30 -Exclude testUser8 50 | SamAccountName Enabled No Account LastLogonDate Size/GB 51 | -------------- ------- ---------- ------------- ------- 52 | testUser1 True False 11-11-2021 3:19:49 PM 0.12 53 | testUser2 True False 11-12-2021 7:32:26 AM 0.25 54 | testUser3 False False 11-12-2021 10:05:16 AM 0.68 55 | testUser4 False False 11-14-2021 5:23:28 AM 0.64 56 | testUser5 True False 11-10-2021 9:44:51 AM 0.09 57 | testUser6 True False 10-27-2021 7:27:43 PM 0.02 58 | testUser7 True False 11-05-2021 8:34:55 AM 1.03 59 | testUser9 True False 11-12-2021 3:17:10 PM 0.6 60 | .EXAMPLE 61 | Audit-UserShare -daysSinceLogin 30 -Remove Old 62 | SamAccountName Enabled No Account LastLogonDate Size/GB 63 | -------------- ------- ---------- ------------- ------- 64 | testUser1 True False 11-11-2021 3:19:49 PM 0.12 65 | testUser2 True False 11-12-2021 7:32:26 AM 0.25 66 | testUser3 False False 11-12-2021 10:05:16 AM 0.68 67 | testUser4 False False 11-14-2021 5:23:28 AM 0.64 68 | testUser5 True False 11-10-2021 9:44:51 AM 0.09 69 | testUser6 True False 10-27-2021 7:27:43 PM 0.02 70 | testUser7 True False 11-05-2021 8:34:55 AM 1.03 71 | testUser9 True False 11-12-2021 3:17:10 PM 0.6 72 | #> 73 | 74 | param ( 75 | [switch]$folderSize, 76 | 77 | [ValidateScript({Test-Path $_})] 78 | [String] $searchDir=$null, 79 | 80 | [ValidateScript({Test-Path $_})] 81 | [String]$saveFolder=$null, 82 | 83 | [ValidateSet(“NoAccount”,”DisabledUser”,"Old")] 84 | [String] $Move, 85 | 86 | [ValidateSet(“NoAccount”,”DisabledUser”,"Old")] 87 | [String] $Remove, 88 | 89 | [ValidateScript({Test-Path $_})] 90 | [String] $MovePath='', 91 | 92 | [Int]$daysSinceLogin=60, 93 | 94 | [Array]$Exclude=@(), 95 | 96 | [ValidateScript({Test-Path $_})] 97 | [String]$logPath='' 98 | 99 | ) 100 | 101 | Import-Module ActiveDirectory 102 | 103 | #Removes or Moves user folder 104 | #Param $user: The custom object made in the main function 105 | #Param $action: Derived from $DeleteNoUser or $deleteOldUser this specfies to remove or move 106 | function Remove-Folder{ 107 | [cmdletbinding()] 108 | Param($oldUser) 109 | #Invoke-Command -ComputerName "ServerName" -scriptblock {Get-SmbOpenFile|Where-Object Path -like "PathToUserFolder"|Close-SmbOpenFile -Force} 110 | #move-Item "ServerPath" -Recurse -Force $searchDir\$i 111 | #Write-Warning -Message "You are able to (re)move folders, are you sure?" -WarningAction Inquire 112 | $loopCount=0 113 | if($Move){$action="move"; $Group=$Move} 114 | elseif($Remove){$action="remove";$Group=$Remove} 115 | 116 | foreach($i in $oldUser){ 117 | foreach($user in $i){ 118 | Write-Progress -Activity "$($user.SamAccountName)" -PercentComplete $(($loopCount/($oldUser.count))*100) 119 | if($Group -like "NoAccount" -and $user."No Account" -eq $false){continue} 120 | elseif($Group -like "DisabledUser" -and $user.Enabled -eq $true){continue} 121 | elseif($Group -like "Old" -and $user.LastLogonDate -like $null){continue} 122 | 123 | if($logPath){logToPath $logPath "$action : $($user.SamAccountName)"} 124 | #Invoke-Command -ComputerName $searchDir 125 | 126 | if($action -eq "move"){ 127 | 128 | #Verify MovePath is specfied when moving folders and follow through if correct 129 | if($MovePath -eq ''){ 130 | Write-Error -Message "Specify -MovePath" -Category InvalidArgument 131 | Exit 1 132 | } 133 | 134 | Move-Item -Force $user.dir $MovePath 135 | #Write-Host("Moving $user.SamAccountName") 136 | 137 | }elseif($action -eq "remove"){ 138 | 139 | #Get-ChildItem $searchDir\$($user.SamAccountName) -Include * -Recurse | ForEach { $_.Delete()} #| Remove-Item -Force -Recurse -ErrorAction SilentlyContinue 140 | Get-ChildItem $user.dir -Include * -Recurse | Remove-Item -Force -ErrorAction SilentlyContinue 141 | } 142 | $loopCount+=1 143 | } 144 | } 145 | 146 | } 147 | 148 | #Calculate the folder size of each users home folder 149 | #Param: $oldUser: The custom object made in the main function 150 | function Get-FolderSize($oldUser){ 151 | $totalSize=0 152 | $loopCount=0 153 | $totalCount=$oldUser.count 154 | $rtnValue = [System.Collections.Generic.List[object]]::new() 155 | 156 | 157 | foreach($i in $oldUser){ 158 | foreach($user in $i){ 159 | Write-Progress -Activity "Calculating size of : $($user.SamAccountName)" -PercentComplete $(($loopCount/$totalCount)*100) 160 | #$user 161 | if($logPath){logToPath $logPath "Calculating size : $($user.SamAccountName)"} 162 | 163 | $log='' 164 | $log=robocopy $user.dir NULL /L /S /NJH /BYTES /NC /NDL /XJ /R:0 /W:0 /MT:32 /NFL 165 | $Line = $log | Where-Object{$_ -match "^\s+Bytes.+"} 166 | $Line = $Line -split "\s+" 167 | 168 | 169 | $rtnValue.add($($user | select-object -property *,@{n="Size/GB";e={$([math]::Round($Line[3]/1GB,2))}})) 170 | $loopCount++ 171 | } 172 | } 173 | 174 | return $rtnValue 175 | } 176 | 177 | 178 | # Takes in path to usershare and a date to compare to the last time the user logedin 179 | # Returns List of users that they not logged in since this date as well as users unable to be queried 180 | function Get-OldUsers($searchDir,$queryDate){ 181 | 182 | $oldUser = [System.Collections.Generic.List[object]]::new() 183 | 184 | $userShareList=$(get-childitem $searchDir) 185 | $totalCount=$userShareList.count 186 | 187 | $loopCount=0 188 | 189 | 190 | #Loop through the list of users in the share and test for AD accounts if older than x days 191 | foreach($dir in $userShareList){ 192 | Write-Progress -Activity "Searching : $searchDir$dir" -PercentComplete $(($loopCount/$totalCount)*100) 193 | 194 | #Test for if folder and not excluded 195 | if($dir.PSIsContainer -and ($dir.Name -notin $Exclude)){ 196 | 197 | try{ 198 | $user=$(Get-ADUser -Identity $dir.Name -Properties "LastLogonDate" | Where-Object {($_.LastLogonDate -lt $queryDate) -and ($NULL -ne $_.LastLogonDate)}) 199 | $user = $($user |select-object SamAccountName,Enabled,LastLogonDate,@{n="Dir";e={$dir.fullname}}) 200 | 201 | }catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]{ 202 | $user = $(""|select-object @{n="SamAccountName";e={$dir.Name}},@{n="Enabled";e={"N/A"}},@{n="LastLogonDate";e={""}},@{n="Dir";e={$dir.fullname}}) 203 | } 204 | 205 | if($logPath -and $user){logToPath $logPath "User Found : $($user.SamAccountName)"} 206 | if($user){ 207 | 208 | $olduser.add($user) 209 | } 210 | $loopCount++ 211 | } 212 | } 213 | return($olduser) 214 | } 215 | 216 | function logToPath($logPath,$message){ 217 | $date=$(get-date -format "dd.MM.yyyy-HHmmss") 218 | Write-Output "$date : $message" | Out-File $logPath\"UserShareAudit.log" -Append 219 | } 220 | 221 | 222 | 223 | 224 | ## Main ## 225 | #$oldUser = [System.Collections.Generic.List[object]]::new() 226 | 227 | if(!$searchDir){ 228 | $searchDir=($(get-aduser $env:username -Properties HomeDirectory).HomeDirectory -replace "$env:username") 229 | } 230 | 231 | $queryDate = (Get-Date).AddDays(-$daysSinceLogin) 232 | $olduser = (Get-OldUsers $searchDir $queryDate) 233 | 234 | # If -FolderSize is specfied, pass oldUser var and calculate the folder size 235 | if($folderSize){$olduser = Get-FolderSize $oldUser} 236 | 237 | # If move or remove, pass oldUser to Remove-Folder Function 238 | if($Move -or $Remove){Remove-Folder $oldUser} 239 | 240 | # If saveFolder if specfied, save the output to a CSV file 241 | # else output to console 242 | if($saveFolder){ 243 | if($logPath){logToPath $logPath "Saving Output"} 244 | 245 | $date=$(get-date -format "dd.MM.yyyy-HHmmss") 246 | $oldUser | export-csv -NoTypeInformation "$saveFolder\UserShareAudit - $date.csv" 247 | 248 | }else{ 249 | $oldUser | ft 250 | } 251 | 252 | 253 | --------------------------------------------------------------------------------