Last month I created a topic for a script I had created using Veeam Service Provider Console API to automate the upgrade of Veeam Agents (Automate Veeam Agent Upgrades via VSPC | Veeam Community Resource Hub) which also got featured in the last recap of 2023 which was a very nice end to the year for my first post! I was already planning on working on a script to automate the process of bulk updating VBR servers, but after it was mentioned a couple of times, I thought I would share what I have so far. If, like me, you work for an MSP, we have multiple tenant VBR’s kicking around it can be very time consuming when a new upgrade for Veeam comes out, to get this deployed on all of our tenants servers. Luckily, in recent updates, VSPC has improved the process of remotely upgrading VBR via VSPC. However, it is still a manual process that can still be time consuming going through each tenant VBR Server one by one. I wanted to find a way to automate this process.
I could of wrote a script that just went through each VBR server that needed an upgrade and install it but, we had a few problems with that (i.e some VBR servers running on Hyper V Hosts - I know, it isn’t great! - some VBR servers running other applications - again not great). So, I wanted to still be selective in what was upgraded. We also have to take into consideration that the API call requires credentials to be in the body of the API POST. something which isn’t stored directly in VSPC. The route I decided on was to use a CSV file. This CSV hold the following information, VBR Server Name, Username (this is the username of an account that has permissions to install software on the VBR server) and Password. Below is a quick example (don’t worry, these are not real details haha). The column titles are in important for the script, so if you want to change these, you will need to make sure you update the script accordingly.
You may have noticed the double backslash in the Username Column too, it is important you do this otherwise the script will not work either.
Once you have completed your CSV file, save it somewhere that can be seen from where you intend to run the script from. I am currently just running this locally, but we could add this into an RMM product or Azure Functions as I do with the Agent Scripts.
What the script does is the following:
- Connects to VSPC API and gets a list of all VBR Servers
- Imports the predefined CSV file
- Sets the up to date version of VBR (this has to be manually updated each time a new version is released - for now.
- Filters out VBR servers that are currently up to date
- For each server that is listed in the CSV File, the script checks for the InstanceUID and collects the Username and Password for that instance from the CSV file
- The script then goes ahead and initiates the upgrade of each VBR Server in your list using the predefined conditions and Answer XML (please note these conditionals can be modified to your needs - more information can be found here (Upgrading Veeam Backup & Replication Servers - Veeam Service Provider Console Guide)
- You can then monitor the progress of the upgrades via VSPC Console
Below is the script that I am currently using:
## Ignore SSL certificate validation ##
Add-Type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
uSystem.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
$headers = New-Object "System.Collections.Generic.DictionaryNeString], String]]"
$headers.Add("Content-Type", "application/json")
$headers.Add("Authorization", "Bearer BEARERTOKEN")
## Connect to VBR API and collect information on backup servers ##
$VBRServers = Invoke-RestMethod 'https://VSPC-URL/api/v3/infrastructure/backupServers' -Method 'GET' -Headers $headers
$VBRServerData = $VBRServers.Data
## Import CSV file with server names ##
$importCSV = Import-Csv -LiteralPath "PATH TO CSV"
## Current released version of VBR - this will need to be manually editied when a new version is released ##
$CurrentVersion = eVersion]"12.1.0.2131"
## Filter to only VBR Servers that Require an Upgrade - this is to stop attempting to upgrade a VBR server in the CSV that has already been upgraded ##
$UpgradeRequired = $VBRServerData | Where-Object {$_.version -lt $CurrentVersion}
Foreach ($VBRServer in $UpgradeRequired)
{
Foreach($Import in $ImportCSV)
{
## Gather VBR Details ##
$VBR = $VBRServer| Where-Object {$_.Name -eq $Import.VBR}
$Username = ($Import | Where-Object {$_.VBR -eq $VBR.Name}).Username
$Password = ($Import | Where-Object {$_.VBR -eq $VBR.Name}).Password
$InstanceUID = ($VBR | Where-Object {$_.Name -eq $Import.VBR}).InstanceUID
Foreach($Instance in $InstanceUID)
{
## Create body for the API POST, this can be customized per your requirements (i.e "allowAutoReboot" set to null) ##
$body = @"
{
`"distribution`": null,
`"answerXml`": `"<?xml version=`\`"1.0`\`" encoding=`\`"utf-8`\`"?>`\r`\n<unattendedInstallationConfiguration bundle=`\`"VBR`\`" mode=`\`"upgrade`\`">`\r`\n`\t<!--nRequired] Parameter 'mode' defines installation mode that silent install should operate in-->`\r`\n`\t<!--Supported values: install/upgrade/uninstall-->`\r`\n`\r`\n`\t<properties>`\r`\n`\r`\n`\t`\t<!--License agreements-->`\r`\n`\t`\t<!--Specify parameters to accept all the license agreements during silent installation or upgrade-->`\r`\n`\r`\n`\t`\t<!--mRequired] Parameter ACCEPT_EULA specifies if you want to accept the Veeam license agreement. Specify '1' to accept the license agreement and proceed with installation or upgrade-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"ACCEPT_EULA`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t`\t<!--tRequired] Parameter ACCEPT_LICENSING_POLICY specifies if you want to accept Veeam licensing policy. Specify '1' to accept the licensing policy and proceed with installation or upgrade-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"ACCEPT_LICENSING_POLICY`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t`\t<!--ARequired] Parameter ACCEPT_THIRDPARTY_LICENSES specifies if you want to accept all the 3rd party licenses used. Specify '1' to accept the license agreements and proceed with installation or upgrade-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"ACCEPT_THIRDPARTY_LICENSES`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t`\t<!--ERequired] Parameter ACCEPT_REQUIRED_SOFTWARE specifies if you want to accept all the required software licenses. Specify '1' to accept the license agreements and proceed with installation or upgrade-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"ACCEPT_REQUIRED_SOFTWARE`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t`\t<!--License file-->`\r`\n`\t`\t<!--Specify path to a license file and autoupdate option-->`\r`\n`\r`\n`\t`\t<!--aOptional] Parameter VBR_LICENSE_FILE specifies a full path to the license file. If you do not specify this parameter(or leave it empty value), Veeam Backup & Replication will be installed using current license file. To install Community Edition it must be set to 0-->`\r`\n`\t`\t<!--Supported values: file path/0(to install CE)-->`\r`\n`\t`\t<!--property name=`\`"VBR_LICENSE_FILE`\`" value=`\`"`\`" /-->`\r`\n`\r`\n`\t`\t<!--eOptional] Parameter VBR_LICENSE_AUTOUPDATE specifies if you want to update license automatically(enables usage reporting). If you do not specify this parameter, autoupdate will be enabled. For Community Edition and NFR it must be set to 1-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"VBR_LICENSE_AUTOUPDATE`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t`\t<!--Service account-->`\r`\n`\r`\n`\t`\t<!--gOptional] Parameter VBR_SERVICE_PASSWORD specifies a password for the account under which the Veeam Backup Service is running. Required during upgrade if service account is not LocalSystem account-->`\r`\n`\t`\t<!--Make sure you keep the answer file in a safe location whenever service account password is added to the answer file-->`\r`\n`\t`\t<!--Supported values: password in plain text-->`\r`\n`\t`\t<!--property name=`\`"VBR_SERVICE_PASSWORD`\`" value=`\`"`\`" hidden=`\`"1`\`"/-->`\r`\n`\r`\n`\t`\t<!--Database configuration-->`\r`\n`\t`\t<!--Specify database server installation options and required configuration parameters for Veeam Backup & Replication database-->`\r`\n`\r`\n`\t`\t<!--fOptional] Parameter VBR_SQLSERVER_PASSWORD specifies a password to connect to the SQL server in the native authentication mode-->-->`\r`\n`\t`\t<!--Make sure you keep the answer file in a safe location whenever SQL server account password is added to the answer file-->`\r`\n`\t`\t<!--Supported values: password in plain text-->`\r`\n`\t`\t<property name=`\`"VBR_SQLSERVER_PASSWORD`\`" value=`\`"password`\`" hidden=`\`"1`\`"/>`\r`\n`\r`\n`\t`\t<!--Automatic update settings-->`\r`\n`\t`\t<!--Specify Veeam B&R autoupdate settings-->`\r`\n`\r`\n`\t`\t<!---Optional] Parameter VBR_AUTO_UPGRADE specifies if you want Veeam Backup & Replication to automatically upgrade existing components in the backup infrastructure. If you do not specify this parameter, Veeam Backup & Replication will not upgrade out of date components automatically-->`\r`\n`\t`\t<!--Supported values: 0/1-->`\r`\n`\t`\t<property name=`\`"VBR_AUTO_UPGRADE`\`" value=`\`"1`\`" />`\r`\n`\r`\n`\t</properties>`\r`\n</unattendedInstallationConfiguration>`\r`\n`",
`"allowAutoReboot`": true,
`"stopAllActivities`": true,
`"adminUserName`": "$Username",
`"adminPassword`": "$Password"
}
"@
## Upgrade each required VBR Server ##
$Upgrade = Invoke-RestMethod "https://VSPC-URL/api/v3/infrastructure/backupServers/$Instance/upgrade" -Method 'POST' -Headers $headers -Body $body
}
}
}
My next thoughts are, having a CSV File stored locally with all the admin credentials is not the best idea (It cant be deleted once the upgrades are finished of course!), however, I want to try and get those admin creds without using a CSV file. We use “Keeper” as our password manager here (we have recently moved to it) which I can see has some clever integration with PowerShell so I should hopefully be able to update this in the coming weeks (once I learn how to use it!).
Anyway, I hope this helps some other people out there!