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.