Compare home folders with user names and fetch folder size

This topic contains 7 replies, has 3 voices, and was last updated by  Max Kozlov 9 months, 1 week ago.

  • Author
    Posts
  • #61398

    I am trying to make a script which loops through the (sub)folders were the home folders for the users are stored and compare folder names with user names (they are identical). I want the script to print the names of the folders to users no longer working here, aka the folders that do not have a matching user name in AD. I want the script to display folder size as well so I know how much space is wasted on these folders as well.

    I have an idea that the folder names are put in an array and an foreach-loop runs through the list of folder names and fetches the size of the folders. The code fetching the folder names works but the rest does not work. What am I doing wrong.

    $UserNames = Get-ADUser -Filter * -SearchBase "OU=NAME_OF_OU_WITH_USERS3,OU=NAME_OF_OU_WITH_USERS2,OU=NAME_OF_OU_WITH_USERS1,DC=DOMAIN_NAME,DC=COUNTRY_CODE" | Select -ExpandProperty samaccountname
    $UserRegex = ($UserNames | ForEach{[RegEx]::Escape($_)}) -join "|"
    
    $myArray = (Get-ChildItem -Path "\\file2\Felles\Home\*" -Directory | Where{$_.Name -notmatch $UserRegex})
    #$myArray
    
    foreach ($mapper in $myArray) {
        #Param ($mapper = $(Throw "no folder name specified"))
    
        # calculate folder size and recurse as needed
        $size = 0
        Foreach ($file in $(ls $mapper -recurse)){
        If (-not ($file.psiscontainer)) {
            $size += $file.length
            }
        }
    
        # return the value and go back to caller
        echo $size
    }
    [/pre]
  • #61401

    Don Jones
    Keymaster

    First, be aware that adding up the file sizes like that is likely going to be slower than the general technique in https://devopscollective.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/getting-folder-sizes.html. You're taking a pretty VBScript-ish approach, which is fine, but it works a bit contrary to how PowerShell is designed, so you'll end up working harder and running slower.

    Also, "echo" is kind of a bad approach. It's not even a proper PowerShell term; you probably want to be using Write-Output.

    I would probably get the users, enumerate THOSE in a ForEach to get one username at a time, and then run the above technique per user folder. I'd output a custom object containing the user name and file size...

    [pscustomobject]@{'Username'=$whatever;'FolderSize'=$sum}
    

    As written, that'll output to the pipeline, which is what Write-Output does. The result will be pipe-able to Export-CSV, ConvertTo-HTML, or a huge variety of other cmdlets, which is how PowerShell is intended to work. You may want to pick up "Learn Windows PowerShell in a Month of Lunches, 3rd Edition" and "Learn PowerShell Toolmaking in a Month of Lunches" in terms of better understanding some of the shell's native patterns, if you're interested.

  • #61405

    Don Jones
    Keymaster

    This being a kind of approach that a lot of people take, would you mind if I used your example above in a presentation or article? I think a lot of people would relate to it, and I'd like to use it as the basis for a walk-through of how to modify the approach. I'd obviously give you credit, and it looks like you've removed any proprietary information already. I can post the final result here, too.

  • #61408

    Don Jones
    Keymaster

    Also, I missed:

    "I want the script to print the names of the folders to users no longer working here, aka the folders that do not have a matching user name in AD."

    My suggestion won't quite do that because it's only including users from AD as its source. I would probably achieve this second goal with a second command (e.g., make two functions in a script). All you'd need to do is get the top-level folders from your server (e.g., the user names, use Get-ChildItem -Directory), sort them by user name, and compare-object them to a sorted list of user names from AD. Compare-Object will show you any folders without users, and any users without folders.

    Having two functions – one to get folder sizes and another to get "orphan" folders – would also follow a PowerShell pattern of having tightly-scoped tools. You could then run both of those functions from a simple script (or the bottom of the script containing the functions) to get a consolidated result. Again – if you don't mind me using this in an article or something, I'd actually be willing to write this up.

  • #61410

    I have had variety of versions of this script and I felt like I were moving far away from were I should be. PowerShell does hefty stuff in a simple manner. If I crack this nut I imagine its a far simpler approach than the one I have started on now.

    Sure thing. Go ahead with your article 😉

  • #61432

    Don Jones
    Keymaster

    I think I'd do this (still testing, so appreciate patience with errors):

    function Get-FolderSize {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory=$True,
                       ValueFromPipeline=$True,
                       ValueFromPipelineByPropertyName=$True)]
            [string[]]$Path
        )
        BEGIN {}
        PROCESS {
            ForEach ($folder in $path) {
                Write-Verbose "Checking $folder"
                if (Test-Path -Path $folder) {
                    Write-Verbose " + Path exists"
                    $params = @{'Path'=$folder
                                'Recurse'=$true
                                'File'=$true}
                    $measure = Get-ChildItem @params |
                               Measure-Object -Property Length -Sum
                    [pscustomobject]@{'Path'=$folder
                                      'Files'=$measure.count
                                      'Bytes'=$measure.sum}
                } else {
                    Write-Verbose " - Path does not exist"
                    [pscustomobject]@{'Path'=$folder
                                      'Files'=0
                                      'Bytes'=0}
                } #if folder exists
            } #foreach
        } #PROCESS
        END {}
    } #function
    
    
    function Get-UserHomeFolderInfo {
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory=$True)]
            [string]$HomeRootPath
        )
        BEGIN {}
        PROCESS {
            Write-Verbose "Enumerating $HomeRootPath"
            $params = @{'Path'=$HomeRootPath
                        'Directory'=$True}
            ForEach ($folder in (Get-ChildItem @params)) {
                
                Write-Verbose "Checking $($folder.name)"
                $params = @{'Identity'=$folder.name
                            'ErrorAction'='SilentlyContinue'}
                $user = Get-ADUser @params
    
                if ($user) {
                    Write-Verbose " + User exists"
                    $result = Get-FolderSize -Path $folder.fullname
                    [pscustomobject]@{'User'=$folder.name
                                      'Path'=$folder.fullname
                                      'Files'=$result.files
                                      'Bytes'=$result.bytes
                                      'Status'='OK'}
                } else {
                    Write-Verbose " - User does not exist"
                    [pscustomobject]@{'User'=$folder.name
                                      'Path'=$folder.fullname
                                      'Files'=0
                                      'Bytes'=0
                                      'Status'="Orphan"}
                } #if user exists
    
            } #foreach
        } #PROCESS
        END {}
    }
    

    Then I'd just run Get-UserHomeFolderInfo and feed it the root location for your home folders. It'll tell you which folders don't have a corresponding user, and for folders that do, you'll get a file count and folder size. This may obviously take some time to run – if I had a TON of LARGE user folders, I'd probably incorporate Workflow to do the actual folder-size-stuff, since that can run multiples in parallel.

  • #61860

    I have not had the time to test this before today but I have stored the script in a file called "HomeRootPath.ps1". I executing the script by typing ".\HomeRootPath.ps1 , right? I tried executing the script first to put it in the memory and then run the command "Get-UserHomeFolderInfo -HomeRootPath " but nothing happened. Guess this tells how n00b I am. 😉

  • #61863

    Max Kozlov
    Participant

    you need to dot-source this file and run it with parameters
    like

    cd "C:\PlaceWhereImPutTheScript\"
    . .\HomeRootPath.ps1
    Get-UserHomeFolderInfo -HomeRootPath c:\users\Einar
    

You must be logged in to reply to this topic.