Reputation: 13
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
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:
It loops on the subdirectories list (foreach ($d in
$subDirectories)
)
It extracts the parent directory from the directory ($t = Split-Path $d -Parent
)
If the list does not contains the parent (if ($deepestDirectories -NotContains $t)
), it adds the current directory ($deepestDirectories.Add($d) | Out-Null
)
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
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