Author Posts

April 28, 2017 at 3:50 pm

Hello there, i am facing a problem that i could not resolve, and i am stucked
I made a function that return all the directories and subdirectories of a given path and depth.
I know that in Powershell V5 the -depth parameter is included in the gci cmdlet, but im dealing with old windows servers (with PS v2), so i have to
make a compatible function with old versions

The problem is, that i want to list ALL subdirectories (inlcuding those were i don´t haver permission).
But, the issue is that i don´t retrieve those folders were i dont have permission. Those folders remains like hidden.

The only solution i found, i to use -recurse, which doesn´t solve my problem, cause -recurse go through all the directories untill end.

So basically, i need a way to list ALL directories (also those directories i dont have permission) until the -depth value that i put.

I transcribe the part that does this task

....
if ($depth){
	for($i=1;$i -le $depth;$i++){
	
		$actualSubdirectory=$symbol * $i
		$subfolders+= Get-ChildItem $drive$actualSubdirectory -ErrorVariable +err
			-ErrorAction SilentlyContinue | ? {$_.psiscontainer -eq $true} | 
				Select-Object -ExpandProperty FullName 
	}
}
elseif (-not $depth){

    # 
        $subfolders = Get-ChildItem $drive -Recurse -ErrorVariable +err `
			-ErrorAction SilentlyContinue | ? {$_.psiscontainer -eq $true} | 
				Select-Object -ExpandProperty FullName
}
....   	

example:
$symbol='*\'
$drive='C:\temp\carpeta1'

I will apreciatte any help in advance.
Thanks a lot!

April 28, 2017 at 6:03 pm

If you want to specify depth, you'd have to implement recursion manually. I would have to imagine that someone has done this before, but this is a basic example:

function Get-ChildItemCustom {
    [CmdLetBinding()]
    param (
        $Path,
        $Depth 
    )
    begin {
        
    }
    process {
        foreach ($item in Get-ChildItem -Path $Path ) {
            if ($item.PSIsContainer) {
                Write-Verbose (($item.fullname -split '\\').Count)
                if (($item.fullname -split '\\').Count -le $Depth) {
                    Get-ChildItemCustom -Path $item.FullName
                }
                else {
                    Write-Verbose "Stopping because of depth param"
                }
                
            }
            else {
                $item
            }
        }
    }
    end {
        
    }
}

Get-ChildItemCustom -Path C:\Windows -Depth 3 -Verbose

Output:

C:\Windows\Provisioning\categories.xml                              
C:\Windows\Provisioning\IccidToRegion.xml                           
C:\Windows\Provisioning\Microsoft-Common-Provisioning.dat           
C:\Windows\Provisioning\Microsoft-Desktop-Provisioning.dat          
VERBOSE: 3
VERBOSE: 4
VERBOSE: Stopping because of depth param
C:\Windows\Registration\R000000000001.clb                           
C:\Windows\Registration\R00000000000c.clb                           
C:\Windows\Registration\R00000000000d.clb                           
C:\Windows\Registration\{02D4B3F1-FD88-11D1-960D-00805FC79235}.{D...
VERBOSE: 3
VERBOSE: 4
VERBOSE: Stopping because of depth param
VERBOSE: 4
VERBOSE: Stopping because of depth param
VERBOSE: 4
VERBOSE: Stopping because of depth param
C:\Windows\rescache\ResCache.mni          

April 29, 2017 at 7:31 pm

isnt this what your looking for?

Get-ChildItem d:\ -Directory -Depth 2

April 30, 2017 at 5:48 am

Rob Simmers, it´s very near what i am looking for. A recursive way

I was trying tu modify it, so i can receive a PS custom object.

 foreach ($item in (Get-ChildItem $drive -ErrorAction SilentlyContinue | Where-Object {$_.psiscontainer -eq $true})){
                        if (($item.FullName -split "\\").Count -le $depth){

                                $hashTable=@{
                                    Folders=Get-ChildItem -Path $item.FullName -ErrorAction SilentlyContinue -ErrorVariable +err
                                    Errors=$err
                                }
                                $prop=New-Object -TypeName psobject -Property $hashTable
                                $script:folderArray+=$prop



                                get-AclLocallyLocal -drive $item.FullName
                        }
                        else{

                            break;
                        }
                }
                 return $script:folderArray

But i get the result more than one time. I get a line for each recursive pass.
And also i dont retrieve a folder that i haver permission
Here is the structure and the result i get

C:\Windows\Temp\Folder
       Folder 1 (No Permission)
       Folder 2 (with permission)
Get-AclLocallyLocal C:\Windows\Temp\Folder\ -depth 5

Result:

Errors                                                           Folders
------                                                           -------
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}
{Access to the path 'C:\Windows\Temp\Folder\Folder1' is denied.}

April 30, 2017 at 5:51 am

Chris Bakker, yeap. But i´m dealing with windows servers 2003, which have powershell v2, and therefore do not have the -depth parameter 🙁

May 1, 2017 at 1:18 pm

There are several bad practices in your code.

  • $script:folderArray+=$prop is writing to a global variable versus having a function return results.
    function Get-ChildItemCustom {
        [CmdLetBinding()]
        param (
            $Path,
            $Depth 
        )
        begin {
            
        }
        process {
            #Everything that occurs in the loop is written to $results, no need to +=
            $results = foreach ($item in Get-ChildItem -Path $Path ) {
                if ($item.PSIsContainer) {
                    Write-Verbose (($item.fullname -split '\\').Count)
                    if (($item.fullname -split '\\').Count -le $Depth) {
                        Get-ChildItemCustom -Path $item.FullName
                    }
                    else {
                        Write-Verbose "Stopping because of depth param"
                    }
                }
                else {
                    $item.fullname
                }
            }
        }
        end {
            $results
        }
    }
    
    
    $myResults = Get-ChildItemCustom -Path C:\Windows -Depth 3 
    $myResults
    
  • break; will break the current loop which you need the loop to continue for each item, break stops the enumeration
  • $err is unnecessary. The $Error global variable will contain any failures that occured. You may want to add $Error.Clear() to the begin{} block to clear existing errors and it any Get-ChildItem failure will be logged there. I've had a seperate function that parsed the $Error varaible to get Get-ChildItem failures
    PS C:\Users\Rob> $Error.Clear()
    
    PS C:\Users\Rob> #run code
    
    PS C:\Users\Rob> $Error
    Get-ChildItem : Access to the path 'C:\Windows\Prefetch' is denied.
    At line:12 char:38
    +         $results = foreach ($item in Get-ChildItem -Path $Path ) {
    +                                      ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : PermissionDenied: (C:\Windows\Prefetch:String) [Get-ChildItem], UnauthorizedAccessException
        + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
     
    Get-ChildItem : Access to the path 'C:\Windows\ModemLogs' is denied.
    At line:12 char:38
    +         $results = foreach ($item in Get-ChildItem -Path $Path ) {
    +                                      ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : PermissionDenied: (C:\Windows\ModemLogs:String) [Get-ChildItem], UnauthorizedAccessException
        + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
     
    Get-ChildItem : Access to the path 'C:\Windows\Minidump' is denied.
    At line:12 char:38
    +         $results = foreach ($item in Get-ChildItem -Path $Path ) {
    +                                      ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : PermissionDenied: (C:\Windows\Minidump:String) [Get-ChildItem], UnauthorizedAccessException
        + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
     
    Get-ChildItem : Access to the path 'C:\Windows\LiveKernelReports' is denied.
    At line:12 char:38
    +         $results = foreach ($item in Get-ChildItem -Path $Path ) {
    +                                      ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : PermissionDenied: (C:\Windows\LiveKernelReports:String) [Get-ChildItem], UnauthorizedAccessException
        + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
     
    Get-ChildItem : Access to the path 'C:\Windows\InfusedApps' is denied.
    At line:12 char:38
    +         $results = foreach ($item in Get-ChildItem -Path $Path ) {
    +                                      ~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : PermissionDenied: (C:\Windows\InfusedApps:String) [Get-ChildItem], UnauthorizedAccessException
        + FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
    

May 4, 2017 at 4:06 pm

Rob, thangs again for your quickly help !!. Really apreciatted.

I was using your workarround, and change the output to a customobject.

function Get-AclLocallyLocal_test2 {

 [CmdLetBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [String]$drive,
        [Parameter(Mandatory=$false)]
        [int]$depth 

    )

    BEGIN{
        
    }#BEGIN
            

    PROCESS {
        
        foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue){
                
                
                #if is directory
                if ($item.psiscontainer){
                       
                        #if amount of directories are equal or lower than specified depth
                  if(($item.fullname -split '\\').count -le $depth){
                        
                        
                                                                    

                        $aclLocalyHashtable=@{
                            Directory=$item.fullname
                            
                        }
                        $aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
                        $aclLocallyObject          
                        Get-AclLocallyLocal_test2 $item.fullname -depth $depth

                        
                    }    
                    elseif(!($item.fullname -split '\\').count -le $depth){
                        continue
                    }
                }#if psiscontainer    
                else{
                    continue
                }
            
            }#foreach   


    }#PROCESS   

    END{

                   
        #$aclLocallyObject
        
    }#END
    
}  

But i cant manage the errors within the script.
I dont know if it is posible to return a customobject with 2 keys
Directory (all directories)
Errors ( a nested customobject in this key, with the errors details)
I write it here to graphic what i am trying to say (sorry if i am doing wrong.I´m trying to do my best and learn)

function Get-AclLocallyLocal_test3 {

 [CmdLetBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [String]$drive,
        [Parameter(Mandatory=$false)]
        [int]$depth 

    )

    BEGIN{
        
    }#BEGIN
            

    PROCESS {
        
        foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue -ErrorVariable +err){
                
                
                #if is directory
                if ($item.psiscontainer){
                       
                        #if amount of directories are equal or lower than specified depth
                  if(($item.fullname -split '\\').count -le $depth){
                        
                        if ($err){
                            $errors=@{
                            Target=$err.Get_categoryinfo().TargetName
                            Category=$err.Get_categoryinfo().Category
                            Reason=$err.Get_categoryinfo().Reason
                            }
                        }
                                                

                        $aclLocalyHashtable=@{
                            Directory=$item.fullname
                            Error=$errors
                        }
                        $aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
                        $aclLocallyObject          
                        $err.clear()
                        Get-AclLocallyLocal_test2 $item.fullname -depth $depth

                        
                    }    
                    elseif(!($item.fullname -split '\\').count -le $depth){
                        continue
                    }
                }#if psiscontainer    
                else{
                    continue
                }
            
            }#foreach   


    }#PROCESS   

    END{
        
       
        
    }#END
    
}  

June 12, 2017 at 6:01 pm

Sorry to be such a bore, but i´m trying hard to resolve it.

I know im near, but i can´t get a hashtable of the directories and the directories with no permissions.
I only get the hashtable with directories, and in the errors column i receive nothing.
Here is my code

## Get-AclLocallyLocal
function Get-AclLocallyLocal_test2 {

 [CmdLetBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [String]$drive,
        [Parameter(Mandatory=$false)]
        [int]$depth 

    )

    BEGIN{
        
    }#BEGIN
            

    PROCESS {
        
        foreach($item in get-childitem -path $drive -ErrorAction SilentlyContinue -ErrorVariable +err){
                
                
                #if is directory
                if ($item.psiscontainer){
                       
                  #if amount of directories are equal or lower than specified depth
                  if(($item.fullname -split '\\').count -le $depth){
                        
                        
                                                                    

                        $aclLocalyHashtable=@{
                            Directory=$item.fullname
                            Error=""
                        }
                        $aclLocallyObject = new-object -TypeName psobject -Property $aclLocalyHashtable
                        $aclLocallyObject   
                        Get-AclLocallyLocal_test2 $item.fullname -depth $depth

                        
                    }    
                    elseif(!($item.fullname -split '\\').count -le $depth){
                        continue
                    }
                }#if psiscontainer    
                else{
                    continue
                }
            
            }#foreach   


    }#PROCESS   

    END{
               
        #$aclLocallyObject
        
    }#END
    
}  

Get-AclLocallyLocal_test2 -drive "C:\temp\CarpetaPrueba" -depth 5

And here is the output:

Error Directory
----- ---------
      C:\temp\CarpetaPrueba\Carpeta1
      C:\temp\CarpetaPrueba\Carpeta2
      C:\temp\CarpetaPrueba\Carpeta3

Again, sorry to bother you, but i really need this.
Thanks a lot.