Compare home folders with user names and fetch folder size

This topic contains 7 replies, has 3 voices, and was last updated by Profile photo of Max Kozlov Max Kozlov 4 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
    Profile photo of Don Jones
    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
    Profile photo of Don Jones
    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
    Profile photo of Don Jones
    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
    Profile photo of Don Jones
    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
    Profile photo of Max Kozlov
    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.