The newest release of Veeam v12.2 came with an unexpected twist – multiple high level CVEs were reported to be fixed in Veeam v12.2. While security fixes are generally expected in every new release of any software, what was unexpected was this disclosure came so quickly after the Veeam 12.2 version was released.

Ok, so we have a new release, we cannot change that it was released, nor can we change that the CVE disclosure happened. What we can do is upgrade our servers.

Sounds good, right? However, what does that have to do with a Veeam Service Provider Dashboard?

Gathering Tenant Version Information

When this CVE was released it caused a bit of a kerfuffle because Veeam 12.2 drops all support for client VBR’s of anything older than Veeam v12, while Veeam v12.1 supported both Veeam v10a and Veeam v11 tenants. This presents an obvious problem when a Service Provider upgrades to Veeam v12.2 – they’re likely going to orphan some customers until those customers upgrade.

I had a Veeam Service Provider Dashboard I’ve used for a while now that tracks tenant version information, so I knew I could help identify which customers are using which versions. I made a post about this on the Veeam Community Forums if you want to grab this script itself. I realized my dashboard was grabbing some outdated information, which led me to think – now’s probably a good time to revamp my dashboard to include the proper version the tenant is using along with the tenant name.

TIG – Telegraph, Influx and Grafana

I won’t go into the nitty gritty of deploying a telegraph, influx or grafana deployment – there are lots of those guides available. The Telegraph and Grafana versions aren’t as important as the InfluxDB version – as my script will not inject data properly into InfluxDB 2.x. I am currently using 1.8.10 – one day I will update my code. Maybe.

The Script

Here’s a script you can schedule to run periodically from your VCC Server itself. This script will take the data gathered from your Veeam Cloud Connect environment and send it to your InfluxDB server.

# Simple script to pull the current number of running cloud connect jobs and push the information to InfluxDB
# Written by Tyler Jurgens - https://explosive.cloud

##
# Configurations
##

# Endpoint URL for InfluxDB
$veeamInfluxDBURL = "http://10.1.1.1" #CHANGE THIS TO POINT TO YOUR INFLUXDB
$veeamInfluxDBPort = "8086"
$veeamInfluxDB = "influx" #USE THE CORRECT INFLUXDB DATABASE
$veeamInfluxDBUser = "123" #USE THE CORRECT INFLUXDB USER
$veeamInfluxDBPassword = '123' | ConvertTo-SecureString -AsPlainText -Force #USE THE CORRECT INFLUXDB PASS
$cred = New-Object System.Management.Automation.PSCredential($veeamInfluxDBUser, $veeamInfluxDBPassword) 
$uri = "${veeamInfluxDBURL}:${veeamInfluxDBPort}/write?db=$veeamInfluxDB"

# Increase connection limit
$ServicePoint = [System.Net.ServicePointManager]::FindServicePoint("$uri")
$ServicePoint.ConnectionLimit = 100

# Load Veeam PowerShell Module
Import-Module Veeam.Backup.PowerShell

# Warm-up for Veeam V12
Get-VBRLicenseAutoUpdateStatus  

# Function to send data to InfluxDB and print to screen
function Send-InfluxData {
    param (
        [string]$body
    )

    if (![string]::IsNullOrWhiteSpace($body)) {
        #Write-Host "Sending Data to InfluxDB: $body"
        try {
            $restParams = @{
                Uri        = $uri
                Method     = 'POST'
                Body       = $body
                Credential = $cred
            }
            Invoke-RestMethod @restParams
        } catch {
            Write-Host "Error sending data to InfluxDB: $_"
        }
    } else {
        Write-Host "No data to send to InfluxDB. Skipping."
    }
}

# Fetch running jobs
$running = 0
for ($i = 0; $i -lt 10; $i++) {
    $myjobs = [Veeam.Backup.Core.CCloudSession]::GetRunning() | 
              Where-Object { $_.JobName -notin @("Console", "Auxiliary session", "Scheduler", "File Copy", "VPN tunnel", "DbAccessor", "Malware Detection", "Shell run", "Infrastructure rescan") }
    if ($myjobs) {
        $running = $myjobs.Count
        break
    }
    Start-Sleep -Seconds 1
}

# Send running job count
$body = "veeamcc_running_stats,active=Running running=$running"
Send-InfluxData $body

# Fetch repository info
$concurrentJobs = Get-CimInstance -Query "SELECT * FROM Repository" -Namespace "ROOT\VeeamBS"
foreach ($repo in $concurrentJobs) {
    $repoName = $repo.Name -replace ' ', '-'
    $repoTasks = $repo.ConcurrentJobsNow
    $body = "veeamcc_running_stats,repository=$repoName repotasks=$repoTasks"
    Send-InfluxData $body
}

# Fetch proxy jobs
$proxyJobs = Get-CimInstance -Query "SELECT * FROM Proxy" -Namespace "ROOT\VeeamBS"
foreach ($proxy in $proxyJobs) {
    $proxyName = $proxy.Name -replace ' ', '-'
    $proxyTasks = $proxy.ConcurrentJobsNow
    $body = "veeamcc_running_stats,proxy=$proxyName proxytasks=$proxyTasks"
    Send-InfluxData $body
}

# Fetch SOBR repositories and their extents
$sobrs = Get-VBRBackupRepository -ScaleOut
foreach ($sobr in $sobrs) {
    $sobrName = $sobr.Name -replace ' ', '-'
    $sobrTotal, $sobrFree = 0, 0

    foreach ($extent in ($sobr | Get-VBRRepositoryExtent)) {
        $server = $extent.Repository.Host.Name
        $ostype = ($extent.Repository.Host.Type).ToString()
        $extName = $extent.Repository.Info.Name -replace ' ', '-'
        $container = $extent.Repository.GetContainer()
        $freeSpace = $container.CachedFreeSpace.InGigabytes
        $totalSpace = $container.CachedTotalSpace.InGigabytes
        $usedSpace = ((1 - ($freeSpace / $totalSpace)) * 100).ToString("0.00")

        $sobrTotal += $totalSpace
        $sobrFree += $freeSpace

        $body = "veeamcc_running_stats,server=$server,extentname=$extName ostype=`"$ostype`",freespace=$freeSpace,totalspace=$totalSpace,usedspace=$usedSpace"
        Send-InfluxData $body
    }

    $sobrUsed = ((1 - ($sobrFree / $sobrTotal)) * 100).ToString("0.00")
    $body = "veeamcc_running_stats,sobrname=$sobrName sobrtotal=$sobrTotal,sobrfree=$sobrFree,sobrused=$sobrUsed"
    Send-InfluxData $body
}

# Fetch general repository info
$repos = Get-VBRBackupRepository
foreach ($repo in $repos) {
    $repoName = $repo.Name -replace ' ', '-'
    $server = Get-VBRServer | Where-Object { $_.Id -eq $repo.HostID }
    $serverName = $server.Name
    $ostype = $server.Type.ToString()
    $container = $repo.GetContainer()
    $totalSpace = $container.CachedTotalSpace.InBytes / 1GB
    $freeSpace = $container.CachedFreeSpace.InBytes / 1GB
    $usedSpace = ($totalSpace - $freeSpace)
    $usedPercentage = (($usedSpace / $totalSpace) * 100).ToString("0.0")

    $body = "veeamcc_running_stats,server=$serverName,reponame=`"$repoName`" ostype=`"$ostype`",freespace=$freeSpace,totalspace=$totalSpace,usedspace=$usedSpace,usedper=$usedPercentage"
    Send-InfluxData $body
}

# Fetch backup server info
$backupServers = Get-CimInstance -Query "SELECT * FROM BackupServer" -Namespace "ROOT\VeeamBS"
foreach ($serv in $backupServers) {
    $name = $serv.Name
    $version = $serv.DisplayVersion
    $status = Get-VBRCloudInfrastructureState
    $serviceState = Get-VBRCloudInfrastructureServiceState
    $state = $serviceState.State
    $delay = $serviceState.ServiceResponseDelay

    $body = "veeamcc_running_stats,server=$name name=`"$name`",version=`"$version`",maintenancestatus=`"$status`",state=`"$state`",delay=`"$delay`""
    Send-InfluxData $body
}

# Fetch tenant data and version info
$tenants = Get-VBRCloudTenant
$tenantVer = Get-CimInstance -Query "SELECT * FROM CloudTenantVersionInfo" -Namespace "ROOT\VeeamBS"

# Iterate through each tenant and find matching version data
foreach ($tenant in $tenants) {
    # Find the matching tenant version using TenantId (InstanceUid) and Tenant Id correlation
    $matchingVersion = $tenantVer | Where-Object { $_.InstanceUid -eq $tenant.Id }
    
    if ($matchingVersion) {
        # Extract the required tenant data fields
        $tenantName = $tenant.Name -replace ' ', '-'
        $vmCount = $tenant.VMCount
        $replicaCount = $tenant.ReplicaCount
        $productVersion = $matchingVersion.ProductVersion
        $lastActive = $tenant.LastActive  # No formatting applied

        # Prepare the data body for InfluxDB (without the tenantId field)
        $body = "veeamcc_running_stats,tenant=$tenantName VMCount=$vmCount,ReplicaCount=$replicaCount,ProductVersion=`"$productVersion`",LastActive=`"$lastActive`""
        
        # Send the data to InfluxDB
        Send-InfluxData $body
    }
}

# Close all connections
$ServicePoint.CloseConnectionGroup("")

Grafana

You can display any information from the InfluxDB data you want. I’ve created a simple dashboard for you, just in case you need something pre build.

You can find my grafana dashboard json here:

https://github.com/TnTBass/VeeamServiceProviderDashboard/blob/main/Veeam%20Cloud%20Connect.json

It looks a little bit like this:

Conclusion

If you dig a bit into the Veeam powershell commands, you can find some decent information for service providers that you cannot find anywhere else – including VeeamOne.

Unfortunately, some of this information comes from ‘unsupported’ methods. All we do with the script is gather data and dump it to InfluxDB, but still, use at your own risk. I have see some of these unsupported methods break in the past, so be advised the data gathered may not stay relevant across upgrades.

Also, the counts you see here for tenant information are not for billing purposes. Veeam will show multiple counts if you have the same VM in multiple jobs, while Cloud Connect license metering will only bill that one time. Eg: Backup one VM in 10 jobs and you’ll see the VMCount reflect 10, while Cloud Connect backup licensing will be only 1 instance.