Veeam Integration with vRealize Operations Manager

To keep control of a complex Datacenter good monitoring tools are necessary. In my opinion one of the major feature of a good monitoring solution is the capability to correlate data from different sources. VMware vRealize Operations Manager is a great solution if you run a VMware based SDDC with a mix of VMware and Partner Products. You can out of the box correlate metrics from VMware NSX, VMware vSphere, VMware VSAN and a lot of other VMware Products. But also integration of Third-Party solutions like the Blue Medora NetApp Storage Management Pack are possible. In most VMware SDDC`s also Veeam Backup & Replication is in use, so I tried to create a Veeam Integration with vRealize Operations Manager. I need to mention that Veeam offers with Veeam ONE also their own monitoring solution with a deep VMware vSphere and Veeam Integration. But in complex datacenter designs with a wide range of products that need to be integrated VMware vRealize Operations Manager is more flexible at this point.

My concept of a Veeam Integration with vRealize Operations Manager is only a proof of concept at this point. And even if it works is it not ready for production yet!

Veeam Protection View

The main goal of the proof of concept was to create a view that shows the Veeam protection state of the virtual machines.  The gathered data is identical to the data I have collected with an earlier PowerShell project – Veeam vSphere Interactions with PowerShell.

Veeam Integration with vRealize Operations Manager - View

The View has only my custom properties as Data with shorter Metric Labels.

Veeam Integration with vRealize Operations Manager - View Data

Group of Veeam Protected VMs

With the additional properties regarding the Veeam protection status I am also able to create new groups. The groups can be used to apply other Policies or to limit the permissions of users.

Veeam Integration with vRealize Operations Manager - Group Criteria

vRealize Operations Manager API

An easy possibility to add additional properties to resources (in my case virtual machine) in VMware vRealize Operations Manager is the API. vMan.ch wrote a great Blog Post how to add properties with the Suite-API. My Veeam Integration with vRealize Operations Manager is essentially a PowerShell Script that pushes collected data to the vRealize Operations Manager API.

The documentation of the Suite-API can accessed on every vRealize Operations Manager Appliance: https://<FQDN>/suite-api/

This is the sample request how to add custom properties with XML as payload:

Veeam Integration with vRealize Operations Manager - API Documentation addProperties

Name Description Type Required
statKey Stat Key for which the data is being posted xs:string yes
timestamps The array of timestamps at which the data is being posted xs:long yes
data The numeric data that is being posted (optional) xs:double no
values The string values that are being posted (optional) xs:string no

My real live example of a XML Body:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <ops:property-contents xmlns:ops="http://webservice.vmware.com/vRealizeOpsMgr/1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <ops:property-content statKey="VEEAM|LastRestorePoint">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:values>2/15/2018 2:49:59 PM</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|LastBackupJob">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:values>vRops-Test</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|FirstRestorePoint">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:values>2/14/2018 3:43:34 PM</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|IsProtected">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:values>True</ops:values>
        </ops:property-content>
            <ops:property-content statKey="VEEAM|VmRestorePoints">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:data>2</ops:data>
        </ops:property-content>
            <ops:property-content statKey="VEEAM|BackupServer">
            <ops:timestamps>1518771941000</ops:timestamps>
            <ops:values>VBR01</ops:values>
        </ops:property-content>
    </ops:property-contents>

You can also use your Browser or even better, Tools like Postman the leverage the Suite-API:

Veeam Integration with vRealize Operations Manager - Postman

Veeam Integration with vRealize Operations Manager PowerShell Script

My PowerShell script uses a modification of an older Veeam Project to get the protection details about the vSphere virtual machines from Veeam, I have only added some additional output to the function (Get-VeeamProtectionEnhanced). This data can now be used to create the API Request.

In difference to the API-Only solution from vMan.ch I have used the PowerCLI Cmdlet Get-OMResource to find the correct resource to add the properties.

# Credits: http://vman.ch/vrops-suite-api-properties-import/

#region: SSL Config for SelfSigned Cert and force TLS 1.2
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;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#endregion

#region: Functions
function Get-VeeamProtectionEnhanced {
    <#
        .NOTES
        Forked From: 
        https://github.com/mycloudrevolution/Veeam-vSphere-Interactions/blob/master/Veeam.PowerCLI-Interactions/functions/Get-VeeamProtection.psm1
    
    #>
    #Requires -Version 4
    #Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"}
    #Requires -PSSnapin VeeamPssnapin
    
      [CmdletBinding()]
        param(
            [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
            [ValidateNotNullorEmpty()]
                [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]
                $myVMs
        )
    Begin {
        if ($global:DefaultVIServer) {
            Write-Verbose "VI Server Connected"
            if ($ViServer = Get-VBRServer -Name $global:DefaultVIServer.Name) {
                Write-Verbose "VI Server '$($ViServer.Name)' in Veeam Inventory found"
            }
            else {
                throw "VI Server not in Veeam found!"
            }
        }
        else {
            throw "no VI Server Connected!"
        }
    
        $VbrReastorePoints = Get-VBRRestorePoint
        $VBRServerObject = Get-VBRServer | Where-Object {$_.Description -eq "Backup server" -and $_.Type -eq "Local"}
    }
    Process {
        $MyView = @()
        foreach ($MyVm in $MyVms) {
            [DateTime] $NowDate = (Get-date)
            [int64] $NowDateEpoc = Get-Date -Date $NowDate.ToUniversalTime() -UFormat %s
            $NowDateEpoc = $NowDateEpoc*1000
            $MoRef = $MyVm.ExtensionData.MoRef.Value
            if ($VeeamVm = Find-VBRViEntity -Server $ViServer | Where-Object {$_.Reference -eq $MoRef}) {
                [Array]$VmRestorePoints = $VbrReastorePoints | Where-Object {$_.InsideDir.Split("(,)") -eq $MoRef}
                if ($VmRestorePoints.count -gt 0) {$IsProtected = $true} else {$IsProtected = $false}
                $LastRestorePoint = $VmRestorePoints | Sort-Object CreationTime | Select-Object -Last 1
                $LastBackupJob = Get-VBRBackup | Where-Object {$_.Id -eq $LastRestorePoint.BackupId}
                $FirstRestorePoint = $VmRestorePoints | Sort-Object CreationTime | Select-Object -First 1
    
                $Report = [PSCustomObject] @{
                    VmName = $VeeamVm.name
                    MoRef = $MoRef
                    IsProtected = $IsProtected
                    VmRestorePoints = $VmRestorePoints.count
                    LastRestorePoint = $LastRestorePoint.CreationTime
                    LastBackupJob = $LastBackupJob.Name
                    FirstRestorePoint = $FirstRestorePoint.CreationTime
                    BackupServer = $VBRServerObject.Name
                    VmObject = $MyVm
                    Date = $NowDate
                    DateEpoch = $NowDateEpoc
                    }
                $MyView += $Report
            }
    
        }
        $MyView
    
        }
    }
#endregion

#region: Pre-Requirments
Import-Module VMware.VimAutomation.Core, VMware.VimAutomation.vROps
Add-PSSnapin VeeamPssnapin
$WarningPreference = "SilentlyContinue"
#endregion

#region: Global definitions
$vRopsFQDN = "<vRopsFQDN>"
$vCenterFQDN = "<vCenterFQDN>"
$VeeamFQDN = "<VeeamFQDN>"
$vRopsCred = Import-Clixml -Path "C:\Secure\vRops-Credential.xml"
$vCenterCred = Import-Clixml -Path "C:\Secure\vCenter-Credential.xml"
$VeeamCred = Import-Clixml -Path "C:\Secure\Veeam-Credential.xml"
$vCenterDcFilter = "Production"
#endregion

#region: Connections
Connect-VIServer -Server $vCenterFQDN -Credential $vCenterCred
Connect-OMServer -Server $vRopsFQDN -Credential $vRopsCred
Connect-VBRServer -Server $VeeamFQDN -Credential $VeeamCred
#endregion

#region: Get Protected VMs
$ProtectedVms = Get-Datacenter -Name $vCenterDcFilter | Get-VM | Get-VeeamProtectionEnhanced
if (!$ProtectedVms) {
    Throw "No Veeam Protected VMs found"
}
#endregion

#region: Create XML Structure and populate variables from the Metadata file
foreach ($ProtectedVm in $ProtectedVms) {
    $XmlFile = @('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <ops:property-contents xmlns:ops="http://webservice.vmware.com/vRealizeOpsMgr/1.0/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <ops:property-content statKey="VEEAM|LastRestorePoint">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:values>{1}</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|LastBackupJob">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:values>{2}</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|FirstRestorePoint">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:values>{3}</ops:values>
        </ops:property-content>
        <ops:property-content statKey="VEEAM|IsProtected">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:values>{4}</ops:values>
        </ops:property-content>
            <ops:property-content statKey="VEEAM|VmRestorePoints">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:data>{5}</ops:data>
        </ops:property-content>
            <ops:property-content statKey="VEEAM|BackupServer">
            <ops:timestamps>{0}</ops:timestamps>
            <ops:values>{6}</ops:values>
        </ops:property-content>
    </ops:property-contents>' -f $ProtectedVm.DateEpoch,
                                $ProtectedVm.LastRestorePoint,
                                $ProtectedVm.LastBackupJob,
                                $ProtectedVm.FirstRestorePoint,
                                $ProtectedVm.IsProtected,
                                $ProtectedVm.VmRestorePoints,
                                $ProtectedVm.BackupServer
    )
    
    [xml]$XmlSend = $XmlFile

    $OmRessource = Get-OMResource -Entity $ProtectedVm.VmObject

    if ($OmRessource) {
        $UrlSend = 'https://' + $vRopsFQDN + '/suite-api/api/resources/'+ $OmRessource.Id + '/properties'
        $ContentType = "application/xml;charset=utf-8"
        Invoke-RestMethod -Method POST -uri $UrlSend -Body $XmlSend -Credential $vRopsCred -ContentType $ContentType -Verbose
    }
    else {
        Throw "No vRealize Operations Manager Ressource for '$($ProtectedVm.VmName)' Found"
    }
    
}
#endregion

#region: Disconnect
Disconnect-VIServer -Server * -Force:$true -Confirm:$false
Disconnect-OMServer -Server * -Force:$true -Confirm:$false
Disconnect-VBRServer
#endregion

This script is also available as a GitHub Gist.

Script Details:

  • 4 – 16 : Accept Self Signed Certificates and force TLS 1.2
  • 20 – 89 : Function Get-VeeamProtectionEnhanced
    • 39 – 50 : Make sure vCenter is connected and with the same name available in Veeam
    • 58 – 67 : Collect per VM details
    • 69 – 82 : Build return
  • 93 – 94 : Load Modules and SnapIns
  • 99 – 105 : Load global definitions and saved credential
    (See also: Import-Clixml and Export-Clixml)
  • 109 – 111 : Connect vCenter, Veeam and Operations Manager
  • 115 – 118 : Get vSphere VMs and their protection details
  • 123 – 156 : Build XML for the API Post
  • 160 : Get Operations Manager Ressource from vSphere VM
  • 162 – 169 : Invoke API Post
  • 175 – 177 : Disconnect vCenter, Veeam and Operations Manager

Known Problems

As I started playing with the custom properties I was wondering why the new properties were not available as a filter option in Views or Groups. But John Dias was able to clarify the situation (Many thanks!):

That means it can take while until the new properties pop up in all Filters. The more objects have this properties the faster it will work.

Leave a Reply