Posted On October 27, 2019

PowerShell: Synchronize Files Between Different Domains

kimconnect 0 comments
blog.KimConnect.com >> Codes >> PowerShell: Synchronize Files Between Different Domains
<# File_Copy_Script_UNC_to_Local_V0.1.ps1
Purpose: connect to an external domain to copy files onto a Intranet server.

Features:
- Synchronize data between different domains
- Output logs onto C:\scripts\logs (or whichever directory stores the script)

Requirements:
- Robocopy version XP027 5.1.10.1027 or higher (Windows 2008 Server and above)
- Valid Extranet CIFS (SMB) credentials

Usage:
- Edit the indicated section to provide appropriate credentials and storage paths
- Create a Windows scheduled task (taskschd.msc) with "powershell.exe -executionpolicy bypass C:\scripts\File_Copy_Script_UNC_to_Local_V0.1.ps1 -runtype $true"

Future Development:
- Save passwords as encrypted characters inside an XML file, instead of plain text.
- Automatically add script to scheduled tasks
- Send email notifications upon completion of each execution
#>

################################# Edit Only This Section ####################################################
$sourceDomainUser="EXTRANET.KIMCONNECT.COM\BALOO"
$plainTextPassword="PASWORDHERE"
$sourceDomainPassword = ConvertTo-SecureString $plainTextPassword -AsPlainText -Force
$sourceCredential = New-Object System.Management.Automation.PSCredential ($sourceDomainUser, $sourceDomainPassword)

$arr=@{}
$arr["from"] = @{}; $arr["to"] = @{}
$baseUNC="\\EXTRANET.KIMCONNECT.COM\CIFS";$baseLocalShare="D:\SHARES";
$arr["from"] = @("$baseUNC\SHARE1"); $arr["to"]=@("$baseLocalShare\SHARE1")
$arr["from"] += "$baseUNC\SHARE2"; $arr["to"]+="H:\SHARE2"

################################# Edit Only Above This Line #################################################
$dateStamp = Get-Date -Format "yyyy-MM-dd-hhmmss"
$scriptName=$MyInvocation.MyCommand.Path
$scriptPath=Split-Path -Path $scriptName
$logPath="$scriptPath\logs"
if(!(Test-Path $logPath)){New-Item -ItemType Directory -Force -Path $logPath}
$logFile="$logPath\robocopy-log-$dateStamp.txt"
$log="/LOG+:'$logFile'"
$switches="/MIR /R:0 /W:0 /XO /XJD /XJF /FFT /MT:32 /TBD /NP "
$GLOBAL:pathErrors="";
$stopWatch= [System.Diagnostics.Stopwatch]::StartNew()

# Paths validation
$sourcePathErrors="";
$destinationPathErrors="";
cls;
for ($i=0; $i -lt $arr.from.length; $i++){
$from=$arr.from[$i]
$to=$arr.to[$i]
if(!(test-path $from -ErrorAction SilentlyContinue)){
$sourcePathErrors+="$from`n"
}
if(!(test-path $to -ErrorAction SilentlyContinue)){
$destinationPathErrors+="$to`n"
}
}

if($sourcePathErrors -or $destinationPathErrors){
if($sourcePathErrors){$sourcePathErrors};
if($destinationPathErrors){$destinationPathErrors};
"Path errors:`n$sourcePathErrors`n$destinationPathErrors";
}else{
"All paths appear to be valid.";
}

# Write log header
$initInfo="=============================================Job Started: $dateStamp=============================================`r`n";
$initInfo+="Powershell version detected: $($PSVersionTable.PSVersion.Major)`.$($PSVersionTable.PSVersion.Minor)`r`n"
$initInfo+="Processing the following operations:`n";
if($arr.from -is [Array]){
$initInfo+=for ($i=0;$i -lt $arr.from.length; $i++){
"$($arr.from[$i]) => $($arr.to[$i])`n";
}
}else{$initInfo+="$($arr.from) => $($arr.to)`n";}
$initInfo;
Add-Content $logFile $initInfo;

# This function is useful for sanity checks prior to assigning persistent drive letters
function selectPsDrive{
# Select available drive letter at this moment in time
$driveLettersExclusion="[CDHKLMPZ]"
$availableDriveLetters=ls function:[A-Z]: -n|?{!(test-path $_)}|%{$_[0]}|?{!($_ -match $driveLettersExclusion)}
$GLOBAL:firstAvailableDriveLetter=$availableDriveLetters[0];
if ($firstAvailableDriveLetter -in (Get-PSdrive).Name){return $False;}else{return $True;}
}

function clearAllMappedDrives{
# The old-school method
Net Use * /delete /y

# The fancy new Powow Shill method
# Get a list of currently mapped drives
$mappedDrives = Get-WMIObject Win32_LogicalDisk | Where-Object { $_.DriveType -eq 4 }

if ($mappedDrives) {
$driveList = $mappedDrives.DeviceID
Foreach ($drive in $driveList) {
$driveLetter = $drive -replace ":"
Remove-SmbMapping -LocalPath $Drive -Force -UpdateProfile
If ( (Get-PSDrive -Name $driveLetter) 2>$Null ) {
Remove-PSDrive -Name $driveLetter -Force
}
}
}
}

function copyFilesFromDifferentDomain{
param(
[string]$from,
[string]$to
)

if (selectPsDrive){
<# Troubleshooting: How to remove persistent and hidden PSDrive
PS C:\Windows\system32> New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem -Root $from -Persist -Credential $sourceCredential
New-PSDrive : The local device name has a remembered connection to another network resource
At line:1 char:1
+ New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (A:PSDriveInfo) [New-PSDrive], Win32Exception
+ FullyQualifiedErrorId : CouldNotMapNetworkDrive,Microsoft.PowerShell.Commands.NewPSDriveCommand

PS C:\Windows\system32> net use
New connections will be remembered.
Status Local Remote Network

-------------------------------------------------------------------------------
OK \\FS01.KIMCONNECT.COM\IPC$ Microsoft Windows Network
The command completed successfully.

PS C:\Windows\system32> net use /delete \\FS01.KIMCONNECT.COM\IPC$
\\FS01.HULU.COM\IPC$ was deleted successfully.

The incident above has correlated to the "persistent" setting of the New-PSDrive command that was triggered prior to be followed by Remove-PSDrive
Persitent: New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem -Root $from -Persistent -Credential $sourceCredential

Failed experimental method
Create a UNC map to the outside-domain source
Test: New-PSDrive -Name "mapped_by_$($env:username)" -PSProvider FileSystem -Root "\\fs01.HULU.com\intellisense_home\00-Intellisense_Systems" -Credential $sourceCredential
$mapName=firstAvailableDriveLetter;
if ($mapName -in (Get-PSDrive).Name){Remove-PSDrive -Name $mapName -Scope Global|Out-Null}
New-PSDrive -Name $mapName -PSProvider FileSystem -Root $from -Credential $sourceCredential
#>
if ($firstAvailableDriveLetter -in (Get-PSDrive).Name){Remove-PSDrive -Name $firstAvailableDriveLetter -Scope Global}
try{
Invoke-Expression "net use '$firstAvailableDriveLetter`:' $from /user:$sourceDomainUser $plainTextPassword /y"
#New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem -Root $from -Credential $sourceCredential|Out-Null
}
catch{
clearAllMappedDrives;
Invoke-Expression "net use '$firstAvailableDriveLetter`:' $from /user:$sourceDomainUser $plainTextPassword /y"
#New-PSDrive -Name $firstAvailableDriveLetter -PSProvider FileSystem -Root $from -Credential $sourceCredential|Out-Null
}

# Validate the source path prior to copying
if(test-path $from -ErrorAction SilentlyContinue){
try{
write-host "robocopy '$from' '$to' $switches $log"
invoke-expression "robocopy '$from' '$to' $switches $log"
}
catch{
$errorMessage = $_.Exception.Message
$failedItem = $_.Exception.ItemName
write-Host "$errorMessage $failedItem"
continue;
}
}else{
write-Host "$from is not accessible."
$GLOBAL:pathErrors+="$from`n"
}

# Release UNC map for future reassignments
# Failed experimental method: Remove-PSDrive -Name $mapName -Scope Global -Force
# Remove-PSDrive -Name $firstAvailableDriveLetter -Scope Global -Force
Invoke-Expression "net use /delete '$firstAvailableDriveLetter`:' /y"
}
}

# Process the copying operations depending on whether we have a multi-dimensional array
if($arr.from -is [Array]){
# Create paths to Sources and trigger robocopy for each item in the array
$arrayLength=$arr.from.length
for ($i=0; $i -lt $arrayLength; $i++){
$from=$arr.from[$i]
$to=$arr.to[$i]
$processDisplay="=============Pass $($i+1) of $arrayLength`: $from => $to=======================================`r";
Write-Host $processDisplay;
Add-Content $logFile $processDisplay;
copyFilesFromDifferentDomain -from $from -to $to;
$passMarker="==============Pass $($i+1) of $arrayLength` Completed=======================================`r";
Add-Content $logFile $passMarker;
}
}else{
$processDisplay="=======================================Pass 1 of 1: $($arr.from) => $($arr.to)=======================================`r";
Write-Host $processDisplay;
Add-Content $logFile $processDisplay;
copyFilesFromDifferentDomain -from $arr.from -to $arr.to;
$passMarker="==============Pass $($i+1) of $arrayLength` Completed=======================================`r";
Add-Content $logFile $passMarker;
}

if ($pathErrors){Add-Content $logFile "Path errors:`n$pathErrors";}

# Stop the timer
$time=[math]::Round($($stopWatch.Elapsed.TotalHours),2);

# Write closure to log
Write-Host "Copying process completed in $time hours."
Add-Content $logFile $pathErrors
Add-Content $logFile "`n---------------------------------Copying process completed in $time hours---------------------------------"

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Post

PowerShell: Windows Automated Disk Cleanup

##################################################################################     <#      This script is created to automate the cleanup activity. Doing so will benefit to reduce the size of disk.     This script will perform the following   1. Clear windows temp and user temp folder   2. Empty recycle bin   3. Disk Cleanup   4. Clear CBS cabinet log files   5. Clear downloaded patches   6. Clear downloaded driver   7. Clean download folder      Note:  …

Quick Script to Test SharePoint Online Credentials

$principle = "[email protected]" $password = 'PASSW0RT' $sharePointUrl = "https://SOMESHAREPOINT.COM/SitePages/Home.aspx" $credential=New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $principle,$(ConvertTo-securestring $password…

PowerShell: Find Locking PID of a File

$filePath="C:\Program Files\Google\Chrome\Application\chrome.exe" function findPidOfFile($filepath){ try{ if (!(Get-Command handle.exe -ErrorAction SilentlyContinue)) { if (!(Get-Command choco.exe -ErrorAction…