Compare home folders with user names and fetch folder size

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]

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.

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.

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.

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 :wink:

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.

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. :wink:

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

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