Almost a year ago I was asked to write a little script.
This customer had chosen Dell AppAssure as their backup solution.
Note that I’m not bashing the product since I think it’s a solid solution.
However… as always a sales guy sold the product to a customer telling them ‘Yes, we can!’ when instead he should have said ‘No, we can not!’.
In the first place I want to thank this sales guy because he has brought me some new customers… 🙂
So, the customer was expecting AppAssure to be able to perform synchronous backups of i.e. a database- and file server.
But AppAssure is built for scheduled continuous backups!
You define that a server needs to get a backup ever X hours. When this time is passed, it will start a new backup.
So when the schedule is every 2 hours and the database server finished before that but the file server doesn’t, the database server will begin a new backup while the file server is still performing the previous backup task… and there we have the inconsistency.
So, this customer asked me to solve this.
I don’t think it needed to be solved since AppAssure simply has a different backup philosophy…
But I found it to be challenging nevertheless so here’s my solution (Dell AppAssure.ps1) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
param ( $CSV ) $CSVName = import-csv $CSV function Write-AppAssureToEventLog { [CmdletBinding()] [OutputType([int])] param ( [parameter(Mandatory=$true)][int]$EventID, [Parameter(Mandatory=$true)][string]$ErrorMessage, [parameter(Mandatory=$true)][validateset("Error","Information","Warning")][string]$Level ) try { [System.Diagnostics.EventLog]::CreateEventSource("AppAssureScript","Application") } catch {} Write-EventLog -LogName 'Application' -Source 'AppAssureScript' -EventId $EventID -Message $ErrorMessage -EntryType $Level } function Check-JobStatus { param ( [parameter(mandatory=$true,position=0)]$ProtectedServer ) $Check = Get-Activejobs -ProtectedServer $ProtectedServer if (($Check -eq "") -or ($Check -eq $null)) { write-output $false } else { write-output $true } } workflow Start-AppAssureSynchronousBackup { param ( [parameter(mandatory=$true)]$ComputerName ) foreach -parallel ($Computer in $ComputerName) { $TargetSnapshotJob = New-Snapshot -ProtectedServer $Computer $TargetCurrentJob = $true while ($TargetCurrentJob -eq $true) { Start-Sleep -seconds 5 $TargetCurrentJob = Check-JobStatus -ProtectedServer $Computer } Write-Output "* * * Backup job finished for $Computer * * *" $Status = Get-Completedjobs -ProtectedServer $Computer if ($Status[0].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: $Computer") } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: $Computer") } } } Import-Module AppAssurePowerShellModule foreach ($Entry in $CSVName) { $Targets = @() if ($Entry.Target0 -ne "") { $Targets += $Entry.Target0 } if ($Entry.Target1 -ne "") { $Targets += $Entry.Target1 } if ($Entry.Target2 -ne "") { $Targets += $Entry.Target2 } if ($Entry.Target3 -ne "") { $Targets += $Entry.Target3 } if ($Entry.Target4 -ne "") { $Targets += $Entry.Target4 } $Target0 = Get-ProtectedServers | where {$_.DisplayName -eq "$Targets[0]"} $Target1 = Get-ProtectedServers | where {$_.DisplayName -eq "$Targets[1]"} $Target2 = Get-ProtectedServers | where {$_.DisplayName -eq "$Targets[2]"} $Target3 = Get-ProtectedServers | where {$_.DisplayName -eq "$Targets[3]"} $Target4 = Get-ProtectedServers | where {$_.DisplayName -eq "$Targets[4]"} if (($Target0 -ne "") -and ($Target1 -ne "") -and ($Target2 -ne "") -and ($Target3 -ne "") -and ($Target4 -ne "")) { if ($Target0 -ne "") { $Target0SnapshotJob = New-Snapshot -ProtectedServer $Targets[0] } if ($Target1 -ne "") { $Target1SnapshotJob = New-Snapshot -ProtectedServer $Targets[1] } if ($Target2 -ne "") { $Target2SnapshotJob = New-Snapshot -ProtectedServer $Targets[2] } if ($Target3 -ne "") { $Target3SnapshotJob = New-Snapshot -ProtectedServer $Targets[3] } if ($Target4 -ne "") { $Target4SnapshotJob = New-Snapshot -ProtectedServer $Targets[4] } Start-Sleep -Seconds 5 # Check job status $Target0CurrentJob = $false $Target1CurrentJob = $false $Target2CurrentJob = $false $Target3CurrentJob = $false $Target4CurrentJob = $false if ($Target0 -ne "") { $Target0CurrentJob = Check-JobStatus -ProtectedServer $Targets[0] } if ($Target1 -ne "") { $Target1CurrentJob = Check-JobStatus -ProtectedServer $Targets[1] } if ($Target2 -ne "") { $Target2CurrentJob = Check-JobStatus -ProtectedServer $Targets[2] } if ($Target3 -ne "") { $Target3CurrentJob = Check-JobStatus -ProtectedServer $Targets[3] } if ($Target4 -ne "") { $Target4CurrentJob = Check-JobStatus -ProtectedServer $Targets[4] } # Check job status - loop while (($Target0CurrentJob -eq $true) -or ($Target1CurrentJob -eq $true) -or ($Target2CurrentJob -eq $true) -or ($Target3CurrentJob -eq $true) -or ($Target4CurrentJob -eq $true)) { Start-Sleep -Seconds 3 if ($Target0 -ne "") { $Target0CurrentJob = Check-JobStatus -ProtectedServer $Targets[0] } if ($Target1 -ne "") { $Target1CurrentJob = Check-JobStatus -ProtectedServer $Targets[1] } if ($Target2 -ne "") { $Target2CurrentJob = Check-JobStatus -ProtectedServer $Targets[2] } if ($Target3 -ne "") { $Target3CurrentJob = Check-JobStatus -ProtectedServer $Targets[3] } if ($Target4 -ne "") { $Target4CurrentJob = Check-JobStatus -ProtectedServer $Targets[4] } } # Output $Array = @() if ($Target0 -ne "") { Write-Output "* * * Backup job finished for $($Targets[0]) * * *" $Array += "$Target0 " $Status = Get-Completedjobs -ProtectedServer $Targets[0] if ($Status[0].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: "+$Targets[0]) -Level 'Information' } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: "+$Targets[0]) -Level 'Error' } } if ($Target1 -ne "") { Write-Output "* * * Backup job finished for $($Targets[1]) * * *" $Array += "$Target1 " $Status = Get-Completedjobs -ProtectedServer $Targets[1] if ($Status[1].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: "+$Targets[1]) -Level 'Information' } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: "+$Targets[1]) -Level 'Error' } } if ($Target2 -ne "") { Write-Output "* * * Backup job finished for $($Targets[2]) * * *" $Array += "$Target2 " $Status = Get-Completedjobs -ProtectedServer $Targets[2] if ($Status[2].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: "+$Targets[2]) -Level 'Information' } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: "+$Targets[2]) -Level 'Error' } } if ($Target3 -ne "") { Write-Output "* * * Backup job finished for $($Targets[3]) * * *" $Array += "$Target3 " $Status = Get-Completedjobs -ProtectedServer $Targets[3] if ($Status[3].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: "+$Targets[3]) -Level 'Information' } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: "+$Targets[3]) -Level 'Error' } } if ($Target4 -ne "") { Write-Output "* * * Backup job finished for $($Targets[4]) * * *" $Array += "$Target4 " $Status = Get-Completedjobs -ProtectedServer $Targets[4] if ($Status[4].Status -eq 'Succeeded') { Write-AppAssureToEventLog -EventID '33182' -ErrorMessage ("AppAssure backup procedure completed for: "+$Targets[4]) -Level 'Information' } else { Write-AppAssureToEventLog -EventID '33183' -ErrorMessage ("AppAssure backup procedure failed for: "+$Targets[4]) -Level 'Error' } } } } |
So how does it work?
Files
The files for the scripted AppAssure synchronous backup are located on the AppAssure management server at ‘C:\AppAssure’.
The script
The script is located in ‘C:\AppAssure’ on the AppAssure server and is named ‘AppAssure_SynchronousBackup.ps1’. This script takes a CSV name as a parameter when executed through the Windows Task Scheduler.
The content (CSV)
For each group a backup schedule is decided. For each backup schedule there is a separate CSV file named ‘Group.csv’. These CSV files have headers named ‘Target’
Each row in such a file is considered a group of server that need their backup to be in sync. There is a maximum of 5 servers per group:
Task Scheduler
The Windows task scheduler is used to execute the backup jobs according to a defined schedule.
This schedule is configured in the task, not in the script.
Event log
The script writes output to the Application event log in Windows.
The source for the events is ‘AppAssureScript’ where the EventID ‘33182’ means the backup job has succeeded and Event ID ‘33183’ means the backup job has failed.
The name of the server is shown in the message of the event:
Howto add a new group of server to an existing backup job
Edit the appropriate CSV file and add a new group of servers.
Howto create a new backup job
1) Create a new CSV file the the headers as shown in the following example:
2) Create a new Scheduled Task with the following content:
The arguments are ‘-ExecutionPolicy bypass -command C:\AppAssure\AppAssure_SynchronousBackup.ps1 -CSV C:\AppAssure\group1.csv ‘ without the quotes and with the appropriate CSV file.
3) Configure the desired schedule for the backup task to run by defining a trigger:
The finishing touch
1) Define and configure the backup schedules (Window Task Scheduler)
2) Enter the desired groups of servers in the appropriate CSV files.
3) Configure the internal monitoring tool to monitor failed backup jobs by using the events written to the event log by the script.