PowerShell Sync Framework - Sync to all subdirectories.

Using the script I found here:

http://stevenmurawski.com/powershell/2009/11/using-the-sync-framework-from-powershell

You provide a source and a destination to the script, and it will sync them.

I'd like to modify this script (or add to it), so that it will do this:

You provide a source and a destination to the script, and it will sync the files to each subdirectory of the destination path provided.

I believe I need to add something like:

foreach($_ in (Get-ChildItem $DestinationPath -directory | select-object FullName

But I'm not sure where in the script to add it, and how I should alter the rest of the $DestinationPath items in the script to use the new array.

Any advise?

Thanks!

(Original Script)

# Requires -Version 2
# Also depends on having the Microsoft Sync Framework 2.0 SDK or Runtime
# --SDK--
# http://www.microsoft.com/downloads/details.aspx?FamilyID=89adbb1e-53ff-41b5-ba17-8e43a2e66254&displaylang=en
# --Runtime--
# http://www.microsoft.com/downloads/details.aspx?FamilyId=109DB36E-CDD0-4514-9FB5-B77D9CEA37F6&displaylang=en
#
#            

[CmdletBinding(SupportsShouldProcess=$true)]
param (
    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [Alias('FullName', 'Path')]
    [string]$SourcePath
    , [Parameter(Position=2, Mandatory=$true)]
    [string]$DestinationPath
    , [Parameter(Position=3)]
    [string[]]$FileNameFilter
    , [Parameter(Position=4)]
    [string[]]$SubdirectoryNameFilter
)
<#
    .Synopsis
        Synchronizes to directory trees
    .Description
        Examines two directory structures (SourcePath and DestinationPath) and uses the Microsoft Sync Framework
        File System Provider to synchronize them.
    .Example
        An example of using the command
#>
begin
{
    [reflection.assembly]::LoadWithPartialName('Microsoft.Synchronization') | Out-Null
    [reflection.assembly]::LoadWithPartialName('Microsoft.Synchronization.Files') | Out-Null            

    function Get-FileSystemChange()
    {
        param ($path, $filter, $options)
        try
        {
            $provider = new-object Microsoft.Synchronization.Files.FileSyncProvider -ArgumentList $path, $filter, $options
            $provider.DetectChanges()
        }
        finally
        {
            if ($provider -ne $null)
            {
                $provider.Dispose()
            }
        }
    }            

    function Invoke-OneWayFileSync()
    {
        param ($SourcePath, $DestinationPath,
                    $Filter, $Options)
        $ApplyChangeJobs = @()
        $AppliedChangeJobs = @()
        try
        {
            # Scriptblocks to handle the events raised during synchronization
            $AppliedChangeAction = {
                $argument = $event.SourceEventArgs
                switch ($argument.ChangeType)
                {
                    { $argument.ChangeType -eq [Microsoft.Synchronization.Files.ChangeType]::Create } {[string[]]$global:FileSyncReport.Created += $argument.NewFilePath}
                    { $argument.ChangeType -eq [Microsoft.Synchronization.Files.ChangeType]::Delete } {[string[]]$global:FileSyncReport.Deleted += $argument.OldFilePath}
                    { $argument.ChangeType -eq [Microsoft.Synchronization.Files.ChangeType]::Update } {[string[]]$global:FileSyncReport.Updated += $argument.OldFilePath}
                    { $argument.ChangeType -eq [Microsoft.Synchronization.Files.ChangeType]::Rename } {[string[]]$global:FileSyncReport.Renamed += $argument.OldFilePath}
                }
            }            

            $SkippedChangeAction = {
                [string[]]$global:FileSyncReport.Skipped += $event.SourceEventArgs.CurrentFilePath            

                if ($event.SourceEventArgs.Exception -ne $null)
                {
                    Write-Error '[' + "$($event.SourceEventArgs.Exception.Message)" +']'
                }
            }            

            # Create source provider and register change events for it            

            $sourceProvider = New-Object Microsoft.Synchronization.Files.FileSyncProvider -ArgumentList $SourcePath, $filter, $options
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $SourceProvider -EventName AppliedChange -Action $AppliedChangeAction
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $SourceProvider -EventName SkippedChange -Action $SkippedChangeAction             

            $ApplyChangeJobs += $SourceApplyChangeJob            

            # Create destination provider and register change events for it
            $destinationProvider = New-Object Microsoft.Synchronization.Files.FileSyncProvider -ArgumentList $DestinationPath, $filter, $options
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $destinationProvider -EventName AppliedChange -Action $AppliedChangeAction
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $destinationProvider -EventName SkippedChange -Action $SkippedChangeAction            

            $ApplyChangeJobs += $DestApplyChangeJob            

            # Use scriptblocks for the SyncCallbacks for conflicting items.
            $ItemConflictAction =   {
                $event.SourceEventArgs.SetResolutionAction([Microsoft.Synchronization.ConflictResolutionAction]::SourceWins)
                [string[]]$global:FileSyncReport.Conflicted += $event.SourceEventArgs.DestinationChange.ItemId
            }
            $ItemConstraintAction = {
                $event.SourceEventArgs.SetResolutionAction([Microsoft.Synchronization.ConstraintConflictResolutionAction]::SourceWins)
                [string[]]$global:FileSyncReport.Constrained += $event.SourceEventArgs.DestinationChange.ItemId
            }            

            #Configure the events for conflicts or constraints for the source and destination providers
            $destinationCallbacks = $destinationProvider.DestinationCallbacks
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $destinationCallbacks -EventName ItemConflicting -Action $ItemConflictAction
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $destinationCallbacks -EventName ItemConstraint -Action $ItemConstraintAction             

            $sourceCallbacks = $SourceProvider.DestinationCallbacks
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $sourceCallbacks -EventName ItemConflicting -Action $ItemConflictAction
            $AppliedChangeJobs += Register-ObjectEvent -InputObject $sourceCallbacks -EventName ItemConstraint -Action $ItemConstraintAction             

            # Create the agent that will perform the file sync
            $agent = New-Object  Microsoft.Synchronization.SyncOrchestrator
            $agent.LocalProvider = $sourceProvider
            $agent.RemoteProvider = $destinationProvider            

            # Upload changes from the source to the destination.
            $agent.Direction = [Microsoft.Synchronization.SyncDirectionOrder]::Upload            

            Write-Host "Synchronizing changes from $($sourceProvider.RootDirectoryPath) to replica: $($destinationProvider.RootDirectoryPath)"
            $agent.Synchronize();
        }
        finally
        {
            # Release resources.
            if ($sourceProvider -ne $null) {$sourceProvider.Dispose()}
            if ($destinationProvider -ne $null) {$destinationProvider.Dispose()}
        }
    }            

    # Set options for the synchronization session. In this case, options specify
    # that the application will explicitly call FileSyncProvider.DetectChanges, and
    # that items should be moved to the Recycle Bin instead of being permanently deleted.            

    $options = [Microsoft.Synchronization.Files.FileSyncOptions]::ExplicitDetectChanges
    $options = $options -bor [Microsoft.Synchronization.Files.FileSyncOptions]::RecycleDeletedFiles
    $options = $options -bor [Microsoft.Synchronization.Files.FileSyncOptions]::RecyclePreviousFileOnUpdates
    $options = $options -bor [Microsoft.Synchronization.Files.FileSyncOptions]::RecycleConflictLoserFiles
}
process
{
    $filter = New-Object Microsoft.Synchronization.Files.FileSyncScopeFilter
    if ($FileNameFilter.count -gt 0)
    {
       $FileNameFilter | ForEach-Object { $filter.FileNameExcludes.Add($_) }
    }
    if ($SubdirectoryNameFilter.count -gt 0)
    {
       $SubdirectoryNameFilter | ForEach-Object { $filter.SubdirectoryExcludes.Add($_) }
    }            

    # Perform the detect changes operation on the two file locations
    Get-FileSystemChange $SourcePath $filter $options
    Get-FileSystemChange $DestinationPath $filter $options            

    # Reporting Object - using the global scope so that it can be updated by the event scriptblocks.
    $global:FileSyncReport = New-Object PSObject |
        Select-Object SourceStats, DestinationStats, Created, Deleted, Overwritten, Renamed, Skipped, Conflicted, Constrained            

    # We don't need to pass any filters here, since we are using the file detection that was previously completed.
    # this will only 
    $global:FileSyncReport.SourceStats = Invoke-OneWayFileSync -SourcePath $SourcePath -DestinationPath $DestinationPath -Filter $null -Options $options
    $global:FileSyncReport.DestinationStats = Invoke-OneWayFileSync -SourcePath $DestinationPath -DestinationPath $SourcePath -Filter $null -Options $options            

    # Write result to pipeline
    Write-Output $global:FileSyncReport
}
September 3rd, 2015 5:54pm

Advise: ask the author.

Can you please as a specific question?  We cannot rewrite scripts found on the Internet.  We will answer specific questions related to YOUR scripting issues. If you have a script written by someone else than you should really ask the author to fix it for you.

Free Windows Admin Tool Kit Click here and download it now
September 3rd, 2015 6:58pm

Advise: ask the author.

Can you please as a specific question?  We cannot rewrite scripts found on the Internet.  We will answer specific questions related to YOUR scripting issues. If you have a script written by someone else than you should really ask the author to fix it fo

September 4th, 2015 11:13am

And I should say I'm not sure if this is even worth while. Feel free to tell me if I'd be waiting my time. From my noob eye, it looks like I'm going to have to create a function to replace any CMD where $DestinationPath is called. For example, what I am planning to do is replace Parameter 2 ($DestinationPath) in the original script with "$DestinationRootPath" Then sub my "$_" variable for "$DestinationPath" What I'm not sure on, is where else functions will have to replace existing commands where $DestinationPath is called.
Free Windows Admin Tool Kit Click here and download it now
September 4th, 2015 11:40am

Try it.  You have to start to learn some time.
September 4th, 2015 12:31pm

Try it.  You have to start to learn
Free Windows Admin Tool Kit Click here and download it now
September 4th, 2015 12:35pm

Sorry but you haven't asked a question.  You are asking for a fix.  No one wants to pour over many lines of code and decode what you are trying to ask.

It seems simple to you only because you do not know enough to even edit the code.  Try implementing your fix and post back with a specific question.  By actually trying to write code you will learn.

September 4th, 2015 12:44pm

I went ahead an combined a few scripts to get the results I needed.

  1. #1 to monitor the source directory for changes, then run script 2.
#This script uses the .NET FileSystemWatcher class to monitor file events in folder(s). 
#The advantage of this method over using WMI eventing is that this can monitor sub-folders. 
#The -Action parameter can contain any valid Powershell commands. 
#Heavily modified by Joseph Garfield 
#Version 1.2

$folder = 'C:\Temp\Source' # Enter the root path you want to monitor. 
$filter = '*.*' # You can enter a wildcard filter here.

# In the following line, you can change 'IncludeSubdirectories to $true if required. 
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property @{IncludeSubdirectories = $true;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}

Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged1 -Action { 
$path = $Event.SourceEventArgs.FullPath 
$changeType = $Event.SourceEventArgs.ChangeType 
$timeStamp = $Event.TimeGenerated 
$datestamp = get-date -uformat "%Y-%m-%d@%H-%M-%S" 
$Computer = get-content env:computername 
$Body = " $path on $Computer was $changeType at $timeStamp" 
$PIECES=$path.split("\") 
$newfolder=$PIECES[-2] 
Out-File -FilePath c:\PowerShell\SyncChanges.log -Append -InputObject " $path on $Computer was $changeType at $timeStamp" 
Send-MailMessage -To "matthew.davis@commercebank.com" -From "SyncTriggered@cbsh.com" -Subject $Body -SmtpServer "kc-sntp.cbsh.com" -Body " " 
#$DestinationRootPath = "c:\Temp\Dest\"
foreach($_ in Get-ChildItem $DestinationRootPath -directory | select-object FullName)
{C:\PowerShell\Sync_OneWay.ps1 c:\Temp\Source $_.FullName
#Write-Host $SourcePath.Fullname
}
#Copy-Item $path "c:\scripts\versions\$newfolder" 
#Rename-Item "c:\Scripts\Versions\$newfolder\web.config" "web.config-$datestamp" 
#Remove-Item -path "c:\Scripts\Versions\$newfolder\web.config" -force}

# To stop the monitoring, run the following commands: 
# Unregister-Event FileChanged1

2.  #2 to search for sub-directories, then run the Sync script above (#3) against each sub dir.

$DestinationRootPath = "c:\Temp\Dest\"
foreach($SourcePath in Get-ChildItem $DestinationRootPath -directory | select-object FullName)
{C:\PowerShell\Sync_OneWay.ps1 c:\Temp\Source $SourcePath.FullName
#Write-Host $SourcePath.Fullname
}

-Matt

Free Windows Admin Tool Kit Click here and download it now
September 9th, 2015 2:38pm

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics