diff --git a/Veeam BR Update/Update-Veeam.ps1 b/Veeam BR Update/Update-Veeam.ps1
new file mode 100644
index 0000000..9606f90
--- /dev/null
+++ b/Veeam BR Update/Update-Veeam.ps1
@@ -0,0 +1,750 @@
+<#
+.SYNOPSIS
+ Upgrades Veeam Environment to v12.1
+
+.DESCRIPTION
+ This script will upgrade Veeam Backup Enterprise Manager and/or
+ Veeam Backup & Replication Server depending on what's installed. The script
+ is designed to be executed on the server to be upgraded. It's also
+ possible to execute the script from a remote PowerShell session.
+
+.PARAMETER ISO
+ Location of Veeam ISO containing upgrade files. If not specified, script will attempt to download the ISO from Veeam's public servers.
+
+.PARAMETER License
+ Veeam License key file
+
+.PARAMETER ServicePassword
+ Password for the account under which the Veeam Backup Service will run (only required if LocalSystem account is not used)
+
+.PARAMETER SqlAuthentication
+ Authentication mode to connect to the database server. Possible options: Windows, Native
+
+.PARAMETER SqlPassword
+ Password to connect to the SQL server (only required when using 'Native' authentication mode)
+
+.PARAMETER LicenseAutoupdate
+ Boolean to determine if you want to update license automatically
+
+.PARAMETER AutoUpgrade
+ Boolean to automatically upgrade existing components in the backup infrastructure
+
+.OUTPUTS
+ Update-Veeam.ps1 returns exit code of 0 upon success
+
+.EXAMPLE
+ Update-Veeam.ps1 -License "C:\license.lic"
+
+ Description
+ -----------
+ Upgrades Veeam environment using the specified license file and downloads the Veeam 12 ISO from Veeam's public servers
+
+.EXAMPLE
+ Update-Veeam.ps1 -ISO "C:\VeeamBackup&Replication_12.1.1.56_20240127.iso" -License "C:\license.lic"
+
+ Description
+ -----------
+ Upgrades Veeam environment using the specified license file and uses the specified local ISO
+
+.NOTES
+ NAME: Update-Veeam.ps1
+ VERSION: 1.0
+ AUTHOR: Chris Arceneaux
+ TWITTER: @chris_arceneaux
+ GITHUB: https://github.com/carceneaux
+
+.LINK
+ https://helpcenter.veeam.com/docs/backup/vsphere/upgrade_vbr_answer_file.html?ver=120
+
+.LINK
+ https://helpcenter.veeam.com/docs/backup/em/em_silent_upgrade.html?ver=120
+
+.LINK
+ https://github.com/VeeamHub/powershell/blob/master/BR-UpgradeV12.1/Update-Veeam.ps1
+
+ Original script by Chris Arceneaux
+
+#>
+#Requires -RunAsAdministrator
+[CmdletBinding(DefaultParametersetName = "None")]
+param(
+ [Parameter(Mandatory = $false)]
+ [String] $ISO = "download",
+ [Parameter(Mandatory = $false)]
+ [String] $License,
+ [Parameter(Mandatory = $false)]
+ [String] $ServicePassword,
+ [Parameter(Mandatory = $true, ParameterSetName = "SqlAuth")]
+ [ValidateSet("Windows", "Native")]
+ [String] $SqlAuthentication = "Windows",
+ [Parameter(Mandatory = $true, ParameterSetName = "SqlAuth")]
+ [String] $SqlPassword,
+ [Parameter(Mandatory = $false)]
+ [bool] $LicenseAutoupdate = $true,
+ [Parameter(Mandatory = $false)]
+ [bool] $AutoUpgrade = $true
+)
+
+# Setting Log Location
+$logFolder = $env:SYSTEMDRIVE + "\temp\veeam-upgrade"
+New-Item -ItemType Directory -Force -Path $logFolder | Out-Null #makes sure folder exists
+$logFile = "$logFolder\upgrade.log"
+Clear-Content $logFile -ErrorAction SilentlyContinue
+
+Function Get-Software {
+ # Sourced from https://mcpmag.com/articles/2017/07/27/gathering-installed-software-using-powershell.aspx
+ [OutputType('System.Software.Inventory')]
+ [Cmdletbinding()]
+ Param(
+ [Parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
+ [String[]]$Computername = $env:COMPUTERNAME
+ )
+ Begin {
+ }
+ Process {
+ ForEach ($Computer in $Computername) {
+ If (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
+ $Paths = @("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "SOFTWARE\\Wow6432node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
+ ForEach ($Path in $Paths) {
+ Write-Verbose "Checking Path: $Path"
+ # Create an instance of the Registry Object and open the HKLM base key
+ Try {
+ $reg = [microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine', $Computer, 'Registry64')
+ }
+ Catch {
+ Write-Error $_
+ Continue
+ }
+ # Drill down into the Uninstall key using the OpenSubKey Method
+ Try {
+ $regkey = $reg.OpenSubKey($Path)
+ # Retrieve an array of string that contain all the subkey names
+ $subkeys = $regkey.GetSubKeyNames()
+ # Open each Subkey and use GetValue Method to return the required values for each
+ ForEach ($key in $subkeys) {
+ Write-Verbose "Key: $Key"
+ $thisKey = $Path + "\\" + $key
+ Try {
+ $thisSubKey = $reg.OpenSubKey($thisKey)
+ # Prevent Objects with empty DisplayName
+ $DisplayName = $thisSubKey.getValue("DisplayName")
+ If ($DisplayName -AND $DisplayName -notmatch '^Update for|rollup|^Security Update|^Service Pack|^HotFix') {
+ $Date = $thisSubKey.GetValue('InstallDate')
+ If ($Date) {
+ Try {
+ $Date = [datetime]::ParseExact($Date, 'yyyyMMdd', $Null)
+ }
+ Catch {
+ Write-Warning "$($Computer): $_ <$($Date)>"
+ $Date = $Null
+ }
+ }
+ # Create New Object with empty Properties
+ $Publisher = Try {
+ $thisSubKey.GetValue('Publisher').Trim()
+ }
+ Catch {
+ $thisSubKey.GetValue('Publisher')
+ }
+ $Version = Try {
+ #Some weirdness with trailing [char]0 on some strings
+ $thisSubKey.GetValue('DisplayVersion').TrimEnd(([char[]](32, 0)))
+ }
+ Catch {
+ $thisSubKey.GetValue('DisplayVersion')
+ }
+ $UninstallString = Try {
+ $thisSubKey.GetValue('UninstallString').Trim()
+ }
+ Catch {
+ $thisSubKey.GetValue('UninstallString')
+ }
+ $InstallLocation = Try {
+ $thisSubKey.GetValue('InstallLocation').Trim()
+ }
+ Catch {
+ $thisSubKey.GetValue('InstallLocation')
+ }
+ $InstallSource = Try {
+ $thisSubKey.GetValue('InstallSource').Trim()
+ }
+ Catch {
+ $thisSubKey.GetValue('InstallSource')
+ }
+ $HelpLink = Try {
+ $thisSubKey.GetValue('HelpLink').Trim()
+ }
+ Catch {
+ $thisSubKey.GetValue('HelpLink')
+ }
+ $Object = [pscustomobject]@{
+ Computername = $Computer
+ DisplayName = $DisplayName
+ Version = $Version
+ InstallDate = $Date
+ Publisher = $Publisher
+ UninstallString = $UninstallString
+ InstallLocation = $InstallLocation
+ InstallSource = $InstallSource
+ HelpLink = $HelpLink
+ EstimatedSizeMB = [decimal]([math]::Round(($thisSubKey.GetValue('EstimatedSize') * 1024) / 1MB, 2))
+ }
+ $Object.pstypenames.insert(0, 'System.Software.Inventory')
+ Write-Output $Object
+ }
+ }
+ Catch {
+ Write-Warning "$Key : $_"
+ }
+ }
+ }
+ Catch { }
+ $reg.Close()
+ }
+ }
+ Else {
+ Write-Error "$($Computer): unable to reach remote system!"
+ }
+ }
+ }
+}
+
+Function Test-PendingReboot {
+ # Sourced from https://www.powershellgallery.com/packages/PendingReboot/0.9.0.6
+ [CmdletBinding()]
+ param(
+ [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
+ [Alias("CN", "Computer")]
+ [String]
+ $ComputerName = $env:COMPUTERNAME
+ )
+
+ process {
+ try {
+ $invokeWmiMethodParameters = @{
+ Namespace = 'root/default'
+ Class = 'StdRegProv'
+ Name = 'EnumKey'
+ ComputerName = $ComputerName
+ ErrorAction = 'Stop'
+ }
+
+ $hklm = [UInt32] "0x80000002"
+
+ ## Query the Component Based Servicing Reg Key
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\')
+ $registryComponentBasedServicing = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootPending'
+
+ ## Query WUAU from the registry
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\')
+ $registryWindowsUpdateAutoUpdate = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames -contains 'RebootRequired'
+
+ ## Query JoinDomain key from the registry - These keys are present if pending a reboot from a domain join operation
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Services\Netlogon')
+ $registryNetlogon = (Invoke-WmiMethod @invokeWmiMethodParameters).sNames
+ $pendingDomainJoin = ($registryNetlogon -contains 'JoinDomain') -or ($registryNetlogon -contains 'AvoidSpnSet')
+
+ ## Query ComputerName and ActiveComputerName from the registry and setting the MethodName to GetMultiStringValue
+ $invokeWmiMethodParameters.Name = 'GetMultiStringValue'
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\', 'ComputerName')
+ $registryActiveComputerName = Invoke-WmiMethod @invokeWmiMethodParameters
+
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\', 'ComputerName')
+ $registryComputerName = Invoke-WmiMethod @invokeWmiMethodParameters
+
+ $pendingComputerRename = $registryActiveComputerName -ne $registryComputerName -or $pendingDomainJoin
+
+ ## Query PendingFileRenameOperations from the registry
+ $invokeWmiMethodParameters.ArgumentList = @($hklm, 'SYSTEM\CurrentControlSet\Control\Session Manager\', 'PendingFileRenameOperations')
+ $registryPendingFileRenameOperations = (Invoke-WmiMethod @invokeWmiMethodParameters).sValue
+ $registryPendingFileRenameOperationsBool = [bool]$registryPendingFileRenameOperations
+
+ $isRebootPending = $registryComponentBasedServicing -or `
+ $pendingComputerRename -or `
+ $pendingDomainJoin -or `
+ $registryPendingFileRenameOperationsBool -or `
+ $systemCenterConfigManager -or `
+ $registryWindowsUpdateAutoUpdate
+
+ return $isRebootPending
+ }
+
+ catch {
+ Write-Warning "$Computer`: $_"
+ }
+
+ }
+}
+
+Function Write-Log {
+ Param ([string]$logString)
+
+ $logEntry = "$('[{0:MM/dd/yyyy} {0:HH:mm:ss}]' -f (Get-Date)) $logString"
+ Write-Output $logEntry
+ Write-Output $logEntry | Out-file $logFile -Append
+}
+
+Function Update-Veeam {
+ Param(
+ [String]$SilentInstallExe,
+ [String]$Answer,
+ [String]$Logs
+ )
+
+ $params = @(
+ "/AnswerFile"
+ '"{0}"' -f $Answer
+ "/SkipNetworkLogonErrors"
+ "/LogFolder"
+ '"{0}"' -f $Logs
+ )
+ return (Start-Process "$SilentInstallExe" -Wait -ArgumentList $params -Passthru).ExitCode
+}
+
+Write-Log "INFO: Upgrade logs for this script can be found here: $logFile"
+
+# Pending reboot check
+if (Test-PendingReboot) {
+ throw "This Windows server requires a reboot prior to beginning the Veeam Backup & Replication upgrade. After rebooting this server, you can proceed with the upgrade."
+}
+
+# Enforcing absolute paths
+if ($iso -ne "download") {
+ $iso = Resolve-Path $iso
+}
+if ($license) {
+ $license = Resolve-Path $license
+}
+
+# Determining installed software
+$vbem = Get-Software | Where-Object { $_.DisplayName -eq "Veeam Backup Enterprise Manager" } | Select-Object DisplayName, Version
+$vbr = Get-Software | Where-Object { $_.DisplayName -eq "Veeam Backup & Replication Server" } | Select-Object DisplayName, Version
+if ((-not $vbem) -and (-not $vbr)) {
+ throw "At least 1 Veeam product must be installed on this server: Veeam Backup Enterprise Manager or Veeam Backup & Replication Server"
+}
+if ($vbem) { Write-Log "Veeam Backup Enterprise Manager found: $($vbem.Version)" }
+if ($vbr) { Write-Log "Veeam Backup & Replication Server found: $($vbr.Version)" }
+
+# Checking for license if Enteprise Manager is installed
+if ($vbem -and ($license.Length -eq 0)) {
+ throw "The License parameter MUST be used when upgrading Veeam Backup Enterprise Manager. Please correct and re-run this script."
+}
+
+# If ISO wasn't specified, download it from Veeam's public servers
+if ($iso -eq "download") {
+ try {
+ $iso = "$logFolder\VeeamBackup&Replication_12.1.1.56_20240127.iso"
+ Write-Log "ISO not specified. Checking if previously downloaded..."
+ if (Test-Path $iso) {
+ Write-Log "ISO found: $iso"
+ }
+ else {
+ Write-Log "ISO not found. Downloading ISO now..."
+ Start-BitsTransfer -Source "https://download2.veeam.com/VBR/v12/VeeamBackup&Replication_12.1.1.56_20240127.iso" -Destination $iso
+ Write-Log "ISO downloaded to: $iso"
+ }
+ }
+ catch {
+ Write-Log $_
+ throw "ISO download failed. Please check upgrade log for more information: $logFile"
+ }
+}
+
+# Mounting ISO
+try {
+ Write-Log "Mounting ISO in Operating System"
+ Mount-DiskImage -ImagePath $iso | Out-Null
+ $mountDrive = (Get-Volume | Where-Object { $_.FileSystemLabel -like "VEEAM BACKUP" }).DriveLetter + ":"
+}
+catch {
+ Write-Log $_
+ throw "ISO mount failed. Please check upgrade log for more information: $logFile"
+}
+
+# Validating ISO major version
+$fileInfo = Get-Content "$mountDrive\autorun.inf"
+if ($fileInfo -match "12") {
+ Write-Log "ISO validated for version 12"
+}
+else {
+ Write-Log "ISO failed validation! Non-v12 ISO detected."
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "Incorrect ISO detected! This script was designed to work with a Veeam Backup & Replication v12.1 ISO. Please correct and re-run this script."
+}
+
+# Validating 12.1 ISO
+try {
+ # Identifying Silent Install EXE
+ $file = Get-ChildItem -Recurse -Filter "Veeam.Silent.Install.exe" -File -Path $mountDrive
+ $exe = $file.FullName
+
+ if ("1.0.0" -eq $file.VersionInfo.ProductVersion) {
+ Write-Log "ISO failed validation! Non-v12.1 ISO detected."
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw
+ }
+
+ Write-Log "ISO validated as version 12.1"
+}
+catch {
+ Write-Log $_
+ Write-Log "Unable to validate 12.1 ISO. Please investigate and resolve. Logs can be found here: $logFile"
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "Incorrect ISO detected! This script was designed to work with a Veeam Backup & Replication v12.1 ISO. Please correct and re-run this script."
+}
+
+### VBR PRE-UPGRADE ACTIONS
+if ($vbr) {
+ try {
+ # Registering VeeamPSSnapin if necessary
+ Write-Log "Registering VeeamPSSnapin if necessary"
+ if (-Not (Get-Module -ListAvailable -Name Veeam.Backup.PowerShell)) {
+ Add-PSSnapin -PassThru VeeamPSSnapIn -ErrorAction Stop | Out-Null
+ }
+
+ try {
+ # Checking for Cloud Connect environment
+ $state = Get-VBRCloudInfrastructureState # returns error not Cloud Connect
+ Write-Log "Cloud Connect instance found. Determining infrastructure state..."
+
+ # Pre-upgrade actions for Cloud Connect environment
+ $vcc = $true
+ if ($state -eq "Active") {
+ Write-Log "ACTIVE: Enabling maintenance mode and waiting for all active sessions to complete"
+ Enable-VBRCloudMaintenanceMode
+ }
+ else {
+ Write-Log "MAINTENANCE: Maintenance mode already enabled. Currently waiting for all active sessions to complete"
+ }
+ $sw = [System.Diagnostics.Stopwatch]::StartNew() #stopwatch
+ # Forever loop until Cloud Connect active sessions complete or manual user interrupt
+ while ($true) {
+ $sessions = ([Veeam.Backup.Core.CCloudSession]::GetAll() | Where-Object { $_.JobName -ne "Console" } | Where-Object { $_.State -eq "Working" }).Count
+ if ($sessions -eq 0) {
+ Write-Log "All active sessions have gracefully ended. Total time waiting: $([int]$sw.Elapsed.TotalMinutes) minutes"
+ $sw.Stop()
+ break
+ }
+ Clear-Host
+ Write-Host "Still waiting for $sessions active sessions to complete after $([int]$sw.Elapsed.TotalMinutes) minutes..."
+ Write-Host "To interrupt the wait and forcefully close active sessions, press (I)"
+ # Allows manual escape from loop
+ if ($host.UI.RawUI.KeyAvailable) {
+ $key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp,IncludeKeyDown")
+ if ("i" -like $key.Character) {
+ Write-Log "Manual interrupt received, proceeding with upgrade. $sessions active sessions will be forcefully closed."
+ $sw.Stop()
+ break
+ }
+ }
+ Start-Sleep -Seconds 5
+ }
+
+ # Performing Configuration Backup prior to upgrade
+ Write-Log "Performing Configuration Backup prior to upgrade"
+ Start-VBRConfigurationBackupJob
+ }
+ catch {
+ # Pre-upgrade actions for Veeam Backup & Replication environment (no Cloud Connect)
+ $vcc = $false
+
+ # Performing Configuration Backup prior to upgrade
+ Write-Log "Performing Configuration Backup prior to upgrade"
+ Start-VBRConfigurationBackupJob
+ }
+ }
+ catch {
+ Write-Log "One of the pre-upgrade actions failed. Please investigate and resolve. Logs can be found here: $logFile"
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "ERROR: Upgrade halted. Please check logs for more information."
+ }
+}
+### END VBR PRE-UPGRADE ACTIONS
+
+### VBEM UPGRADE
+if ($vbem) {
+ # Closing open Console sessions
+ Write-Log "Closing open Console sessions"
+ Stop-Process -Name "Veeam.Backup.Shell" -Force -ErrorAction SilentlyContinue
+
+ # Stopping all Veeam services prior to upgrade
+ Write-Log "Stopping all Veeam services"
+ Get-Service veeam* | Stop-Service
+
+ try {
+ # Generate upgrade answer file
+ $answerFile = "$logFolder\EmAnswerFile_upgrade.xml"
+
+ Write-Log "Checking if Enterprise Manager answer file already exists..."
+ if (Test-Path $answerFile) {
+ Write-Log "Answer file: $answerFile"
+ }
+ else {
+ Write-Log "Answer file not found. Generating file now..."
+ Add-Content $answerFile @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(if ($LicenseAutoupdate){
+ ''
+ } else {
+ ''
+ })
+
+
+
+
+
+
+ $(if ($ServicePassword){
+ ''
+ })
+
+
+
+
+
+
+
+ $(if ($SqlAuthentication -eq "Native"){
+ ''
+ })
+
+
+
+
+
+
+
+
+
+
+"@
+ }
+ }
+ catch {
+ Write-Log $_
+ Write-Log "Answer file generation failed. Please investigate and resolve. Logs can be found here: $logFile"
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "ERROR: Answer file generation failed. Please check logs for more information."
+ }
+
+ try {
+ # Upgrading Veeam Backup Enterprise Manager
+ Write-Log "Upgrading Veeam Backup Enterprise Manager using answer file: $answerFile"
+ $result = Update-Veeam -SilentInstallExe $exe -Answer $answerFile -Logs $logFolder
+ if ($result -eq 0 -or $result -eq 3010 -or $result -eq 3011) { Write-Log "SUCCESS: ${result}" }
+ else { throw "ERROR: ${result}" }
+ }
+ catch {
+ Write-Log $_
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "Upgrade failed. Please check debug log for more information: $logFolder\EnterpriseManager.log"
+ }
+
+ Write-Log "Veeam Backup Enterprise Manager has been successfully upgraded"
+
+}
+### END VBEM UPGRADE
+
+### VBR UPGRADE
+if ($vbr) {
+ # Closing open Console sessions
+ Write-Log "Closing open Console sessions"
+ Stop-Process -Name "Veeam.Backup.Shell" -Force -ErrorAction SilentlyContinue
+
+ # Stopping all Veeam services prior to upgrade
+ Write-Log "Stopping all Veeam services"
+ Get-Service veeam* | Stop-Service
+
+ try {
+ # Generate upgrade answer file
+ $answerFile = "$logFolder\VbrAnswerFile_upgrade.xml"
+
+ Write-Log "Checking if Veeam Backup & Replication answer file already exists..."
+ if (Test-Path $answerFile) {
+ Write-Log "Answer file: $answerFile"
+ }
+ else {
+ Write-Log "Answer file not found. Generating file now..."
+ Add-Content $answerFile @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(if ($License){
+ ''
+ })
+
+
+
+ $(if ($LicenseAutoupdate){
+ ''
+ } else {
+ ''
+ })
+
+
+
+
+
+
+ $(if ($ServicePassword){
+ ''
+ })
+
+
+
+
+
+
+
+ $(if ($SqlAuthentication -eq "Native"){
+ ''
+ })
+
+
+
+
+
+
+ $(if ($AutoUpgrade){
+ ''
+ } else {
+ ''
+ })
+
+
+
+
+
+
+
+
+
+
+"@
+ }
+ }
+ catch {
+ Write-Log $_
+ Write-Log "Answer file generation failed. Please investigate and resolve. Logs can be found here: $logFile"
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "ERROR: Answer file generation failed. Please check logs for more information."
+ }
+
+ try {
+ # Upgrading Veeam Backup & Replication Server
+ Write-Log "Upgrading Veeam Backup & Replication Server using answer file: $answerFile"
+ $result = Update-Veeam -SilentInstallExe $exe -Answer $answerFile -Logs $logFolder
+ if ($result -eq 0 -or $result -eq 3010 -or $result -eq 3011) { Write-Log "SUCCESS: ${result}" }
+ else { throw "ERROR: ${result}" }
+ }
+ catch {
+ Write-Log $_
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "Upgrade failed. Please check debug log for more information: $logFolder\BackupServer.log"
+ }
+
+ Write-Log "Veeam Backup & Replication has been successfully upgraded"
+}
+### END VBR UPGRADE
+
+### VBR POST-UPGRADE ACTIONS
+if ($vbr) {
+ try {
+ if ($vcc) {
+ Write-Log "Disabling Cloud Connect Maintenance Mode"
+ powershell.exe -NoLogo -ExecutionPolicy bypass -NoProfile -Command "Import-Module 'C:\Program Files\Veeam\Backup and Replication\Console\Veeam.Backup.PowerShell\Veeam.Backup.PowerShell.psd1'; Disable-VBRCloudMaintenanceMode"
+ }
+
+ Write-Log "Shutting down Veeam prior to reboot. This may take a while as all Veeam Proxies & Repositories are currently being upgraded."
+ Get-Service veeam* | Where-Object {$_.Name -ne "VeeamBackupSvc"} | Stop-Service
+ Get-Service veeam* | Stop-Service
+ }
+ catch {
+ Write-Log $_
+ Write-Log "One of the post-upgrade actions failed. Please investigate and resolve. Logs can be found here: $logFile"
+ Write-Log "Unmounting Veeam ISO"
+ Dismount-DiskImage -ImagePath $iso
+ throw "ERROR: Post-upgrade actions failed. Please check logs for more information."
+ }
+}
+### END VBR POST-UPGRADE ACTIONS
+
+Write-Log "Unmounting Veeam ISO"
+Dismount-DiskImage -ImagePath $iso
+Write-Log "Script has completed successfully. Please reboot this server prior to using Veeam."
+Write-Host "This can be done easily in PowerShell as well: Restart-Computer -Force"
+return 0
\ No newline at end of file