Replica con Robocopy

17 Novembre 2022 0 Di

Avevo necessità di avere una replica, di tutti i files presenti in una sharing di un nas con alcuni Tb di files e zip (che erano il risultato di un backup) su un pc con windows 2012 r2 server. Il server windows si trovava fisicamente in un altro edificio, poco lontano dal principale, e serviva come backup per disaster recovery. Nel caso in cui, il primo edificio, dove era fisicamente posizionato il nas, fosse crollato, incediato, o anche un semplice furto, ci sarebbe comunque stato modo di avere una ulteriore copia di sicurezza. La replica non doveva però essere un semplice mirror. Doveva fare una copia in sovrascrittura dei files più recenti, e conservare eventuali files presenti nella replica, ma che non fossero piu presenti nel nas. In questo modo se fosse stato eliminato inavvertitamente un file dal nas, un amministratore avrebbe potuto recuperarlo dalla replica.
Certamente ci sono tools sia free che commerciali, adatti allo scopo, ma senza dover scomodare il portafoglio, con uno script powershell schedulato, si è potuto risolvere la cosa in maniera più che egregia.

Lo script, durante le fasi di replica, scrive un file di testo come log, posizionato in C:\TEMP, e alla fine del processo, lo archivia in un file compresso con 7zip, che quindi deve essere precedentemente installato, e poi invia il tutto per email, che può essere una persona preposta al controllo.
Certamente per comprimere il log, si sarebbe potuto usare le funzione powershell, ma ho preferito usare 7zip, perchè permettono una certa libertà anche nel formato di compressione.

## Dati backup del Nas in edificio 1
$ServerRemoto="\\MYNAS\BackupFiles"
$UserName="[UTENTEBACKUP]"
$Password="[MIAPASSWORD]"
$DiscoRemoto="O:"

## Directory da replicare sullo storage locale di questo server
## che si trova nell'edificio 2
$OrigineDaReplicare=$DiscoRemoto+"\"
$DestinazioneBackup="F:\BackupFiles\"

# Variabili per invio email di log finale
$TmpLogPrefix="C:\TEMP\Log_ReplicaArchivi"
$TmpLogSuffix="txt"
$TmpLogDate=Get-Date -format "yyyyMMdd_HHmm"
$TmpLogFile=$TmpLogPrefix+"_"+$TmpLogDate+"."+$TmpLogSuffix
$Now=Get-Date -format "dd-MM-yyyy HH:mm"
$MailTo="[email protected]"
$MailFrom="[email protected]"
$MailSubj="Replica files da $ServerRemoto"
$MailBody="Replica come da oggetto. Per verifica, prego ispeziona il log file allegato."
$SmtpSrv="mail.miodomonio.it"

### Funzioni varie prima del main
function CreaZip([String] $zipFile, [String] $fileDaComprimere) {
	$path7zip = "$($Env:ProgramFiles)\7-Zip\7z.exe"
	[Array]$parametri = "a", "-bd", "-bb0", "-tzip", "$zipFile", "$fileDaComprimere"
	& $path7zip $parametri
}

# Inizio. Cancello i vecchi log files, e inizio il nuovo
DEL "$TmpLogPrefix*.*"
ECHO "Inizio la replica il: $Now" > "$TmpLogFile"

# Controllo se il disco non sia gia montato, se si, lo smonto, e tento il mount di quello corretto.
IF (!(Test-Path "$DiscoRemoto")) {
ECHO "Monto server remoto $ServerRemoto" 2>&1 >> "$TmpLogFile"
NET USE "$DiscoRemoto" "$ServerRemoto" $Password /USER:$UserName /PERSISTENT:NO 2>&1 >> "$TmpLogFile"
ECHO "Ok. Montato...." 2>&1 >> "$TmpLogFile"
Start-Sleep -s 5
} ELSE {
ECHO "Disco Locale di Mount Point, gia in uso. Tento l'unmount..." 2>&1 >> "$TmpLogFile"
NET USE "$DiscoRemoto" /d 2>&1 >> "$TmpLogFile"
Start-Sleep -s 5
ECHO "OK, ora tento il nmount di quello previsto..." 2>&1 >> "$TmpLogFile"
NET USE "$DiscoRemoto" "$ServerRemoto" $Password /USER:$UserName /PERSISTENT:NO 2>&1 >> "$TmpLogFile"
Start-Sleep -s 5
}

# Se il disco è montato procedo al backup
IF (Test-Path "$DiscoRemoto") {
ECHO "Ok $ServerRemoto e montato. Procedo alla replica." 2>&1 >> "$TmpLogFile"
ROBOCOPY "$OrigineDaReplicare" "$DestinazioneBackup" /E /ZB /R:10 /W:5 /NDL /NP /NC /NS 2>&1 >> "$TmpLogFile"
$Now=Get-Date -format "dd-MM-yyyy HH:mm"
ECHO "--------------------------------------------------" 2>&1 >> "$TmpLogFile"
ECHO "Ok. Replica terminata il $Now" 2>&1 >> "$TmpLogFile"
Start-Sleep -s 5
} ELSE {
ECHO "Server Remoto non montato o rilevato disco di mount point." 2>&1 >> "$TmpLogFile"
ECHO "Replica ROBOCOPY non effettuato. Prego controllare motivo." 2>&1 >> "$TmpLogFile"
}

# quando è finito, smonto il disco
ECHO "Processo terminato. Smonto $ServerRemoto" 2>&1 >> "$TmpLogFile"
NET USE "$DiscoRemoto" /d 2>&1 >> "$TmpLogFile"
ECHO "Bye, bye" 2>&1 >> "$TmpLogFile"

# Comprimo File Log, perche' potenzialmente grande
$TmpLogZipFile=$TmpLogPrefix+"_"+$TmpLogDate+".zip"
CreaZip "$TmpLogZipFile" "$TmpLogFile"

#Invio Email con log allegato, ed esco.
$SmtpObj=New-Object Net.Mail.SmtpClient($SmtpSrv, 25)
$Attachment=New-Object Net.Mail.Attachment("$TmpLogZipFile", 'application/zip')
$MailMsg=New-Object Net.Mail.mailmessage
$MailMsg.From = "$MailFrom"
$MailMsg.To.Add($MailTo)
$MailMsg.Subject="$MailSubj"
$MailMsg.Body="$MailBody"
$MailMsg.Attachments.Add($Attachment)
$SmtpObj.Send($MailMsg)
Remove-Variable MailMsg
Remove-Variable Attachment
Remove-Variable SmtpObj
[System.GC]::Collect()
EXIT

Il codice mi sembra abbastanza chiaro. Ho cercato di dare nomi variabili coerenti, e commentare un minimo.
La parte iniziale definisce delle variabili dove settare il path ed i nomi dei dischi locali in cui verrà montato lo sharing e riversata la replica, seguito dalle variabili per la costruzione del file di log ed i parametri per le email a cui inviarlo a replica terminata.
Poi si continua con un controllo preventivo per verificare che il volume remoto non sia già montato, e nel caso tenta l’unmount prima. Questo perchè a volte è capitato che fosse già montato un altro volume con la stessa denominazione locale, e quindi in questo modo mi sono assicurato che sia effettivamente l’origine voluta, quella da sottoporre a replica.

Arriva poi la parte principale con il Robocopy, con i flag /E /ZB /R:10 /W:5 /NDL /NP /NC /NS
Una descrizione oltre che con l’help, lo si può trovare anche sul sito learn.microsoft.com.
In dettaglio si occupano di:

/Ecopia e discende nelle directory, anche quelle vuote;
/ZBcopia con il supporto al resume. Se l’accesso al lock file, viene negato, usa la modalità backup;
/R:10in caso di impossibilità di lettura di un file dall’origine, ritenta fino a 10 volte prima di andare in errore;
/W:5in caso di impossibilità di scrittura di un file sulla destinazione, ritenta fino a 5 volte prima di generare un errore;
/NDLnon scrivere il nome delle directory nel file log, quando discende in una di esse;
/NPnon visualizza una barra di progress;
/NCnon visualizza e quindi non scrive nel log, la classe del tipo di file;
/NSnon visualizza e non scrive nel log, la dimensione del file.

Lo script assumendo che sia stato salvato in “C:\scripts” e nominato “ReplicaArchivi.ps1” va poi posto nello schedulatore di windows con una Action -> Start Program -> Powershell.exe con l’arguments a -ExecutionPolicy Bypass C:\scripts\ReplicaArchivi.ps1

Se trovate lo script utile, o avete qualche osservazione, lasciatemi pure un commento.
Grazie.