In previous posts “Randomize your Veeam backups via PowerShell” and “Fixing Veeam Backup v8 Periodic Scheduling” I discussed the unwanted and unexpected changes Veeam Backup and Replication 8 made to is Periodic Scheduling.
In summary, Periodic Schedules could no longer exceed a 24-hour period (e.g. you could no longer configure jobs to run every 30 hours) and that all Periodic Schedules were reset at midnight (causing all periodically scheduled backup jobs to run at the same time, right after midnight).
My desire is to have backup jobs evenly distributed throughout each day, with only a few running at a time. I also don’t want to manually tweak and manage all of my backup job schedules to avoid undesired concurrent operations. When a new backup job gets created, I don’t want my team to waste time trying to figure out when it should be scheduled to run.
My solution was to create a PowerShell script that will use Get-Random to randomly stagger all of the scheduled features of each backup job — leaving it to random chance to automatically schedule my backup jobs to avoid running all at the same time.
Veeam 9 made a some changes and additions to its PowerShell cmdlets to warrant a re-write of the scripts that I wrote to randomize the schedules of my backup jobs. My script modifies backup job schedules, but does not enable or disable if those schedules are used (meaning, you’ll see some settings below that are grayed out, but have settings that the script will change).
Scheduled Settings Before running Script
There are many settings in a backup job that are scheduled, and Veeam 9 introduced a few new ones. These screen shots are of a test/sample backup job and graphically illustrate the settings that my script could randomize for you.
Backup Job > Storage > Advanced > Backup
My script could randomize the following for you:
- Create synthetic full backups periodically (currently set for Saturday)
- Create active full backups periodically
- Monthly on (currently set for First Friday)
- Weekly on selected days (currently set for Saturday)
Backup Job > Storage > Advanced > Maintenance
These settings are new to Veeam 9. Depending on your architecture/storage, they likely require a lot of IOPS. Having these maintenance tasks staggered among jobs is ideal.
- Perform backup files health check
- Monthly on (currently set for Second Sunday)
- Weekly on selected days (currently set for Tuesday)
- Defragment and compact full backup file
- Monthly on (currently set for Third Tuesday)
- Weekly on selected days (currently set for Saturday)
Backup Job > Schedule
Modifying the periodic schedule is the primary purpose of the script. Simply state how many backups you want performed each day and the script will randomly stagger in minutes how often the job should run.
- Periodically every (currently set for 354 Minutes)
- Wait before each retry attempt for (currently set for 32 minutes)
Backup Job > Schedule > Time Periods
Preventing all backup jobs from running at midnight is the secondary purpose of the script. By using Permitted and Denied options, the script will automatically Deny jobs from running up to 4 hours after midnight (again, a offset that is randomly selected per day per job across all jobs).
- Denied (note the pattern and that some days there is no delay, while other days have a 4-hour delay)
- Start time within an hour (currently set for 42 minutes)
Scheduled Settings After running Script
After running my script, all of those settings have been semi-randomly modified.
Backup Job > Storage > Advanced > Backup
- Create synthetic full backups periodically (currently set for Wednesday)
- Create active full backups periodically
- Monthly on (currently set for First Saturday)
- Weekly on selected days (currently set for Wednesday)
Backup Job > Storage > Advanced > Maintenance
- Perform backup files health check
- Monthly on (now set for Last Friday)
- Weekly on selected days (now set for Sunday)
- Defragment and compact full backup file
- Monthly on (now set for Last Tuesday)
- Weekly on selected days (now set for Saturday)
Backup Job > Schedule
- Periodically every (now set for 451 Minutes)
- Wait before each retry attempt for (now set for 34 minutes)
Backup Job > Schedule > Time Periods
- Denied (note the pattern and that some days there is no delay, while other days have a 4-hour delay)
- Start time within an hour (now set for 20 minutes)
Veeam PowerShell Script to randomize backup schedules
I’ve added enough comments to my script that I think it sufficiently explains itself. I hope you find it useful.
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | # This script will randomly stagger Veeam 9 backup schedules for one or more jobs # Only schedule-related settings are modified. The script does not enable or disable job options. # Run this on a Veeam Backup and Replication version 9 server # You could even copy and paste it into Veeam Backup and Replication > Menu > PowerShell # Written by Jason Pearce of jasonpearce.com in September 2016 # Use at your own risk, freely share, and comment on improvements ############################################## # ENABLE Veeam PowerShell Snap-In ############ ############################################## # Add Veeam snap-in if required if ((Get-PSSnapin -Name VeeamPSSnapin -ErrorAction SilentlyContinue) -eq $null) {add-pssnapin VeeamPSSnapin} ############################################## # DEFINE User Preferences #################### ############################################## # Target: Using the names of backup jobs (wildcards, prefix, suffix), define which jobs this script should target #$Jobs = Get-VBRJob -Name "ExampleJob" #(one specific job) #$Jobs = Get-VBRJob -Name "Prefix-*" #(one or more jobs) #$Jobs = Get-VBRJob -Name "*-Suffix" #(one or more jobs) $Jobs = Get-VBRJob -Name "TestJob" #$Jobs = Get-VBRJob -Name "*" | Where-Object { $_.IsBackup -eq $true -and $_.IsScheduleEnabled -eq $true } | Sort Name #(all enabled backup jobs) #$Jobs = Get-VBRJob -Name "*-by-folder" | Where-Object { $_.IsBackup -eq $true -and $_.IsScheduleEnabled -eq $true } | Sort Name # Frequency: How many times per day should each job run? (e.g. minum frequency of backups) $BackupFrequencyPerDay = 4 # Retention: How many days of restore points should be retained? (e.g. the oldest restore point). Leave black if you do not want it modified. $BackupRetentionInDays = 7 # -------------------------------------------- # Decide what to Randomize (set $True or $False). # The script will randomize each schedule, but does not Enable or Disable the backup job option. # -------------------------------------------- # Randomize Synthetic Full Backups Periodically schedule? $RandomizeSyntheticFull = $True # Randomize Active Full Backup schedule (both Monthly On and Weekly On)? $RandomizeActiveFull = $True # Randomize Storage-Level Corruption Guard schedule (both Monthly On and Weekly On)? $RandomizeStorageLevelCorruptionGard = $True # Randomize Full Backup File Maintenance schedule (both Monthly On and Weekly On)? $RandomizeFullBackupFileMaintenance = $True # Randomize Periodically Every X Minutes schedule? (this is the main purpose of the script) $RandomizePeriodicallyEvery = $True # Randomize Time Periods schedule? (this adds a random Denied offset to each day to prevent all Periodic jobs from running at midnight) $RandomizeTimePeriods = $True # Randomize Start Time Within An Hour schedule? (instead of starting at :00, jobs will stagger start at :09, :17, :41, etc. minutes after the hour) $RandomizeStartTimeWithinHour = $True # Randomize Automatic Retry Wait in minutes (wait before each retry attempt for X minutes) $RandomizeAutomaticRetryWait = $True ############################################## # CALCULATE User Preferences ################# ############################################## # Frequency Quantity Calculation: Create a range of how many times per day each job should run (e.g. +-0.9) $MinNumBackupsPerDay = $BackupFrequencyPerDay - 0.9 $MaxNumBackupsPerDay = $BackupFrequencyPerDay + 0.9 # Frequency Minutes Calculation: Converting the quantity range into minutes (e.g. backup every 200 to 300 minutes) $MinutesInADay = 1440 $MinBackupRangeInMinutes = [math]::Round($MinutesInADay/$MaxNumBackupsPerDay) $MaxBackupRangeInMinutes = [math]::Round($MinutesInADay/$MinNumBackupsPerDay) # Retention Calculation: Should be ($BackupFrequencyPerDay x $BackupRetentioninDays) # GUI > Edit Backup Job > Storage > Retention Policy > Restore points to keep on disk $RetainCycles = $BackupFrequencyPerDay * $BackupRetentioninDays ############################################## # BEGIN Script ############################### ############################################## ############################################## # BEGIN Random variables # ############################################## # RandomFullPeriod: Variable for Schedule > Run the job automatically > Periodically every XX minutes. Must be a value between 0 and 1440. $RandomFullPeriodMinimum = $MinBackupRangeInMinutes $RandomFullPeriodMaximum = $MaxBackupRangeInMinutes # RandomHourlyOffset: Variable for Schedule > Run the job automatically > Periodically every > Schedule > Start time within an hour. Must be a value between 0 and 59. $RandomHourlyOffsetMinimum = 0 $RandomHourlyOffsetMaximum = 59 # RandomRetryTimeout: Variable for Schedule > Automatic Retry > Wait before each retry attempt for. Must be a value between 0 and 59. $RandomRetryTimeoutMinimum = 30 $RandomRetryTimeoutMaximum = 59 # RandomMidnightDelay: Variable to prevent all periodic backup jobs from running at midnight (Schedule Denied). $RandomMidnightDelay0Hours = "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay1Hours = "1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay2Hours = "1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay3Hours = "1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay4Hours = "1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay5Hours = "1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" $RandomMidnightDelay6Hours = "1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" # RandomPermitDeny: Default schedule settings for documentation purposes. #$RandomPermitDenyDefault = "<scheduler><Sunday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Sunday><Monday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Monday><Tuesday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Tuesday><Wednesday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Wednesday><Thursday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Thursday><Friday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Friday><Saturday>0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0</Saturday></scheduler>" # Every Day Number in the Month $EveryDayNumberInMonth = "First","Second","Third","Fourth","Last" # Every Day of the Week $EveryDayOfWeek = "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" # Every Month of the Year $EveryMonthOfYear = "January","February","March","April","May","June","July","August","September","October","November","December" # Random Day Number in the Month (mostly here for copy/paste reference in the foreach loop, because we'll want uniquely random results each time) $RandomDayNumberInMonth = Get-Random -Input "First","Second","Third","Fourth","Last" # Random Day of the Week (mostly here for copy/paste reference in the foreach loop, because we'll want uniquely random results each time) $RandomDayOfWeek = Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" # Random Month of the Year (mostly here for copy/paste reference in the foreach loop, because we'll want uniquely random results each time) $RandomMonthOfYear = Get-Random -Input "January","February","March","April","May","June","July","August","September","October","November","December" # Script Name $scriptName = "RandomizeBackupJobSchedules" # Start Time $starttime = Get-Date -uformat "%m-%d-%Y %I:%M:%S" # Report Header Clear-Host Write-Host "********************************************************************************" Write-Host "$scriptName`t`tStart Time:`t$starttime" Write-Host "********************************************************************************`n" ############################################## # BEGIN foreach loop # ############################################## # Make the following changes to all backup jobs foreach ($job in $jobs) { # Notice: State which job is being updated Write-Host "Setting job options on "$job.Name -ForegroundColor Yellow #-------------------------------------------------------------------- # Retention Policy if ($BackupRetentionInDays -ne $null) { # BEGIN Job Options ###################### # Read current job settings $jobOptions = Get-VBRJobOptions -Job $job # Set Job Advanced Backup Storage Options $jobOptions.BackupStorageOptions.RetainCycles = $RetainCycles # Apply these additional Backup Mode settings $jobOptions = Set-VBRJobOptions -Job $job -Options $jobOptions } #-------------------------------------------------------------------- # Randomize Synthetic Full Backups Periodically if ($RandomizeSyntheticFull -eq $True) { # Set-VBRJobAdvancedBackupOptions: Customizes advanced job backup settings. Set-VBRJobAdvancedBackupOptions -Job $job -TransformToSyntethicDays (Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday") | Out-Null } #-------------------------------------------------------------------- # Randomize Active Full Backup if ($RandomizeActiveFull -eq $True) { # Set-VBRJobAdvancedBackupOptions: Customizes advanced job backup settings. Set-VBRJobAdvancedBackupOptions -Job $job -FullBackupDays (Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday") -Months $EveryMonthOfYear -DayNumberInMonth (Get-Random -Input "First","Second","Third","Fourth","Last") -DayOfWeek (Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday") | Out-Null } #-------------------------------------------------------------------- # Randomize Storage-Level Corruption Guard if ($RandomizeStorageLevelCorruptionGard -eq $True) { # Set-VBRJobOptions: Applies custom job settings. Acts like a catch all for settings not in other Veeam 9 PowerShell Cmdlets. # Get current job settings $jobOptions = Get-VBRJobOptions $job # Define new randomized job settings $jobOptions.GenerationPolicy.RecheckDays = Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" $jobOptions.GenerationPolicy.RecheckBackupMonthlyScheduleOptions.DayNumberInMonth = Get-Random -Input "First","Second","Third","Fourth","Last" $jobOptions.GenerationPolicy.RecheckBackupMonthlyScheduleOptions.DayOfWeek = Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" $jobOptions.GenerationPolicy.RecheckBackupMonthlyScheduleOptions.Months = $EveryMonthOfYear # Apply these additional Backup Mode settings Set-VBRJobOptions -Job $job -Options $jobOptions | Out-Null } #-------------------------------------------------------------------- # Randomize Full Backup File Maintenance if ($RandomizeFullBackupFileMaintenance -eq $True) { # Set-VBRJobOptions: Applies custom job settings. Acts like a catch all for settings not in other Veeam 9 PowerShell Cmdlets. # Get current job settings $jobOptions = Get-VBRJobOptions $job # Define new randomized job settings $jobOptions.GenerationPolicy.CompactFullBackupDays = Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" $jobOptions.GenerationPolicy.CompactFullBackupMonthlyScheduleOptions.DayNumberInMonth = Get-Random -Input "First","Second","Third","Fourth","Last" $jobOptions.GenerationPolicy.CompactFullBackupMonthlyScheduleOptions.DayOfWeek = Get-Random -Input "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" $jobOptions.GenerationPolicy.CompactFullBackupMonthlyScheduleOptions.Months = $EveryMonthOfYear # Apply these additional Backup Mode settings Set-VBRJobOptions -Job $job -Options $jobOptions | Out-Null } #-------------------------------------------------------------------- # Randomize Periodically Every X Minutes if ($RandomizePeriodicallyEvery -eq $True) { # Create a new job schedule $jobScheduleOptions = Get-VBRJobScheduleOptions -Job $job # Disable Other Schedules $jobScheduleOptions.OptionsDaily.Enabled = $false $jobScheduleOptions.OptionsMonthly.Enabled = $false $jobScheduleOptions.OptionsContinuous.Enabled = $false # Enable Periodically Schedule $jobScheduleOptions.OptionsPeriodically.Enabled = $true # Configure Periodic Schedule in Minutes (0 = Hours, 1 = Minutes) $jobScheduleOptions.OptionsPeriodically.Kind = 1 # Generate random number for FullPeriod (Minimum and Maximum variables are defined above) $RandomFullPeriod = Get-Random -Minimum $RandomFullPeriodMinimum -Maximum $RandomFullPeriodMaximum # Create a random FullPeriod offset $jobScheduleOptions.OptionsPeriodically.FullPeriod = $RandomFullPeriod # Do some math to randomly stagger the NextRun (future), while correctly calculating the FullPeriod difference $RandomlyStagger = Get-Random -Minimum 1 -Maximum $RandomFullPeriod $jobScheduleOptions.NextRun = ($jobScheduleOptions.LatestRunLocal).AddMinutes($RandomlyStagger) # Apply the new job schedule Set-VBRJobScheduleOptions -Job $job -Options $jobScheduleOptions | Out-Null } #-------------------------------------------------------------------- # Randomize Time Periods schedule (e.g. midnight "run all jobs" avoidance) if ($RandomizeTimePeriods -eq $True) { # Create a new job schedule $jobScheduleOptions = Get-VBRJobScheduleOptions -Job $job # Generate random Midnight Delay (Schedule Denied) for each day. Variables up to $RandomMidnightDelay6Hours are already created above. $RandomMidnightDelaySun = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelayMon = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelayTue = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelayWed = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelayThu = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelayFri = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours $RandomMidnightDelaySat = Get-Random $RandomMidnightDelay0Hours,$RandomMidnightDelay1Hours,$RandomMidnightDelay2Hours,$RandomMidnightDelay3Hours,$RandomMidnightDelay4Hours # Create a random Midnight Delay (Schedule Denied) for the full week $RandomMidnightDelay = "<scheduler><Sunday>" + $RandomMidnightDelaySun + "</Sunday><Monday>" + $RandomMidnightDelayMon + "</Monday><Tuesday>" + $RandomMidnightDelayTue + "</Tuesday><Wednesday>" + $RandomMidnightDelayWed + "</Wednesday><Thursday>" + $RandomMidnightDelayThu + "</Thursday><Friday>" + $RandomMidnightDelayFri + "</Friday><Saturday>" + $RandomMidnightDelaySat + "</Saturday></scheduler>" # Apply the random Midnight Delay (Schedule Denied) $jobScheduleOptions.OptionsPeriodically.Schedule = $RandomMidnightDelay # Apply the new job schedule Set-VBRJobScheduleOptions -Job $job -Options $jobScheduleOptions | Out-Null } #-------------------------------------------------------------------- # Randomize Start Time Within An Hour schedule if ($RandomizeStartTimeWithinHour -eq $True) { # Create a new job schedule $jobScheduleOptions = Get-VBRJobScheduleOptions -Job $job # Generate random number for HourlyOffset (Minimum and Maximum variables are defined above) $RandomHourlyOffset = Get-Random -Minimum $RandomHourlyOffsetMinimum -Maximum $RandomHourlyOffsetMaximum # Create a random HourlyOffset $jobScheduleOptions.OptionsPeriodically.HourlyOffset = $RandomHourlyOffset # Apply the new job schedule Set-VBRJobScheduleOptions -Job $job -Options $jobScheduleOptions | Out-Null } #-------------------------------------------------------------------- # Randomize Automatic Retry Wait in minutes if ($RandomizeAutomaticRetryWait -eq $True) { # Create a new job schedule $jobScheduleOptions = Get-VBRJobScheduleOptions -Job $job # Generate random number for RetryTimeout (Minimum and Maximum variables are defined above) $RandomRetryTimeout = Get-Random -Minimum $RandomRetryTimeoutMinimum -Maximum $RandomRetryTimeoutMaximum # Create a random RetryTimeout $jobScheduleOptions.RetryTimeout = $RandomRetryTimeout # Apply the new job schedule Set-VBRJobScheduleOptions -Job $job -Options $jobScheduleOptions | Out-Null } # Report which jobs received these changes Write-Host "Changed settings for" $job.Name } # END foreach loop ########################### # END Backup Settings Script ################# #-------------------------------------------------------------------- # Outputs Write-Host "`nProcessing Complete" -ForegroundColor Yellow $finishtime = Get-Date -uformat "%m-%d-%Y %I:%M:%S" Write-Host "`n`n" Write-Host "********************************************************************************" Write-Host "$scriptName`t`tFinish Time:`t$finishtime" Write-Host "********************************************************************************" # Prompt to exit script - This leaves PS window open when run via right-click Write-Host "`n`n" Write-Host "Press any key to continue ..." -foregroundcolor Gray $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") |
Lastly, if you want to further stagger your jobs, you could create a Windows Schedule Task to run this script once a month or quarter to further keep your jobs and their settings randomly staggered.