Automated Macrium Reflect Backups to ISCSI Storage

This HOWTO will allow you to accomplish the following tasks using Macrium Reflect:

– Detect if a external NAS device is powered on and if not, power it on for the duration of the backup and power it down again
– Wait for the web management interface of the NAS device to come online before starting the backup
– Use Powershell to connect to the ISCSI target by verifying several characteristics of the NAS and assign it to a known drive letter
– Start a Reflect Backup and write to this data store

Click below read more link to check out the full source code!

<#
NAS_REFLECTBACKUP.PS1 - OVERVIEW TEXT
SCRIPT TO POWER ON AND POWER OFF VIA A TOGGLE AN ISCSI (FREENAS) NAS.
IT ALSO PROPERLY CONNECTS AND DISCONNECTS ISCSI SESSIONS
VERSION 1.0
#>

# REQUIRED TO SHOW THE .NET MESSAGEBOXES FURTHER DOWN #
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)

# VARIABLE TO QUERY THE CURRENT TIME #
$global:CurrentTime= Set-PSBreakpoint -Variable currenttime -Mode Read -Action { $global:currenttime= Get-Date }

# THESE PARAMETERS NEED TO BE CHANGED TO MATCH YOUR ENVIRONMENT #
$iscsiDriveLetter = "O:\"
$DestinationBackupFolder = "" # INSERT FOLDER HERE IN THE FORMAT "Folder\Subfolder"
$iscsiVolumeLabel = "" # ENTER THE VOLUME LABEL OF THE DRIVE
$iscsiIQN = "" # ENTER THE IQN OF THE ISCSI TARGET
$iscsiIP = "" # ENTER THE IP OF THE ISCSI TARGET
$iscsiMAC = "" # ENTER THE MAC OF THE ISCSI TARGET
$iscsiWebPage = "" # ENTER THE IP OF THE MANAGEMENT INTERFACE FOR THE ISCSI DEVICE
$BatchFileToStartReflect = "" #ENTER THE BATCH FILE THAT STARTS THE REFELCT BACKUP
$ReflectBackupConfig = " # ENTER THE PATH TO THE REFLECT XML BACKUP CONFIGURATION 
$nasUsername = "" # ENTER THE USER NAME USED TO LOGIN TO THE NAS
$nasPassword = "" # ENTER THE PASSWORD FOR THIS ACCOUNT
$PlinkPath = "" # ENTER THE PATH TO THE COMMAND LINE VERSION OF PUTTY (PLINK.EXE)
$WOLPath = "" # ENTER THE PATH TO THE WAKE ON LAN TOOL (WOLCMD.EXE)
$WebsiteTimeout = 10 # Number of attempts to make #

Function Write-LogMessage($msg)
{
Write-Host "[MSG] [$CurrentTime] $msg"
}

function Pause-Script
{
  # The ReadKey functionality is only supported at the console (not is the ISE)
  if (((Get-Host).Name) -eq 'ConsoleHost')
  {
      Write-Host -NoNewLine "Press any key to continue..."
      $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
      Write-Host ""
  } 
  else 
  {
      # Not running in console, so sleep for 5 seconds
      Start-Sleep -Seconds 5
  }
}
	
Function PoweronNAS()
{
	# BEFORE WE DO ANYTHING, WE'LL TRY TO DISCONNECT THE NAS #
	Write-LogMessage "Attempting to disconnect any pre-existing ISCSI sessions to this IQN..."
	Disconnect-IscsiTarget -Confirm:$false -ErrorAction Stop | get-iscsitarget -nodeaddress $iscsiIQN

	Write-LogMessage "Checking if $iscsiDriveLetter is in use by another device..."
	if (test-path $iscsiDriveLetter)
	{ 
  		# VALIDATE THAT DRIVE LETTER IS NOT ASSIGNED.  IF IT IS, IT MUST BE ASSIGNED TO SOMETHING ELSE (REFLECT, USB ETC) #
  		[void] [Windows.Forms.MessageBox]::Show("Drive letter $iscsiDriveLetter is already in use. Please ensure that $iscsiDriveLetter is unassigned and run this script again.", “ISCSI Script”, [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Error)
  		Exit 
	}

	# PING THE NAS IP ADDRESS TO ENSURE THAT IT'S DOWN BEFORE ISSUING THE WAKE ON LAN REQUEST.  SET IT TO 1 PING AS WE ARE ON THE LOCAL LAN SO IF ONE DOESN'T GO THORUGH, NONE OF THEM WILL #
	Write-LogMessage "Checking if the NAS is pingable..."
	If ((Test-Connection $iscsiIP -count 1 -Quiet) -eq $False)
	{
  		Write-LogMessage "NAS is not responding to ICMP Requests.  Sending Wake On Lan request."
		# "&" IS NEEDED TO RUN EXECUTABLE #
		  & $WOLpath $iscsiMAC $iscsiIP 255.255.255.0 7
	}

	# SURPRESS ANY SHELL ERROR MESSAGES IF THE SITE IS NOT UP #
	$ErrorActionPreference = "SilentlyContinue"

	# Specify the site you want to check #
	[string] $url = $iscsiWebpage

	$period = [timespan]::FromSeconds(10)
	$lastRunTime = [DateTime]::MinValue 

	Write-LogMessage "Waiting for Management Webpage to come online... "
	
	$i = 1
	while (1)
	{
    	# IF THE NEXT PERIOD ISN'T HERE YET, SLEEP SO WE DON'T CONSUME CPU
    	
		#It looks like it's about 4 tries before Reflect starts.  So the timeout is set at 10. #
		if ($i -eq $WebsiteTimeOut) {[void] [Windows.Forms.MessageBox]::Show("Timed out waiting for management website.", “ISCSI Script”, [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Error); Exit}
		
		while ((Get-Date) - $lastRunTime -lt $period)
      		{ Start-Sleep -Milliseconds 500 }
    	$lastRunTime = Get-Date

		Write-LogMessage "Waiting for NAS management interface... (Try $i of $WebsiteTimeOut)"
		$i = $i + 1

		# Create an object called request and paste the all of the details of the webrequest of the URL into that object #
		[net.httpWebRequest] $request = [net.webRequest]::create($url)

		# Create an object of type "WebResponse" that will store the the Response Information from the queried URL #
		[net.httpWebResponse] $result = $request.getResponse()
		[IO.Stream] $stream = $result.GetResponseStream()
		[IO.StreamReader] $reader = New-Object IO.StreamReader($stream)
		[string] $output = $reader.readToEnd()

		# IF THE SITE IS UP, MAP THE ISCSI DRIVE.  OTHERWISE, DO NOTHING AND WAIT 10 SECONDS TO TRY AGAIN.
		if ($result.StatusCode -ge "200") 
		{
			Write-LogMessage "Management Website found.  Connecting ISCSI Target..."
			New-Iscsitargetportal -TargetPortalAddress $iscsiIP | Out-Null
  	        Connect-IscsiTarget -NodeAddress $iscsiIQN –IsPersistent $False -ErrorAction Stop | Out-Null

			# GIVE THE CONNECTION A FEW SECONDS TO CONNECT #
			Start-Sleep 2

			# CHANGE THE DRIVE LETTER TO SOMETHING WELL ABOVE WHERE ANY USB KEYS OR OTHER TEMPORARY DRIVES MAY RESIDE #
  			
			Write-LogMessage "Resetting Drive letter for NAS to $iscsiDriveLetter which is specified in config..."
			
			$Drive = get-wmiobject -Class win32_volume | where-object {$_.label -eq $iscsiVolumeLabel} 
	    	set-wmiinstance -input $drive -Arguments @{DriveLetter=$iscsiDriveLetter.Substring(0,2)} | Out-Null
 			
			break # WE'VE GOT THE DRIVE LETTER ASSIGNED TO EXIT THE WHILE LOOP #
		} 
		
		$stream.flush()
		$stream.close()
	}	

}

Function StartReflectBackup()
{
	#Write-LogMessage "Calling Reflect application to run custom backup configured in XML..."
	cmd /c $BatchFileToStartReflect $ReflectBackupConfig
		
	# NEED TO PUT PARAMATERS ON A SEPERATE LINK TO PASS THEM IN  #
	#$exe = "C:\Program Files\Macrium\Reflect\reflect.exe"

	#Write-LogMessage "Calling Reflect application to run custom backup configured in XML..."
	## -e is to execute an XML file, -w is wait if Reflect is busy and -inc is to run an incremental backup #
	#&$exe -e -w -inc $ReflectBackupConfig
	
	Write-LogMessage "Reflect has ended its execution..."
}

Function ShutdownNAS()
{
	# CHECK IF THE NAS IS ONLINE AND CONNECTED TO DRIVE LETTER DEFINED ABOVE #
	# IF TRUE, THIS MEANS THE NAS IS POWERED ON, THE DRIVE IS PRESENT AND THE DRIVE BELONGS TO THE LABEL DEFINED ABOVE #
	
	Write-LogMessage "Checking if specific NAS is online and mounted to the drive letter stated in the config..."
	if ([System.IO.DriveInfo]::GetDrives() | ? {$_.VolumeLabel -eq $iscsiVolumeLabel -and $_.Name -eq $iscsiDriveLetter })
	{ 
  		try
		{
			Write-LogMessage "Attempting to disconnect ISCSI connection..."
			# DISCONNECT ISCSI NODE BY ITS IQN.  SPECIFY CONFIRM TO ENSURE IT DOESN'T PROMPT, SPECIFY ERRORACTION SO THE CATCH GRABS IT IF IT FAILS #
	    	Disconnect-IscsiTarget -Confirm:$false -ErrorAction Stop | get-iscsitarget -nodeaddress $iscsiIQN 
      		Remove-IscsiTargetPortal -TargetPortalAddress $iscsiIP -Confirm:$false -ErrorAction Stop
    	}
  		catch # ASSUMPING THE DISCONNECT FAILED, DISPLAY THE ERROR MESSAGE AND EXIT THE SCRIPT #
			{ [void] [Windows.Forms.MessageBox]::Show("$Error", “ISCSI Script”, [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Error); exit	}
		
		# IF WE GOT THIS FAR, THE NAS SHOULD BE DISCONNECTED FROM ISCSI AND SO WE CAN SHUT IT DOWN.  & IS NEEDED TO RUN EXECUTABLE#
	   	Write-LogMessage "Sending shutdown command to NAS..."
		& $PlinkPath -ssh -pw $nasPassword $nasUsername@$iscsiIP /sbin/shutdown -p now
      	
		Start-Sleep -Milliseconds 3000 # WAIT SEVERAL SECONDS BEFORE DISCONNECTING #
	    
		# DISCONNECT IT AGAIN TO TRY TO PREVENT THE INITITATOR FROM SAYING "RECONNECTING..."
	    Write-LogMessage "Attempted a second time to disconnect the iscsi target for safe measure..."
		Disconnect-IscsiTarget -Confirm:$false -ErrorAction Stop | get-iscsitarget -nodeaddress $iscsiIQN 
	
		Write-LogMessage "Execution Completed.  Teminating..."
	   	Exit # EVERYTHING SHOULD BE SHUTDOWN SO END EXECUTION OF THE SCRIPT #
	}
	
}

# ========================================================= PROGRAM EXECUTION BEGINS HERE ============================================================= #

$a = Get-Date
Write-Host _-_-_-__-_-_-__-_-_-_ $a _-_-_-__-_-_-__-_-_-_

# CHECK IF THE NAS IS ONLINE.  IF IT IS, START THE BACKUP AND THEN KEEP THE NAS UP. OTHERWISE TURN ON THE NAS, RUN THE BACKUP AND THEN TURN IT OFF AGAIN #
#if ([System.IO.DriveInfo]::GetDrives() | ? {$_.VolumeLabel -eq $iscsiVolumeLabel -and $_.Name -eq $iscsiDriveLetter })
# NEED TO PERFORM SIMPLE PING TEST OTHERWISE IT'LL TURN OFF DURING TESTING #
 if (Test-Connection $iscsiIP -count 1 -Quiet) 
{
	#Include some logic to verify that the folder itself is present and that enough space is available
	Write-LogMessage "Detected NAS specified in config.  Verifying Drive Letter..."
	if (Test-Path ($iscsiDriveLetter+$DestinationBackupFolder)) {StartReflectBackup}
	else 
	{ 
		Write-LogMessage "$iscsiDriveLetter$DestinationBackupFolder not found.  Attempting to determine if NAS is connected and just needs a drive remap..."
		PoweronNAS
		# TRY A SECOND TIME TO CONNECT TO THE DRIVE LETTER, IF IT FAILS, EXIT
		if (Test-Path ($iscsiDriveLetter+$DestinationBackupFolder)) {StartReflectBackup}
		else
		{ 
			Write-LogMessage "$iscsiDriveLetter$DestinationBackupFolder still not found.  Terminating execution..."; 
			Pause-Script
			Exit; 
		}
	}
}
Else 
{
	# IF WE ARE HERE IT MEANS THE NAS WAS OFF TO START.  POWER IT ON, RUN THE BACKUP AND TURN IT OFF AGAIN #
	PoweronNAS
	StartReflectBackup
	ShutdownNAS
}
			

 

 

Leave a Reply

Your email address will not be published.