powershellpowershell-2.0copy-item

How to sync 2 shares on LastWriteTime without deletion


I need to synchronize 2 FTP repertories behind 2 different servers which are not member of a cluster IIS. A virtual IP is used to do have a kind of failover.

enter image description here

I tried with robocopy or xcopy needs to have a reference directory. In my case, when the task is launched by a third server, I just need to copy the last written files and directory which are not in the other one.

$Global:pathFTP1 = $SRV1unc + "\c$\inetpub\PlcmSpIp"
$Global:pathFTP2 = $SRV2unc + "\c$\inetpub\PlcmSpIp"
$Global:Files1 = @()
$Global:Files2 = @()
$Global:tSRV = @(
    New-Object PSObject -Property @{Serveur = $SRV1; RacineFTP = $pathFTP1; Fichiers = $Files1}
    New-Object PSObject -Property @{Serveur = $SRV2; RacineFTP = $pathFTP2; Fichiers = $Files2}
)

Here is a part of the code:

$Global:Component = "Tree"
$Comparison = Compare-Object -ReferenceObject $tSRV.Item(0).Fichiers -DifferenceObject $tSRV.Item(1).Fichiers -PassThru -IncludeEqual
$ComparisonFolders = $Comparison | Where-Object{$_.PSIsContainer -eq $true}
$FoldersPresentOnEach = $ComparisonFolders | Where-Object {($_.SideIndicator -eq "==") -and (Test-Path($_.FullName))}
$FoldersNotPresentInSRV1 = $ComparisonFolders | Where-Object {($_.SideIndicator -eq "=>") -and (Test-Path($_.FullName))}
$FoldersNotPresentInSRV2 = $ComparisonFolders | Where-Object {($_.SideIndicator -eq "<=") -and (Test-Path($_.FullName))}
##Folders not present on $SRV1
if ($FoldersNotPresentInSRV1.Count -ne 0) {
    foreach ($Folder in $FoldersNotPresentInSRV1) {
        if (-not (Test-Path (($Folder.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')))) {
            New-Item -ItemType Directory -Path (($Folder.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')) -Force -ErrorAction SilentlyContinue
       }
    }
}
##Folders not present on $SRV2
if ($FoldersNotPresentInSRV2.Count -ne 0) {
    foreach ($Folder in $FoldersNotPresentInSRV2) {
        if (-not (Test-Path (($Folder.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')))) {
            New-Item  -ItemType Directory -Path (($Folder.Fullname.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\')) -Force -ErrorAction SilentlyContinue
        }
    }
}

##Files comparison
$ComparisonFiles = $Comparison | Where-Object {$_.PSIsContainer -eq $false}
$FilesPresentOnEach = $ComparisonFiles | Where-Object {($_.SideIndicator -eq "==")  -and ($_.LastWriteTime -eq (($_.Directory.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')).LastWriteTime)}
$FilesNotPresentInSRV1 = $ComparisonFiles | Where-Object {($_.SideIndicator -eq "=>") -and ((Test-Path($_.Directory.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')))}
$FilesNotPresentInSRV2 = $ComparisonFiles | Where-Object {($_.SideIndicator -eq "<=") -and ((Test-Path($_.Directory.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')))}
## Files not present on $SRV1
if ($FilesNotPresentInSRV1.Count -ne 0) {
    foreach ($File in $FilesNotPresentInSRV1) {
        Copy-Item -Path $File.FullName -Destination (($File.Directory.Fullname.Replace($SRV2unc,$SRV1unc)).Replace('\\\','\\')) -Force -ErrorAction SilentlyContinue
    }
}
##Files not present on $SRV2
if ($FilesNotPresentInSRV2.Count -ne 0) {
    foreach ($File in $FilesNotPresentInSRV2) {
        Copy-Item -Path $File.FullName -Destination (($File.Directory.Fullname.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\')) -Force -ErrorAction SilentlyContinue
    }
}
$Global:Component = "Files already present"
if ($FilesPresentOnEach.Count -ne 0) {
    foreach ($File in $FilesPresentOnEach | Where {$_.LastWriteTime -gt (Get-Item(($_.FullName.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\'))).LastWriteTime}) {
        Copy-Item -Path $File.FullName -Destination (($File.Fullname.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\')) -Force -ErrorAction SilentlyContinue       
    }
    foreach ($File in $FilesPresentOnEach | Where {$_.LastWriteTime -lt (Get-Item(($_.FullName.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\'))).LastWriteTime}) {  
        Copy-Item -Path (($File.Fullname.Replace($SRV1unc,$SRV2unc)).Replace('\\\','\\')) -Destination $File.FullName -Force -ErrorAction SilentlyContinue
    }
}

I expect that the files where the attribute LastWriteTime the more recent is copied on the other one it already exist and older or if doesn't exist. Actually some folders are not copied and it doesn't work as expected.

Instead of rewrite the path with some replace, how can I do it properly? Maybe I take the problem in the wrong way and do it with another tool?


Solution

  • Why make it simple when you can make it complicated - Shadoks

    Thanks to Ansgar Wiechers, using Robocopy without mirror option is the simpliest way to achieve the task.

    robocopy $pathFTP2 $pathFTP1 /E /ZB /X /COPYALL /XO /FFT /LOG:$ficROBOCOPY
    robocopy $pathFTP1 $pathFTP2 /E /ZB /X /COPYALL /XO /FFT /LOG+:$ficROBOCOPY
    

    I have to deal now with a gMSA account to launch the task and this kind of error :

    System.Management.Automation.ParameterBindingArgumentTransformationException: Cannot process argument transformation on parameter 'Value'. Cannot convert value to type System.String. ---> System.Management.Automation.ArgumentTransformationMetadataException: Cannot convert value to type System.String. ---> System.Management.Automation.PSInvalidCastException: Cannot convert value to type System.String. at System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object inputData, Boolean bindingParameters, Boolean bindingScriptCmdlet) --- End of inner exception stack trace ---
    at System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object inputData, Boolean bindingParameters, Boolean bindingScriptCmdlet) at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags) --- End of inner exception stack trace --- at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception) at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame) at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)

    But it is another problem...