To the Vembu users / Windows admins in the world! … and for those of you that have not used Vembu check out their site, they recently announced reasonable pricing for small businesses and it’s a nice backup solution for SME in my opinion!

So to cut to the chase here is my Vembu story… I was running similar backup solution from another provider, I wanted to upgrade to vSphere 6.7 right after it was released…. unfortunately there was no eta from that vendor on support other than Coming Soon! After some googling is when I discovered Vembu… to my surprise supported 6.7 early on!! so I loaded it up and started to test it in my environment…

It’s been a few months now and I find it a nice little solution… Its got a simple enough to use web interface, no need for a thick client and what I really like about it is the API, I am big on automation / monitoring so being able to manipulate it via the API is what got me hooked!

So whats the point of this post you ask?? Well below I provide an example of how to talk to the API, authenticate and query some data via Powershell to export it into a XLSX file. (For more detail on using the API check out this link)

So to start… create “C:\Vembu\Scripts”, within this folder create 2 more folders called “config” & “reports”

To use the script you will need to save / store the credentials of the account that will talk to Vembu server using Get-Credential, then export it to a file that will be loaded later using secure string etc…

$cred = Get-Credential
$cred | Export-Clixml -Path "C:\Vembu\Scripts\config\TEST.xml"

As script will export directly to Excel you will also need to install the ImportExcel module by dfinke @ github

So once you have completed all the steps above you can use the script below which will export a report like this for your Vembu Server. (screens below too)

And here is the full script:

<#
Vembu BDR Server API Powershell script
v1.0 vMan.ch, 01.10.2018 - Initial version

Requires Module --> https://github.com/dfinke/ImportExcel

    Run the command below to store user and pass in secure credential XML for each environment you plan to target.

        $cred = Get-Credential
        $cred | Export-Clixml -Path "C:\VEMBU\Scripts\config\TEST.xml"

Usage Example:

.\VEMBU-API-CLI.ps1 -Server localhost -Action 'LIST_BACKUPS' -creds 'TEST'

#>


param
(
    [String]$Server= 'localhost',
    [String]$Action= 'LIST_BACKUPS',
    [String]$creds = 'TEST'
)

#Get Current Path.

$ScriptPath = (Get-Item -Path ".\" -Verbose).FullName
$OutputExcelFile = "$ScriptPath\Reports\VembuBackupReport.xlsx"

#Get Stored Credentials

if($creds -gt ""){

    $cred = Import-Clixml -Path "$ScriptPath\config\$creds.xml"

    $User = $cred.GetNetworkCredential().Username
    $Pass = $cred.GetNetworkCredential().Password
    }
    else
    {
    echo "No credentials, stop hammer time!"
    Exit
    }

#ConvertFromJSON Function 

function ConvertFrom-Json2{
<#
	.SYNOPSIS
		The ConvertFrom-Json cmdlet converts a JSON-formatted string to a custom object (PSCustomObject) that has a property for each field in the JSON 

	.DESCRIPTION
		The ConvertFrom-Json cmdlet converts a JSON-formatted string to a custom object (PSCustomObject) that has a property for each field in the JSON 

	.PARAMETER InputObject
		Specifies the JSON strings to convert to JSON objects. Enter a variable that contains the string, or type a command or expression that gets the string. You can also pipe a string to ConvertFrom-Json.
	
	.PARAMETER MaxJsonLength
		Specifies the MaxJsonLength, can be used to extend the size of strings that are converted.  This is the main feature of this cmdlet vs the native ConvertFrom-Json2

	.EXAMPLE
		Get-Date | Select-Object -Property * | ConvertTo-Json | ConvertFrom-Json
	
		DisplayHint : 2
	
		DateTime    : Friday, January 13, 2012 8:06:31 PM
	
		Date        : 1/13/2012 8:00:00 AM
	
		Day         : 13
	
		DayOfWeek   : 5
	
		DayOfYear   : 13
	
		Hour        : 20
	
		Kind        : 2
	
		Millisecond : 400
	
		Minute      : 6
	
		Month       : 1
	
		Second      : 31
	
		Ticks       : 634620819914009002
	
		TimeOfDay   : @{Ticks=723914009002; Days=0; Hours=20; Milliseconds=400; Minutes=6; Seconds=31; TotalDays=0.83786343634490734; TotalHours=20.108722472277776; TotalMilliseconds=72391400.900200009; TotalMinutes=1206.5233483366667;TotalSeconds=72391.4009002}
	
		Year        : 2012
	
		This command uses the ConvertTo-Json and ConvertFrom-Json cmdlets to convert a DateTime object from the Get-Date cmdlet to a JSON object.

		The command uses the Select-Object cmdlet to get all of the properties of the DateTime object. It uses the ConvertTo-Json cmdlet to convert the DateTime object to a JSON-formatted string and the ConvertFrom-Json cmdlet to convert the JSON-formatted string to a JSON object..
	
	.EXAMPLE
		PS C:\>$j = Invoke-WebRequest -Uri http://search.twitter.com/search.json?q=PowerShell | ConvertFrom-Json
	
		This command uses the Invoke-WebRequest cmdlet to get JSON strings from a web service and then it uses the ConvertFrom-Json cmdlet to convert JSON content to objects that can be  managed in Windows PowerShell.

		You can also use the Invoke-RestMethod cmdlet, which automatically converts JSON content to objects.
		Example 3
		PS C:\>(Get-Content JsonFile.JSON) -join "`n" | ConvertFrom-Json
	
		This example shows how to use the ConvertFrom-Json cmdlet to convert a JSON file to a Windows PowerShell custom object.

		The command uses Get-Content cmdlet to get the strings in a JSON file. It uses the Join operator to join the strings in the file into a single string that is delimited by newline characters (`n). Then it uses the pipeline operator to send the delimited string to the ConvertFrom-Json cmdlet, which converts it to a custom object.

		The Join operator is required, because the ConvertFrom-Json cmdlet expects a single string.

	.NOTES
		Author: Reddit
		Version History:
			1.0 - Initial release
		Known Issues:
			1.0 - Does not convert nested objects to psobjects
	.LINK
#>

[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='Low')]

param
(  
	[parameter(
		ParameterSetName='object',
		ValueFromPipeline=$true,
		Mandatory=$true)]
		[string]
		$InputObject,
	[parameter(
		ParameterSetName='object',
		ValueFromPipeline=$true,
		Mandatory=$false)]
		[int]
		$MaxJsonLength = 67108864

)#end param

BEGIN 
{ 
	
	#Configure json deserializer to handle larger then average json conversion
	[void][System.Reflection.Assembly]::LoadWithPartialName('System.Web.Extensions')        
	$jsonserial= New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer 
	$jsonserial.MaxJsonLength  = $MaxJsonLength

} #End BEGIN

PROCESS
{
	if ($PSCmdlet.ParameterSetName -eq 'object')
	{
		$deserializedJson = $jsonserial.DeserializeObject($InputObject)

		# Convert resulting dictionary objects to psobjects
		foreach($desJsonObj in $deserializedJson){
			$psObject = New-Object -TypeName psobject -Property $desJsonObj

			$dicMembers = $psObject | Get-Member -MemberType NoteProperty

			# Need to recursively go through members of the originating psobject that have a .GetType() Name of 'Dictionary`2' 
			# and convert to psobjects and replace the current member in the $psObject tree

			$psObject
		}
	}


}#end PROCESS

END
{
}#end END

}

# Powershell HMAC SHA 256 Converter for Vembu
Function VembuGenerateSecret([String]$vembuAction, $UnixTime, $vembuCredential){

$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$utf8 = new-object -TypeName System.Text.UTF8Encoding
$Passwordhash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($vembuCredential)))
$Passwordhash = $Passwordhash.ToLower() -replace '-', ''

#generate sha256 hash of nonce + POST Data
$SecretKey = $Passwordhash + $UnixTime

#get our sha512 hmac
$StringBuilder = New-Object System.Text.StringBuilder
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::UTF8.GetBytes($SecretKey)
$hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes("$vembuAction"))|%{ [Void]$StringBuilder.Append($_.ToString("x2")) }
$signature = $StringBuilder.ToString()

Return $signature

}

#Random vars

$EpocNOW=[int][double]::Parse($(Get-Date -date (Get-Date).ToUniversalTime()-uformat %s))
$sig = VembuGenerateSecret $Action $EpocNOW $Pass


#Actual script

switch($Action)
    {

    LIST_BACKUPS {

        Write-host "Getting Backup List and Status"

        $APICall = '{"UserName":"'+$User+'","Signature1":"'+$sig+'","SignatureVersion":2,"LoginTime":"'+$EpocNOW+'","Action":"'+$Action+'"}'

        $BackupJobs = Invoke-RestMethod -Method POST -uri "http://$Server/bdrwebservices.php" -Body $APICall
        $BackupJobsList = @()
        $BackupJobVMDetails = @()

        ForEach($Job in $BackupJobs.jobList){

                $BackupJobsList += New-Object PSObject -Property @{

                                JobName             = $Job.jobName
                                JobStatus           = $Job.jobStatus
                                CurrentAction       = $Job.CurrentAction
                                NextScheduleTime    = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds([int64]$Job.NextScheduleTime))
                                BackupServers       = $Job.Servers
                                Suspended           = $Job.Suspended
                                PluginType          = $Job.PluginType
                                vSphereHostName     = $Job.HostName
                                IsVmReplication     = $Job.IsVmReplication
                                FullBackup          = $Job.FullBacktip
                                IncrBackup          = $Job.IncrBacktip
                                EditionType         = $Job.EditionType


        }
    
    
        #Get VM Details for Each Job...

        $sig = VembuGenerateSecret 'VIEW_BACKUP' $EpocNOW $Pass

        $GetJobsCall = '{"Action":"VIEW_BACKUP","jobName":"'+$Job.jobName+'","isHyperVJob":"0","UserName":"'+$User+'","Signature1":"'+$sig+'","SignatureVersion":2,"LoginTime":"'+$EpocNOW+'"}'

        $ContentType = "application/json;charset=utf-8"
        $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
        $header.Add("Accept", 'application/json')

        $BackupJobDetails = Invoke-RestMethod -Method POST -uri "http://$Server/bdrwebservices.php" -Body $GetJobsCall -Headers $header -ContentType $ContentType | ConvertFrom-Json2


        ForEach ($J in $BackupJobDetails.viewData.Values.VMwareBackupSource.VMDetails){

                $BackupJobVMDetails += New-Object PSObject -Property @{

                                    JobName = $Job.jobName
                                    Keys  = $J.Keys
                                    Values   = $J.Values
               }
            Clear-Variable J
         }
        Clear-Variable BackupJobDetails
       }
       Clear-Variable Job

        $sig = VembuGenerateSecret 'BACKUP_STATUS_REPORT' $EpocNOW $Pass

        $GetBackupStatusReportURL = '{"Action":"BACKUP_STATUS_REPORT","showFailed":"1","showMissed":"1","showPartial":"1","showSuccess":"1","noOfDays":"1","UserName":"'+$User+'","Signature1":"'+$sig+'","SignatureVersion":2,"LoginTime":"'+$EpocNOW+'"}'

        $ContentType = "application/json;charset=utf-8"
        $header = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
        $header.Add("Accept", 'application/json')

        $GetBackupStatusReportData = Invoke-RestMethod -Method POST -uri "http://$Server/bdrwebservices.php" -Body $GetBackupStatusReportURL -Headers $header -ContentType $ContentType

        $BackupReport = @()

        ForEach($Backup in $GetBackupStatusReportData.reports){

            $BackupReport += New-Object PSObject -Property @{

                clientName        = $Backup.clientName
                backupName        = $Backup.backupName
                scheduleStartTime = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds([int64]$Backup.scheduleStartTime))
                scheduleEndTime   = [TimeZone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds([int64]$Backup.scheduleEndTime))
                nextSchTime       = $Backup.nextSchTime
                schType           = $Backup.schType
                pluginType        = $Backup.pluginType
                backupStatus      = $Backup.backupStatus
                totalFiles        = $Backup.totalFiles
                protectedFiles    = $Backup.protectedFiles
                skippedFiles      = $Backup.skippedFiles
                skippedFolders    = $Backup.skippedFolders
                remarks           = $Backup.remarks

            }
        }

       $BackupJobsList | Select JobName,JobStatus,CurrentAction,NextScheduleTime,IsVmReplication,BackupServers,FullBackup,IncrBackup,vSphereHostName,Suspended,PluginType,EditionType | Export-Excel $OutputExcelFile -WorkSheetname JobList
       $BackupJobVMDetails | Select JobName,Keys,Values | Export-Excel $OutputExcelFile -WorkSheetname JobVMDetails
       $BackupReport | Select backupName,clientName,scheduleStartTime,scheduleEndTime,nextSchTime,schType,pluginType,backupStatus,totalFiles,protectedFiles,skippedFiles,skippedFolders,remarks | Export-Excel $OutputExcelFile -WorkSheetname BackupReport

       Write-Host "Backu Report Completed, See the Reports folder."
      
    }
}

Remove-Variable * -Force -ErrorAction SilentlyContinue

PS: It’s not all roses with Vembu…  one gripe I have is the phone home licensing… I run a tight ship and don’t use the Backup to Cloud Solution’s they offer so I wish there was a way around the phone home licensing as I lock down my environment with NSX’s DFW to limit access to everything, especially the interwebs!

Anyway hope you found this script / example useful, I plan on sharing some additional posts in the near future to provide some real world automation using the API and vRA \ vRO & vROPS.

vMan