Pages

Friday, May 3, 2013

PowerShell Robocopy Wrapper - RoboWrapper

Last week I was asked to help review our CTO's backup architecture for his home PCs and Windows Home Server. Apparently, he backs up his PCs to his WHS and replicate using on of the cloud based solution but when he tried to restore, he found out that his backups were corrupt because they were too large. I recommended using xcopy (older version of robocopy) which will copy full and differentials between sources and destination. Rather than write a simple batch file and run it on every machine, I told myself to write a PowerShell wrapper for robocopy and automate the process of backing up machine on a network with logging and alerting capabilities via email for any errors. Here is version 1 of the script which runs synchronously. I will post version 2 that runs asynchronously next week.


$destinationServer = "BKUPMACHINE"
$destinationHardDiskLetter = "C:"
$shareUsername = "Everyone"
$sourceMachines = "MACHINE1","MACHINE2","MACHINE3"


$mailserver = "smtp.gmail.com"
$from = "[email protected]"
$to = "[email protected]"

$defaultDirExclusionSet = "'System Volume Information' *RECYCLE.BIN Windows* 'Program Files*' Recovery ProgramData"
$defaultFileExclusionSet = "pagefile.sys hiberfil.sys *.dat.* *.TMP"
$roboLog = "/LOG+:$LOGFILE`-Robocopy`-$TIMESTAMP.log"
$roboMsgTypes=@{
"16"="Errror"
"8"="Error"
"4"="Warning"
"2"="Information"
"1"="Information"
"0"="Information"
}
$roboMessages=@{
"16"="Serious error. robocopy did not copy any files.`n
Examine the output log: $LOGFILE`-Robocopy`-$TIMESTAMP.log"
"8"="Some files or directories could not be copied (copy errors occurred and the retry limit was exceeded).`n
Check these errors further: $LOGFILE`-Robocopy`-$TIMESTAMP.log"
"4"="Some Mismatched files or directories were detected.`n
Examine the output log: $LOGFILE`-Robocopy`-$TIMESTAMP.log.`
Housekeeping is probably necessary."
"2"="Some Extra files or directories were detected and removed in $DESTINATION.`n
Check the output log for details: $LOGFILE`-Robocopy`-$TIMESTAMP.log"
"1"="New files from $SOURCE copied to $DESTINATION.`n
Check the output log for details: $LOGFILE`-Robocopy`-$TIMESTAMP.log"
"0"="$SOURCE and $DESTINATION in sync. No files copied.`n
Check the output log for details: $LOGFILE`-Robocopy`-$TIMESTAMP.log"
}
$mail = New-Object Net.Mail.SmtpClient($mailserver, 587)
$mail.EnableSsl = $true
$mail.Credentials = New-Object System.Net.NetworkCredential("[email protected]", "Password");

$sourceMachines | % { 
    $defaultShares = @()
$sourceHarddisks = Get-WmiObject Win32_LogicalDisk -ComputerName $_ | where {$_.DriveType -eq 3 } 
$sourceShares =  Get-WmiObject Win32_Share -ComputerName $_
foreach($hd in $sourceHarddisks ) {
 foreach($sh in $sourceShares) {
   if ($sh.Path -eq ($hd.DeviceID + "\")) {
 $defaultShares += $sh.Name
}
 }
}

    Invoke-Command -ComputerName $destinationServer -ArgumentList $destinationHardDiskLetter,$_,$shareUsername -ScriptBlock { 
 # Windows ACL Helper functions 
 function Create-WMITrustee([string]$NTAccount){ 
   $user = New-Object System.Security.Principal.NTAccount($NTAccount) 
$strSID = $user.Translate([System.Security.Principal.SecurityIdentifier]) 
$sid = New-Object security.principal.securityidentifier($strSID)  
[byte[]]$ba = ,0 * $sid.BinaryLength      
[void]$sid.GetBinaryForm($ba,0)  
$Trustee = ([WMIClass] "Win32_Trustee").CreateInstance()  
$Trustee.SID = $ba 
$Trustee 
 } 
 function Create-WMIAce{ 
   param( 
          [string]$account, 
          [System.Security.AccessControl.FileSystemRights]$rights 

$trustee = Create-WMITrustee $account 
$ace = ([WMIClass] "Win32_ace").CreateInstance()  
$ace.AccessMask = $rights  
$ace.AceFlags = 0 # set inheritances and propagation flags 
$ace.AceType = 0 # set SystemAudit  
$ace.Trustee = $trustee  
$ace 
 } 
 $rights = [System.Security.AccessControl.FileSystemRights]"FullControl"
 $sd = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
 $ace=Create-WMIAce $args[2] $rights 
 $sd.DACL += @($ace.psobject.baseobject)
 $sd.ControlFlags="0x4"

      $bkupdir = $args[0] + "\Backup\" + $args[1]
      if(-not(Test-Path -Path $bkupdir)){ New-Item -Path $bkupdir -type directory -Force}
 $wmishares=[WMICLASS]"Win32_Share"
 $share = "name='" + $args[1] + "'"
 if(-not(Get-WmiObject Win32_Share -filter $share )) {$wmishares.Create($bkupdir,$args[1],0,5000,"Backup Share",$null,$sd) } 
    }

    #bkup every hard disk by default
    foreach ($sh in $defaultShares ) {
      $roboSource = "\\" + $_ + "\" + $sh
 $roboDestin = "\\" + $destinationServer + "\" + $_ + "\" + $sh.TrimEnd("$")
 robocopy $roboSource $roboDestin /E /ZB /COPYALL /MON:10 /MOT:30 /DCOPY:T /MT:20 $roboLog /XF $defaultFileExclusionSet /XD $defaultDirExclusionSet /XJ /R:3 /W:300 /V /FP /ETA /TEE /SAVE:DailyCopyJob
 $exitCode = $lastExitCode
 if ($roboMessages."$exitCode" -gt $null) {
   Write-EventLog -LogName Application -Source "RoboWrapper" -EventID $exitCode -EntryType $roboMsgTypes."$exitCode" -Message $roboMessages."$exitCode"
$mail.Send($from, $to, "RoboError: " + $roboMsgTypes."$exitCode", $roboMessages."$exitCode")
 }
 else {
   Write-EventLog -LogName Application -Source "RoboWrapper" -EventID $exitCode -EntryType Warning -Message "Unknown ExitCode. EventID equals ExitCode"
$mail.Send($from, $to, "Unknown RoboError", "Unknown ExitCode. EventID equals ExitCode")
 }
    }


No comments:

Post a Comment