Some more additions to the script. In this part I add some additional configuration options, parameter validation and some error-checking.
Update : I’ve revisited this script a few times with new additions and modifications.The latest full version of the script is here. That post also includes links covering the other revisions to the script.
I’ll go over the changes below;
[CmdletBinding(DefaultParameterSetName="XMLFile")] param ( [parameter( ParameterSetName="XMLFile")] [ValidateScript({Test-Path $_ -PathType leaf})] [string]$ConfigurationFile=$PSScriptRoot+"\Sync-FolderConfiguration.xml", [parameter( Mandatory=$True, ValueFromPipelineByPropertyName=$True, ParameterSetName="FolderPair")] [string]$SourceFolder, [parameter( Mandatory=$True, ValueFromPipelineByPropertyName=$True, ParameterSetName="FolderPair")] [string]$TargetFolder, [parameter( ParameterSetName="FolderPair")] [string[]]$Exceptions )
In the previous part I changed the script so it would take an XML configuration file as input. That’s been pretty useful but sometimes I just want to run the script against a pair of folders. I added back SourceFolder and TargetFolder as parameters and modified the script so it will take an array of exceptions as well.
That’s a fair number of parameters and some combinations are redundant. To try and make it more user friendly I’ve added two parameter sets. These tell PowerShell which parameters can be used together. In this case we want an XML file to be passed OR a source folder, target folder and optional exception list. This prevents a user passing an XML file and source folder, for example.
As I’ve specified a default XML file to use if the user doesn’t pass one(Sync-FolderConfiguration.xml in the folder the script is run from) , I’ve also marked the XMLFile parameter set as default in the first CmdletBinding setting. If there’s ever any ambiguity in which parameter set to use it’ll default to the config file.
function Check-Exceptions { param ( [parameter(Mandatory=$True)] [ValidateScript({Test-Path $_ -IsValid })] [string]$TestPath, [string[]]$PassedExceptions )
Here I’ve added validation to the $TestPath parameter. When it is passed a script is run (to check if the path format is valid) and if it returns $True the parameter is accepted.
function Sync-OneFolder { param ( [parameter(Mandatory=$True)] [ValidateScript({Test-Path $_ -PathType container})] [string]$SourceFolder, [parameter(Mandatory=$True)] [ValidateScript({Test-Path $_ -IsValid })] [string]$TargetFolder, [string[]]$PassedExceptions )
More validation; checking that SourceFolder is a path to a container (folder) and that TargetFolder is a correctly formatted path (I can’t check the folder exists as it may not; the script can create the TargetFolder if needed).
try { Write-Verbose "Copying missing file : $($TargetFolder+"\"+$MissingFile.Name)" Copy-Item ($SourceFolder+"\"+$MissingFile.Name) ($TargetFolder+"\"+$MissingFile.Name) } catch { Write-Error "Error copying missing file $($TargetFolder+"\"+$MissingFile.Name)" Write-Error $_ }
I’ve added Try/Catch sequences to parts of the script. If the code in the Try section generates an error that would stop the script it runs the code within the Catch section instead of stopping.
Write-Verbose "Running Sync-Folder Script" if ($PSBoundParameters.ContainsKey("SourceFolder")) { Write-Verbose "Syncing folder pair passed as parameters." Sync-OneFolder $SourceFolder $TargetFolder $Exceptions }else { Write-Verbose "Running with Configuration File : $ConfigurationFile" $Config=[xml](Get-Content $ConfigurationFile) foreach ($Pair in $Config.Configuration.SyncPair) { Write-verbose "Processing Pair" Sync-OneFolder $Pair.Source $Pair.Target $Pair.ExceptionList.Exception } }
Here the main body of the code has been changed to detect if a SourceFolder parameter has been passed. If it has I just sync the folders (I know there will be a corresponding $TargetFolder with it as it’s mandatory for the parameter set).
If there isn’t a SourceFolder parameter then I progress with using the XML file as before.