A little over a year ago I posted about converting your scripts from Zerto’s ZVM 9.x API to the new ZVM 10.x API. The process I described here still works, but there is an easier way if you are willing to use a username and password, instead of a key and secret. For most purposes, this new v2 method will be easier to configure and maintain.
This post will help you when converting REST API scripts for Windows ZVM for use with ZVM appliances. Spoiler alert – every script that uses the Windows ZVM REST API will need (some) modifications before it will work with the ZVM Appliance.
Summary of what has changed?
The ZVM Appliance, which replaced the Windows-based ZVM, is a complete overhaul. I won’t cover everything that has changed here, but here are the major changes that affect automation with the REST API.
- New HTTPS port – 443 replaces 9669
- New Authentication Process – Keycloak replaces “session” API
- Login credentials sources – Keycloak replaces vCenter
If you need more information around what / why / etc, check out my first article here. I want to keep this one short.
Changes from my V1 article to this V2 article
In my first article I walk you through creating a new client, and generating a key/secret key pair. This method works fine, but it does require you to setup a new client and do a bunch of things in Keycloak.
This V2 article leverages the existing Keycloak “zerto-client”, which allows us to use the same usernames and passwords to login as you would use at the GUI. This keeps the setup time to a minimum and allows you to easily use credentials from an integrated authentication source such as SAML or OIDC.
Before copying these scripts, I have them on Github which might be easier https://github.com/recklessop/zvm10-examples/
Python3 example
In the script below we will login to the ZVM, and get the list of VPGs. You should be able to run it against your ZVM just by having Python3 and the requests module installed, and by changing the ZVM address and username and password parameters.
import requests
import json
import warnings
import os
import logging
from requests.packages.urllib3.exceptions import InsecureRequestWarning # Correct import statement
# Suppress only the InsecureRequestWarning
warnings.simplefilter('ignore', InsecureRequestWarning)
# Configuration variables
ZVM_ADDRESS = os.getenv("ZVM_ADDRESS", "172.16.50.100")
ZVM_USERNAME = os.getenv("ZVM_USERNAME", "admin")
ZVM_PASSWORD = os.getenv("ZVM_PASSWORD", "Zertodata987!")
VERIFY_CERTIFICATE = os.getenv("VERIFY_CERTIFICATE", "False").lower() in ('true', '1', 't')
# Nothing below should need modified for the example to run and list VPGs
KEYCLOAK_API_BASE = f"https://{ZVM_ADDRESS}/auth/realms/zerto/protocol/openid-connect/token"
ZVM_API_BASE = f"https://{ZVM_ADDRESS}/v1/"
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
#function to get a token from keycloak
def get_token():
uri = KEYCLOAK_API_BASE
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
body = {
'username': ZVM_USERNAME,
'password': ZVM_PASSWORD,
'grant_type': 'password',
'client_id': 'zerto-client' # This is typically required for the password grant
}
try:
response = requests.post(uri, headers=headers, data=body, verify=VERIFY_CERTIFICATE)
response.raise_for_status()
token_data = response.json()
logging.info(f"Got Token Data: {token_data}")
return token_data.get('access_token')
except (requests.exceptions.RequestException, ValueError, KeyError) as e:
logging.error(f"Error obtaining token: {e}")
return None
def run():
token = get_token()
if not token:
logging.error("Failed to get token.")
return
# this line can be adjusted to any API url in ZVM
uri = f"{ZVM_API_BASE}vpgs"
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
body = {}
try:
# this line will need to be modified depending on if the api you want is a GET, POST, PUT, DELETE, etc
response = requests.get(uri, headers=headers, json=body, verify=VERIFY_CERTIFICATE),
response.raise_for_status()
logging.info(f'Request successful.\n{response.json()}')
return
except requests.exceptions.RequestException as e:
logging.error(f"Request to Zerto API failed: {e}")
return
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
return
if __name__ == '__main__':
run()
PowerShell Core Sample
The below script is an example of a PowerShell Script, it will login to ZVM and get a list of the VPGS. I ran this from my Macbook which has Powershell Core 7.4.1 on it.
To run this you will need to change the ZVM address, username, and password, just like the python example above.
# Configuration variables
$ZVM_ADDRESS = $env:ZVM_ADDRESS
if (-not $ZVM_ADDRESS) {
$ZVM_ADDRESS = "172.16.50.100"
}
$ZVM_USERNAME = $env:ZVM_USERNAME
if (-not $ZVM_USERNAME) {
$ZVM_USERNAME = "admin"
}
$ZVM_PASSWORD = $env:ZVM_PASSWORD
if (-not $ZVM_PASSWORD) {
$ZVM_PASSWORD = "Zertodata987!"
}
$VERIFY_CERTIFICATE = false
# Nothing below should need modified for the example to run and list VPGs
$KEYCLOAK_API_BASE = "https://$ZVM_ADDRESS/auth/realms/zerto/protocol/openid-connect/token"
$ZVM_API_BASE = "https://$ZVM_ADDRESS/v1/"
# Configure logging
function Write-Log {
param (
[string]$message,
[string]$level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Output "$timestamp - $level - $message"
}
# Function to get a token from Keycloak
function Get-Token {
$uri = $KEYCLOAK_API_BASE
$headers = @{
'Content-Type' = 'application/x-www-form-urlencoded'
}
$body = @{
'username' = $ZVM_USERNAME
'password' = $ZVM_PASSWORD
'grant_type' = 'password'
'client_id' = 'zerto-client'
}
try {
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Post -Body $body -SkipCertificateCheck:$VERIFY_CERTIFICATE
return $response.access_token
}
catch {
Write-Log "Error obtaining token: $_" "ERROR"
return $null
}
}
# Main function which executes when the program is run
function Run {
# Authenticate to the ZVM
$token = Get-Token
if (-not $token) {
Write-Log "Failed to get token." "ERROR"
return
}
# This line can be adjusted to any API URL in ZVM
$uri = $ZVM_API_BASE + "vpgs"
$headers = @{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $token"
}
try {
# This line will need to be modified depending on if the API you want is a GET, POST, PUT, DELETE, etc.
$response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -SkipCertificateCheck:$VERIFY_CERTIFICATE
Write-Log "Request successful.`n$($response | ConvertTo-Json)"
}
catch {
Write-Log "Request to Zerto API failed: $_" "ERROR"
}
}
# Execute the main function
Run
Taking it further
While these examples are pretty simple, they should show you clearly how the new authentication method using Keycloak can be implemented. More advanced scripting is certainly possible, but this should get you started.
If you are interested in some advanced examples check out some of the stuff I have on GitHub where I leverage the expiration time parameter from the token to re-authenticate automatically. This is great for scripts that run as a service.
https://github.com/recklessop/Zerto_Exporter/blob/master/app/zvma10/zvma.py
The above project is actually a zvm 10.0 module that has authentication handling as well as some other cool functionality. I use it in my Zerto-Exporter and zROC projects.
Pingback: Converting REST API Scripts for ZVM Appliance - Justin's IT Blog
Pingback: Harnessing Zerto 10 API Enhancements for Superior Disaster Recovery - Justin's IT Blog