Asura
Asura

Reputation: 13

Powershell manipulate array with folder list to show only last folders

i have to setup a Windows based file-sharing server for my work environment (500+ users in AD) and i wanted to automate things with powershell. I want to make folders based on OU's in AD hierarchy and after that assign permissions to groups of people actually working in these departments. Each ou = department and sometimes under departments there are teams and even smaller organizational units. So i will have mixed structure of folders like:

Root_directory/Tier_1/Tier_2/Tier_3/their_files_and_folders

Root_directory/Tier_1/Tier_2/their_files_and_folders

Root_directory/Tier_1/their_files_and_folders

Each tier of course can have different names, i hope you get the idea.

My problem is that i cant figure out how to get the list of last folders only created by my script based on AD tree (because i can give permissions based on them) After people will start using my server and start creating their own folders will make my current method not work. My current method is:

Get-childitem "path_to_root_shared_directory" -r -directory | Where-Object {$_.GetDirectories().Count -eq 0}

I thought that since i have a list of folders to create based on AD (in a variable - array) maybe somehow i could manipulate that array to get only the last folders. What i mean by somehow is to compare folder paths to create and leave only paths to folders that have no more sub folders (leaf folders). So if i have folder structure like:

Root_dir

Root_dir/Department_A

Root_dir/Department_A/Team_A

Root_dir/Department_A/Team_B

Root_dir/Department_B

To leave only:

Root_dir/Department_A/Team_A

Root_dir/Department_A/Team_B

Root_dir/Department_B

I hope you guys can understand what i want to achieve ;).

Upvotes: 1

Views: 500

Answers (2)

Gzeh Niert
Gzeh Niert

Reputation: 168

You'll find below an adaptation of the third solution from http://stuart-moore.com/finding-bottom-folders-file-tree-using-powershell/ to fullfill the request I need to see them in a list 1 by 1 so then i can make another foreach loop and add permissions based on them.

Function Get-MostDepthDirectory {
    [CmdletBinding(DefaultParametersetName="DirectoryScan")] 
    param (
        [Parameter(Position=0,Mandatory=$true,ParameterSetName="DirectoryScan")]
        [String]$rootDirectory,

        [Parameter(Position=0,Mandatory=$true,ParameterSetName="DirectoryList")]
        [String[]]$subDirectories       
    )

    if ($PsCmdlet.ParameterSetName -eq "DirectoryScan") {
        $subDirectories = Get-ChildItem -Recurse $rootDirectory -Directory | Select-Object -ExpandProperty FullName
    }

    $deepestDirectories = New-Object System.Collections.ArrayList
    foreach ($d in $subDirectories){
        $t = Split-Path $d -Parent
        if ($deepestDirectories -NotContains $t){
            $deepestDirectories.Add($d) | Out-Null
        } else {
            $deepestDirectories.Remove($t) | Out-Null
            $deepestDirectories.Add($d) | Out-Null
        }
    }
    return [string[]]@($deepestDirectories| Sort-Object)
}

Outputs:

PS C:\> $dirScan = @(Get-ChildItem C:\test -Recurse -Directory | Select-Object -ExpandProperty FullName | Sort-Object)
PS C:\> $dirScan
C:\test\a-level1
C:\test\a-level1\a-level2
C:\test\a-level1\b-level2
C:\test\a-level1\b-level2\a-level3
C:\test\a-level1\b-level2\b-level3
C:\test\a-level1\b-level2\c-level3
C:\test\a-level1\b-level2\c-level3\a-level4
C:\test\b-level1
C:\test\c-level1
C:\test\c-level1\a-level2
C:\test\c-level1\b-level2
C:\test\c-level1\b-level2\a-level3
PS C:\> Get-MostDepthDirectory $dirScan
C:\test\a-level1\a-level2
C:\test\a-level1\b-level2\a-level3
C:\test\a-level1\b-level2\b-level3
C:\test\a-level1\b-level2\c-level3\a-level4
C:\test\b-level1
C:\test\c-level1\a-level2
C:\test\c-level1\b-level2\a-level3
PS C:\> Get-MostDepthDirectory C:\test
C:\test\a-level1\a-level2
C:\test\a-level1\b-level2\a-level3
C:\test\a-level1\b-level2\b-level3
C:\test\a-level1\b-level2\c-level3\a-level4
C:\test\b-level1
C:\test\c-level1\a-level2
C:\test\c-level1\b-level2\a-level3
PS C:\>

Edit: I am adding a little bit of explanation.

The code snippet creates a Get-MostDepthDirectory CmdLet that can be called either with an array of directories to filter or with a directory to scan.

If called with a directory to scan (ParameterSetName="DirectoryScan"), it builds a $subDirectories variable containing the absolute path of all the subdirectories, else (ParameterSetName="DirectoryList") it takes the list of directories directly from the parameter.

It initiates a System.Collections.ArrayList object which is a collection from .NET framework, more appropriate here because it has the Add and Remove methods that allows management content of the array without the need to know the index (otherwise the code would have been even more complicated). The object will store the filtered list.

Then:

  1. It loops on the subdirectories list (foreach ($d in $subDirectories))

  2. It extracts the parent directory from the directory ($t = Split-Path $d -Parent)

  3. If the list does not contains the parent (if ($deepestDirectories -NotContains $t)), it adds the current directory ($deepestDirectories.Add($d) | Out-Null)

  4. Else, it removes the parent ($deepestDirectories.Remove($t) | Out-Null) and add the directory ($deepestDirectories.Add($d) | Out-Null)

Once the loop is done, the ArrayList is sorted and converted to an array of strings before being returned.

The Out-Null are there to prevent the output of the Add and Remove methods of ArrayList object to return their memory state to the pipeline, otherwise PowerShell may return them alongside with the filtered list of directories.

Hope it is a little more clear with the explanations.

Upvotes: 2

sodawillow
sodawillow

Reputation: 13176

Here is an approach :

Get-ChildItem "C:\RootDirectory" -Directory -Recurse |
    ForEach-Object {
        if((Get-ChildItem $_.FullName -Directory).Count) {
            $_.FullName
        }
    } | Sort-Object

Upvotes: 1

Related Questions