Skip to main content

VEEAM Maintenance Mode PowerShell Scripts

  • March 1, 2023
  • 17 comments
  • 559 views

Martin Weber

Yesterday, during the VEEAM User Group Germany someone was missing a Maintenance Mode for VEEAM. The main problem was to keep which job was enabled and which was disabled. I wrote a script some times ago which stopped all jobs and disable them but before the state is dumped into a JSON File. On leaving maintenence the JSON-File will be read and required jobs are set to enabled again and failed jobs are triggered to restart.

 

The scripts (and more) can be found on our github repository: https://github.com/claranet/VeeamHub/

 

Here are these scripts:

Enter-Maintenance.ps1

param(
[switch]$DryRun=$False
)

Import-Module Veeam.Backup.PowerShell

Function log($message) {
$timestamp = Get-Date -Format "yyyy-MM-ss hh:mm:ss"
Write-Host "$($timestamp) - $($message)"
}

if($DryRun) { log "DRY RUN - NOTHING WILL CHANGE" }

$_FILE = "$PSScriptRoot/job_states.json"
if( [System.IO.File]::Exists($_FILE) ) {
log "State File already exists - remove it first!!"
pause
Exit 1
}


# Create new, empty state file
New-Item -Path $_FILE -Force | Out-Null

log "Load Jobs"
$JOBS = Get-VBRJob

# Save list of current jobs state (enabled/disabled)
log "Dump Job states to file"
$STATES = ( $JOBS | SELECT Id, Name, @{N="IsScheduleEnabled";E={$_.info.IsScheduleEnabled}} )
$STATES | ConvertTo-Json | Out-File -FilePath $_FILE

# Stop All running jobs
log "Stop all Jobs"
if(-not $DryRun) { $JOBS | Stop-VBRJob -RunAsync }

# Disable all Jobs
log "Disable all jobs"
if(-not $DryRun) { $JOBS | Disable-VBRJob | Out-Null }

log "Finish"

pause

Leave-Maintenance.ps1

param(
[switch]$DryRun=$False
)
Import-Module Veeam.Backup.PowerShell

Function log($message) {
$timestamp = Get-Date -Format "yyyy-MM-ss hh:mm:ss"
Write-Host "$($timestamp) - $($message)"
}

if($DryRun) { log "DRY RUN - NOTHING WILL CHANGE" }

$_FILE = "$PSScriptRoot/job_states.json"
if( -not [System.IO.File]::Exists($_FILE) ) {
log "Missing state file... Cancle"
pause
Exit 1
}

log "Load list of Last Job States"
$STATES = Get-Content $_FILE | ConvertFrom-Json

log "Enable all needed Jobs"
$JOBS = Get-VBRJob -Name ($STATES|?{-not $_.Proxy -and $_.IsScheduleEnabled}).Name
if(-not $DryRun) { $JOBS | Enable-VBRJob | Out-Null }

log "Retry Failed Backup jobs"
if(-not $DryRun) { $JOBS | ?{ $_.GetLastResult() -eq "Failed" } | Start-VBRJob -RunAsync -RetryBackup }

Remove-Item -Path $_FILE

log "Finish"
pause

 

17 comments

Madi.Cristil
Forum|alt.badge.img+8
  • Community Manager
  • March 1, 2023

This is great , @Martin Weber ! 😊 Thank you for sharing and welcome to the Community Hub 😉


JMeixner
Forum|alt.badge.img+16
  • On the path to Greatness
  • March 1, 2023

Great 😎👍🏼

We have spoken about this yesterday. Saves me some development time 😁


winguru_it
Forum|alt.badge.img
  • New Here
  • March 1, 2023

Hey Martin, thanks for sharing!


Chris.Childerhose
Forum|alt.badge.img+21

These are great scripts for sure.  Thanks for sharing, Martin.  Another thing to add to my repository collection. 😎


JMeixner
Forum|alt.badge.img+16
  • On the path to Greatness
  • March 1, 2023

Is this query reliable?

$STATES = ( $JOBS | SELECT Id, Name, @{N="IsScheduleEnabled";E={$_.info.IsScheduleEnabled}} )

In my environment the IsScheduleEnabled parameter is true for every job, with or without schedule….

Any similar experiences?

 

Edit:
Tested now in a V11 environment - there is the parameter set correct.
Has V12 a problem with this or is there any other parameter for this in V12?

Just created some unscheduled test jobs in V12 and all have set the IsScheduledEnabled parameter to True.


vAdmin
Forum|alt.badge.img+2
  • Influencer
  • March 1, 2023

Thank you @Martin Weber , this is great 😃


Martin Weber
  • Author
  • Comes here often
  • March 2, 2023

@JMeixnerYes, there is a “Feature” ( BUG :P ) in my script

 

$Job.IsSchdeuleEnabled is $False if the Job is running automatically and if you set the to disable. If it is manual job with no active scheduler, you are not able to disable them - and then IsScheduleEnable is always $True.

The better name for this Property should be IsActive…

 

Edit

It is not a bug - but I will further check it. I just added a filter for disabeling jobs if it is scheduleable (automatic scheduler)


JMeixner
Forum|alt.badge.img+16
  • On the path to Greatness
  • March 2, 2023

Ah yes, I thought the wrong way because of the name of the parameter.

Thank you for the clarification @Martin Weber 😎👍🏼


Martin Weber
  • Author
  • Comes here often
  • March 3, 2023

@JMeixner No Problem :)

Also be aware of the cmdlet Remove-VBRRestorePoint!

Get-VBRRestorePoint list every single restore point of a vm. BUT Remove-VBRRestorePoint did not remove a single point, it will remove the whole VM from the storage...


marco_s
Forum|alt.badge.img+8
  • On the path to Greatness
  • March 3, 2023

Very useful script @Martin Weber !

Try proposing a feature request on the Veeam forum, after all, it is a feature already present for VCC and VSPC!

 


vAdmin
Forum|alt.badge.img+2
  • Influencer
  • March 8, 2023

Hi @Martin Weber , there is a slight typo in the Calculated Property:

 

Old code with comma:

$STATES = ($JOBS | Select-Object Id, Name, IsScheduleEnabled, @{ N = "IsSchedulable", E = { $_.IsSchedulable() } })

 

Updated code with semicolon:

$STATES = ($JOBS | Select-Object Id, Name, IsScheduleEnabled, @{ N = "IsSchedulable"; E = { $_.IsSchedulable() } })

 

Hope that helps.


Martin Weber
  • Author
  • Comes here often
  • March 8, 2023

@vAdmin Thanks, yes this was a typo … ashes to my head 🤕

I fixed it on the Repo


falkob
Forum|alt.badge.img+13
  • Veeam Vanguard
  • March 8, 2023

German Veeam Usergroup power here ! Thank you @Martin Weber for sharing !
Sharing is Caring!


cymon
Forum|alt.badge.img+3
  • Comes here often
  • March 9, 2023

Thank you @Martin Weber!


Scott
Forum|alt.badge.img+10
  • Veeam Legend
  • March 15, 2023

Thanks for sharing


chris.grady
Forum|alt.badge.img
  • New Here
  • December 7, 2025

Yesterday, during the VEEAM User Group Germany someone was missing a Maintenance Mode for VEEAM. The main problem was to keep which job was enabled and which was disabled. I wrote a script some times ago which stopped all jobs and disable them but before the state is dumped into a JSON File. On leaving maintenence the JSON-File will be read and required jobs are set to enabled again and failed jobs are triggered to restart.

 

The scripts (and more) can be found on our github repository: https://github.com/claranet/VeeamHub/

 

Here are these scripts:

Enter-Maintenance.ps1

param(
[switch]$DryRun=$False
)

Import-Module Veeam.Backup.PowerShell

Function log($message) {
$timestamp = Get-Date -Format "yyyy-MM-ss hh:mm:ss"
Write-Host "$($timestamp) - $($message)"
}

if($DryRun) { log "DRY RUN - NOTHING WILL CHANGE" }

$_FILE = "$PSScriptRoot/job_states.json"
if( [System.IO.File]::Exists($_FILE) ) {
log "State File already exists - remove it first!!"
pause
Exit 1
}


# Create new, empty state file
New-Item -Path $_FILE -Force | Out-Null

log "Load Jobs"
$JOBS = Get-VBRJob

# Save list of current jobs state (enabled/disabled)
log "Dump Job states to file"
$STATES = ( $JOBS | SELECT Id, Name, @{N="IsScheduleEnabled";E={$_.info.IsScheduleEnabled}} )
$STATES | ConvertTo-Json | Out-File -FilePath $_FILE

# Stop All running jobs
log "Stop all Jobs"
if(-not $DryRun) { $JOBS | Stop-VBRJob -RunAsync }

# Disable all Jobs
log "Disable all jobs"
if(-not $DryRun) { $JOBS | Disable-VBRJob | Out-Null }

log "Finish"

pause

Leave-Maintenance.ps1

param(
[switch]$DryRun=$False
)
Import-Module Veeam.Backup.PowerShell

Function log($message) {
$timestamp = Get-Date -Format "yyyy-MM-ss hh:mm:ss"
Write-Host "$($timestamp) - $($message)"
}

if($DryRun) { log "DRY RUN - NOTHING WILL CHANGE" }

$_FILE = "$PSScriptRoot/job_states.json"
if( -not [System.IO.File]::Exists($_FILE) ) {
log "Missing state file... Cancle"
pause
Exit 1
}

log "Load list of Last Job States"
$STATES = Get-Content $_FILE | ConvertFrom-Json

log "Enable all needed Jobs"
$JOBS = Get-VBRJob -Name ($STATES|?{-not $_.Proxy -and $_.IsScheduleEnabled}).Name
if(-not $DryRun) { $JOBS | Enable-VBRJob | Out-Null }

log "Retry Failed Backup jobs"
if(-not $DryRun) { $JOBS | ?{ $_.GetLastResult() -eq "Failed" } | Start-VBRJob -RunAsync -RetryBackup }

Remove-Item -Path $_FILE

log "Finish"
pause

 

Nice work Martin! Thanks for sharing; I came across this after working on a similar project and noticed the comments suggesting this may have been deprecated in recent versions. Just figured I’d add this if it helped anyone (note: one main difference is my script essentially waits (for a variable amount of minutes) for any running jobs to finish before continuing. It was also designed for SCCM integration with purpose of automating windows updates on all servers but they had concerns about updates forcing a reboot during active jobs running in VBR or if proxies were active related to a VBR job:

https://github.com/lostSail0r/sccm-veeam-proxy-patching
NOTE: Last validated on V12.3.2.x - Unclear if working on V13 Windows, highly unlikely to work on VSA at this time


It worked in that context, but open to fedback on if it can easily be re-purposed for a non SCCM integration to better match the post’s goal.

 

Thanks (I may borrow some of the fancier aspects of your script if still working on v12/13into mine later if thats kosher),

CG


waqasali
Forum|alt.badge.img+4
  • On the path to Greatness
  • December 7, 2025

Hi ​@Martin Weber thanks for sharing.