Powershell Archiving Script, Part 5

Continuing on from the last part where I defined the Get-FolderSize function the next function to be defined is Get-FolderInformation. This gets the relevant information about all the folders that could be processed and outputs that information as objects.  The full script can be found here.

Get-FolderInformation

        function Get-FolderInformation
        {
            param
            (
                [Parameter(Mandatory=$True)]
                    [string]$SourcePath,
                [Parameter(Mandatory=$True)]
                    [string]$ArchivePath,
                [Parameter(ValueFromPipeline=$true)]
                    [string]$Path
            )
            Begin
            {
                Write-Debug "Entering Get-FolderInformation function"
                Write-Debug "Parameter : SourcePath = $SourcePath"
                Write-Debug "Parameter : ArchivePath = $ArchivePath"
            }
            Process
            {

                Write-Debug "Parameter : Path = $Path"
                if (($Path -eq $Null) -or ($Path -eq ""))
                {
                    #If there is no Path specified, get everything from the Source folder.
                    $WorkingPath=$SourcePath+"\*"
                }elseif (Test-Path -Path $Path)
                {
                    #If Path is a valid full path, then use that
                    $WorkingPath=$Path
                }else
                {
                    #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath.
                    $WorkingPath=$SourcePath+"\*"+$Path+"*"
                }
                Write-Debug "WorkingPath = $WorkingPath"
                $WorkingFolderList=Get-Item $WorkingPath | Where-Object -FilterScript {$_.PSISContainer}
                ForEach ($ChildItem in $WorkingFolderList)
                {
                    Write-Debug $ChildItem.FullName
                    #Build a hash table with all the properties we're interested in.
                    $Properties=@{
                        'Name'=$ChildItem.Name;
                        'FullSourcePath'=$ChildItem.FullName;
                        'FullArchivePath'=$ArchivePath+"\"+$ChildItem.Name
                        'Size'=0;
                        'Direction'="To Archive";
                        'Moving'=$False
                        'Message'="";
                        'Successful'=$False
                    }
                    #Test if it's a folder or already a symbolic link; use this to determine if we're going to or from the archive.
                    if ($ChildItem.Attributes -like "*ReparsePoint*")
                    {
                        $Properties.Size=Get-FolderSize -Path $Properties.FullArchivePath
                        #If it's a symbolic link see if it's target is in the archive folder;  if it's going somewhere else
                        #we ignore it.
                        if (!(Test-Path $Properties.FullArchivePath))
                        {
                            return
                        }
                        $Properties.Direction="From Archive"
                    }else
                    {
                        $Properties.Size=Get-FolderSize -Path $ChildItem.FullName
                    }
                    #Build and output a PSObject from the hash table
                    $Object=New-Object -TypeName PSOBject -Property $Properties
                    Write-Output $Object
                }
            }
        }

Quite a chunk of code!  But it’s (mostly) all important.

        function Get-FolderInformation
        {
            param
            (
                [Parameter(Mandatory=$True)]
                    [string]$SourcePath,
                [Parameter(Mandatory=$True)]
                    [string]$ArchivePath,
                [Parameter(ValueFromPipeline=$true)]
                    [string]$Path
            )
            Begin
            {
                Write-Debug "Entering Get-FolderInformation function"
                Write-Debug "Parameter : SourcePath = $SourcePath"
                Write-Debug "Parameter : ArchivePath = $ArchivePath"
            }
            Process
            {

The first part of the code just defines the function and the parameters we’re interested in. It needs to know the path where to look for folders to archive ($SourcePath), the path where folders are archived ($ArchivePath) and the relative or absolute path of the folder to be processed ($Path). We want to be able to stream this last parameter from the pipeline so we can process multiple paths.  Note that while $SourcePath and $ArchivePath are mandatory, $Path isn’t.  When $Path isn’t passed we want the script to get all the folders in $SourcePath.

As it will accept pipeline input we’ll need Begin and Process statements and we’ll put the first Write-Debug statements in the Begin block as these show the parameters that won’t change (as they’re not passed from the pipeline).

                Write-Debug "Parameter : Path = $Path"
                if (($Path -eq $Null) -or ($Path -eq ""))
                {
                    #If there is no Path specified, get everything from the Source folder.
                    $WorkingPath=$SourcePath+"\*"
                }elseif (Test-Path -Path $Path)
                {
                    #If Path is a valid full path, then use that
                    $WorkingPath=$Path
                }else
                {
                    #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath.
                    $WorkingPath=$SourcePath+"\*"+$Path+"*"
                }
                if (!(Test-Path -Path $WorkingPath -PathType Container))
                {
                    Throw ("Path ($WorkingPath) does not exist or is not a folder.")
                                        
                }
                Write-Debug "WorkingPath = $WorkingPath"

We want to build a path to the folder or folders we want to display as options to the user for archiving (or restoring). This needs to be built from $Path but $Path can be in a range of formats;

  • Empty (which we want to behave like a “*” wildcard and return everything).
  • A full, explicit path to a folder to process (like “c:\games\Quake”).
  • A partial or relative path (so return every folder in $SourcePath that contains “Sam”).

The code above builds $WorkingPath depending on the format of $Path.

                if (($Path -eq $Null) -or ($Path -eq ""))
                {
                    #If there is no Path specified, get everything from the Source folder.
                    $WorkingPath=$SourcePath+"\*"

Here is the case where $Path is empty or $Null. $WorkingPath in this case is the all folders under the $SourcePath.

                }elseif (Test-Path -Path $Path)
                {
                    #If Path is a valid full path, then use that
                    $WorkingPath=$Path

If it’s not empty or $Null we use Test-Path to see if it’s a valid path. If it is, we assume that’s what we want to process and we make $WorkingPath the same as $Path.

                }else
                {
                    #Otherwise assume it's for a wildcard search and build a search string from it and the SourcePath.
                    $WorkingPath=$SourcePath+"\*"+$Path+"*"
                }

The default position (if $Path isn’t empty/$Null and not a valid path itself) is to assume it’s a relative path or partial filename. We then build $WorkingPath from $SourcePath and $Path (surrounded by “*” wildcards). This will match all files or folders under $SourcePath with $Path at the end of the pathname.

                Write-Debug "WorkingPath = $WorkingPath"
                $WorkingFolderList=Get-Item $WorkingPath | Where-Object -FilterScript {$_.PSISContainer}
                ForEach ($ChildItem in $WorkingFolderList)
                {
                    Write-Debug $ChildItem.FullName
                    #Build a hash table with all the properties we're interested in.      
                    $Properties=@{
                        'Name'=$ChildItem.Name;
                        'FullSourcePath'=$ChildItem.FullName;
                        'FullArchivePath'=$ArchivePath+"\"+$ChildItem.Name
                        'Size'=0;
                        'Direction'="To Archive";
                        'Moving'=$False
                        'Message'="";
                        'Successful'=$False
                    }

First we build an array of all the folders in $WorkingPath. We’re not interested in files so we we filter to only return folders (Where-Object -FilterScript {$_.PSISContainer}).

For each item in the list of folders to be processed we want to create an object with all the details we’re interested in.  In the code above the $Properties hash-table is created and populated with the relevant information.  Later on we can use that to create an PS-Object.

Most of the properties on the hash-table are self-explanatory but a couple are in place for later use;

Direction.  Used to specify if the folder is being archived or returned from archive.

Moving.  If the user wants to process this folder.  Defaults to $False so the user has to explicitly ask for the folder to be processed.

Message.  A descriptive error message if it’s required.

Successful.  Whether the action on the folder succeeded or not.

 

                    #Test if it's a folder or already a symbolic link; use this to determine if we're going to or from the archive.
                    if ($ChildItem.Attributes -like "*ReparsePoint*")
                    {
                        $Properties.Size=Get-FolderSize -Path $Properties.FullArchivePath
                        #If it's a symbolic link see if it's target is in the archive folder;  if it's going somewhere else
                        #we ignore it.
                        if (!(Test-Path $Properties.FullArchivePath))
                        {
                            return
                        }
                        $Properties.Direction="From Archive"
                    }else
                    {
                        $Properties.Size=Get-FolderSize -Path $ChildItem.FullName
                    }
                    #Build and output a PSObject from the hash table
                    $Object=New-Object -TypeName PSOBject -Property $Properties
                    Write-Output $Object
                }
            }
        }

For each folder in the source path we need to check if it’s a folder to be archived or a symbolic link to a folder to return from the archive. We do that with the “if ($ChildItem.Attributes -like “*ReparsePoint*”)” line.

If the item we’re processing is a symbolic link we then make sure it’s linking to a folder in our archive path. If it’s linked somewhere else we’ll just return out of the function as it’s non-standard.  Additionally we now know the direction for the process is “From Archive” so we can set that property correctly.

The Size property is set here as well; either from the size of the folder in the source path or the size of the folder in the archive path (depending on whether the item we’re processing is a symbolic link).

Finally we convert the $Properties hash-table into a PSOBject and output it.

In the next part we’ll look at the Move-ArchiveObject function, where the actual work is done.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: