- This topic has 8 replies, 2 voices, and was last updated May 8, 2020 by
Alex S.
Create Simple VPG using PowerShell and API
-
Dave SApril 3, 2019 08:02:15 PM
Can someone post a simple template script for creating a new VPG. I am just looking to get the understanding on getting the first one deployed using the API. I am just looking for a simply start that gets one created successfully. I have reviewed the API whitepaper and it is a little overwhelming at first. I would using this to tweak some of the defaults after I get the first one created. It would be great if variables can be defined for, names, networks, datastores, etc..
Dave SApril 4, 2019 07:53:12 PMI have reviewed the Zerto Virtual Replication RESTful APIs doc and what I am looking for is a base script like Example 2: Perform an action on page 24. Instead of reading a CSV file with VMs, just define parameters with the VM name and other VPG properties.
Tobias BApril 24, 2019 06:41:24 AMHi Dave
I’ve experienced the same issue. Starting with creating a VPG with the API can be frustrating. I’ve created a powershell script which just creates a almost fully populated JSON which you then can use to create a VPG.
It uses PSCustomObject for structure and filling in the data. I think it is easier to debug with PSCustom Objects than a raw JSON.
Please note that you have to replace most properties which ends with “Identifier” with your own.
#---------------------------------------------------------[Initialisations]-------------------------------------------------------- # ZVM Server $ZVMServer = "zvm.lab.local" # Get Credentials for ZVM API $Credentials = Get-Credential -Message "Please enter Username and Password for ZVM $($ZVMServer)" -UserName $env:USERNAME $username = $Credentials.UserName $password = $Credentials.GetNetworkCredential().Password #-----------------------------------------------------------[Functions]------------------------------------------------------------ function getxZertoSession ($zvm, $userName, $password) { $xZertoSessionURL = $zvm+"session/add" $authInfo = ("{0}:{1}" -f $userName,$password) $authInfo = [System.Text.Encoding]::UTF8.GetBytes($authInfo) $authInfo = [System.Convert]::ToBase64String($authInfo) $headers = @{Authorization=("Basic {0}" -f $authInfo)} $body = '{"AuthenticationMethod": "1"}' $contentType = "application/json" $xZertoSessionResponse = Invoke-WebRequest -Uri $xZertoSessionURL -Headers $headers -Method POST -Body $body -ContentType $contentType return @{"x-zerto-session"=$xZertoSessionResponse.headers.get_item("x-zerto-session")} } #-----------------------------------------------------------[Execution]------------------------------------------------------------ # Build Zerto REST API Url $ZertoRestURL = "https://$($ZVMServer)/v1/" # Get Zerto Session Header for Auth $ZertoSession = getxZertoSession "$($ZertoRestURL)" $username $password ########### BACKUP SETTINGS ########### $VPGSettingsBackup = $null; ########### BASIC SETTINGS ########### $VPGSettingsBasic= @() $VPGSettingsBasic = [PSCustomObject]@{ JournalHistoryInHours = "168"; Name = "VPGTest"; Priority = "High"; ProtectedSiteIdentifier = "3035c688-3fb2-469d-9034-4f729f5a6432"; RecoverySiteIdentifier = "e63b0d4d-2c2a-47ec-a28b-78c259007e79"; RpoInSeconds = "300"; ServiceProfileIdentifier = $null; TestIntervalInMinutes = "525600"; UseWanCompression = $true; ZorgIdentifier = $null } ########### BOOT GROUPS SETTINGS ########### $VPGSettingsBootGroupGroups= @() $VPGSettingsBootGroupGroups += [PSCustomObject]@{ BootDelayInSeconds =""; BootGroupIdentifier = "00000000-0000-0000-0000-000000000001"; Name = "Default" } $VPGSettingsBootGroups= @() $VPGSettingsBootGroups = [PSCustomObject]@{ BootGroups = $VPGSettingsBootGroupGroups; } ########### JOURNAL SETTINGS ########### $VPGSettingsJournalLimitation = @() $VPGSettingsJournalLimitation = [PSCustomObject]@{ HardLimitInMB = "153600"; HardLimitInPercent = $null; WarningThresholdInMB = "128000"; WarningThresholdInPercent = $null } $VPGSettingsJournal = @() $VPGSettingsJournal = [PSCustomObject]@{ DatastoreIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.datastore-175664"; Limitation = $VPGSettingsJournalLimitation } ########### NETWORK SETTINGS ########### $VPGSettingsNetworksFailoverHypervisor = @() $VPGSettingsNetworksFailoverHypervisor = [PSCustomObject]@{ DefaultNetworkIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.dvportgroup-117" } $VPGSettingsNetworksFailover = @() $VPGSettingsNetworksFailover = [PSCustomObject]@{ Hypervisor = $VPGSettingsNetworksFailoverHypervisor } $VPGSettingsNetworksFailoverTestHypervisor = @() $VPGSettingsNetworksFailoverTestHypervisor = [PSCustomObject]@{ DefaultNetworkIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.dvportgroup-117" } $VPGSettingsNetworksFailoverTest = @() $VPGSettingsNetworksFailoverTest = [PSCustomObject]@{ Hypervisor = $VPGSettingsNetworksFailoverTestHypervisor } $VPGSettingsNetworks = @() $VPGSettingsNetworks = [PSCustomObject]@{ Failover = $VPGSettingsNetworksFailover; FailoverTest = $VPGSettingsNetworksFailoverTest } ########### RECOVERY SETTINGS ########### $VPGSettingsRecovery = @() $VPGSettingsRecovery = [PSCustomObject]@{ DefaultDatastoreIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.datastore-175664"; DefaultFolderIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.group-v42949"; ResourcePoolIdentifier = $null } ########### SCRIPTING SETTINGS ########### $VPGSettingsScriptingPostRecovery = @() $VPGSettingsScriptingPostRecovery = [PSCustomObject]@{ Command = $null; Parameters = $null; TimeoutInSeconds = 0 } $VPGSettingsScriptingPreRecovery = @() $VPGSettingsScriptingPreRecovery = [PSCustomObject]@{ Command = $null; Parameters = $null; TimeoutInSeconds = 0 } $VPGSettingsScripting = @() $VPGSettingsScripting = [PSCustomObject]@{ PostBackup = $null; PostRecovery = $VPGSettingsScriptingPostRecovery; PreRecovery = $VPGSettingsScriptingPreRecovery } ########### VM SETTINGS ########### $VPGSettingsVMs = @() $VPGSettingsVMsJournal = @() $VPGSettingsVMsJournal = [PSCustomObject]@{ DatastoreIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.datastore-175664" } $VPGSettingsVMsVolumes = @() $VPGSettingsVMsVolumesDatastore = @() $VPGSettingsVMsVolumesDatastore = [PSCustomObject]@{ DatastoreIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.datastore-175664"; IsThin = "false" } $VPGSettingsVMsVolumes += [PSCustomObject]@{ VolumeIdentifier = "scsi:0:0"; IsSwap = "false"; Datastore = $VPGSettingsVMsVolumesDatastore } $VPGSettingsVMsRecovery = @() $VPGSettingsVMsRecovery = [PSCustomObject]@{ FolderIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.group-v42949"; DatastoreIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.datastore-175664"; HostIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.host-24265" } $VPGSettingsVMsNics = @() $VPGSettingsVMsNicsFailoverHypervisorIPConfig= @() $VPGSettingsVMsNicsFailoverHypervisorIPConfig = [PSCustomObject]@{ Gateway = $null; IsDhcp = "false"; PrimaryDns = $null; SecondaryDns = $null; StaticIp = $null; SubnetMask = $null } $VPGSettingsVMsNicsFailoverTestHypervisorIPConfig= @() $VPGSettingsVMsNicsFailoverTestHypervisorIPConfig = [PSCustomObject]@{ Gateway = $null; IsDhcp = "false"; PrimaryDns = $null; SecondaryDns = $null; StaticIp = $null; SubnetMask = $null } $VPGSettingsVMsNicsFailoverHypervisor= @() $VPGSettingsVMsNicsFailoverHypervisor = [PSCustomObject]@{ DnsSuffix = $null; IpConfig = $VPGSettingsVMsNicsFailoverHypervisorIPConfig; NetworkIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.dvportgroup-117"; ShouldReplaceMacAddress = "false" } $VPGSettingsVMsNicsFailoverTestHypervisor= @() $VPGSettingsVMsNicsFailoverTestHypervisor = [PSCustomObject]@{ DnsSuffix = $null; IpConfig = $VPGSettingsVMsNicsFailoverTestHypervisorIPConfig; NetworkIdentifier = "4e358c65-35f7-48d4-9742-eaf45897bd9e.dvportgroup-117"; ShouldReplaceMacAddress = "false" } $VPGSettingsVMsNicsFailover= @() $VPGSettingsVMsNicsFailover = [PSCustomObject]@{ Hypervisor = $VPGSettingsVMsNicsFailoverHypervisor; } $VPGSettingsVMsNicsFailoverTest= @() $VPGSettingsVMsNicsFailoverTest = [PSCustomObject]@{ Hypervisor = $VPGSettingsVMsNicsFailoverTestHypervisor; } $VPGSettingsVMsNics += [PSCustomObject]@{ NicIdentifier = "Network Adapter 1"; Failover = $VPGSettingsVMsNicsFailover; FailoverTest = $VPGSettingsVMsNicsFailoverTest } $VPGSettingsVMs += [PSCustomObject]@{ BootGroupIdentifier = "00000000-0000-0000-0000-000000000001"; VmIdentifier = "c0ee66b7-1c85-49cb-b7db-3ff9a83922d9.vm-130947"; Journal = $VPGSettingsVMsJournal; Nics = $VPGSettingsVMsNics; Recovery = $VPGSettingsVMsRecovery; Volumes = $VPGSettingsVMsVolumes } ########### COMBINE ALL SETTINGS ########### $JSON_Raw = [PSCustomObject]@{ Backup = $VPGSettingsBackup; Basic = $VPGSettingsBasic; BootGroups = $VPGSettingsBootGroups; Journal = $VPGSettingsJournal; Networks = $VPGSettingsNetworks; Recovery = $VPGSettingsRecovery; Scripting = $VPGSettingsScripting; Vms = $VPGSettingsVMs } # Convert the PSTableObject to JSON $JSON = $JSON_Raw | ConvertTo-Json -Depth 10 # Depth has to be given because default depth is 2! # Create VPG Setting $VPGSettingsIdentifier = Invoke-RestMethod -Method POST -Uri ("$($ZertoRestURL)VPGSettings") -Body $JSON -ContentType "application/json" -Headers $ZertoSession # Commit VPG Settings with POST Invoke-RestMethod -Method POST -Uri ("$($ZertoRestURL)VPGSettings/$($VPGSettingsIdentifier)/commit") -Body $JSON -ContentType "application/json" -Headers $ZertoSession
Dave SMay 3, 2019 02:28:47 PMThanks Tobias, I will give it a shot and let you know.
Alex SOctober 14, 2019 03:03:33 PMHey, has anyone managed to get Tobias’s script working yet?
@Tobias, apologies I am quite new to using APIs in powershell. You mention these Identifiers for example the Site identifiers? Is there a specific API you are using to get this information?
Alex SOctober 14, 2019 03:33:45 PMIf anyone is interested. I managed so far to get the APIs to return a VPG Identifier with the below API calls. This will also ignore a untrusted certificate.
$strZVMIP = “zvm03”
$strZVMPort = “9669”
$strZVMUser = “domain\username”
$Password = Read-Host -Prompt ‘Please enter your user password’ -AsSecureString
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
$strZVMPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
####################################################
## Perform authentication so that Zerto APIs can run. Return a session identifier that needs tobe inserted in the header for subsequent requests.
################################################
#-Certificates That are not trusted need to be accepted
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 TrustAllCertsPolicyfunction getxZertoSession ($userName, $password){
$baseURL = “https://” + $strZVMIP + “:”+$strZVMPort
$xZertoSessionURL = $baseURL +”/v1/session/add”
$authInfo = (“{0}:{1}” -f $userName,$password)
$authInfo = [System.Text.Encoding]::UTF8.GetBytes($authInfo)
$authInfo = [System.Convert]::ToBase64String($authInfo)
$headers = @{Authorization=(“Basic {0}” -f $authInfo)}
$contentType = “application/json”
$xZertoSessionResponse = Invoke-WebRequest -Uri $xZertoSessionURL -Headers $headers -Method POST -Body $body -ContentType $contentType
#$xZertoSessionResponse = Invoke-WebRequest -Uri $xZertoSessionURL -Headers $headers -Body $body -Method POST
return $xZertoSessionResponse.headers.get_item(“x-zerto-session”)
}
#Extract x-zerto-session from the response, and add it to the actual API:
$xZertoSession = getxZertoSession $strZVMUser $strZVMPwd
$zertoSessionHeader = @{“x-zerto-session”=$xZertoSession}
$zertoSessionHeader_xml = @{“Accept”=”application/xml”
“x-zerto-session”=$xZertoSession}
#Invoke the Zerto API:
$vpgListApiUrl = “https://” + $strZVMIP + “:”+$strZVMPort+”/v1/vpgs”
$VPGNAME = “NL – CUS001 – Test”
#Iterate with XML:
$vpgListXML = Invoke-RestMethod -Uri $vpgListApiUrl -Headers $zertoSessionHeader_xml
foreach ($vpg in $vpgListXML.ArrayOfVpgApi.VpgApi){
if ($vpg.VpgName -eq $VPGNAME){
$tmpVpgIdentifier = $vpg.VpgIdentifier
break
}
}
#Iterate with JSON:
$vpgListJSON = Invoke-RestMethod -Uri $vpgListApiUrl -Headers $zertoSessionHeader
foreach ($vpg in $vpgListJSON){
if ($vpg.VpgName -eq $VPGNAME){
$tmpVpgIdentifier = $vpg.VpgIdentifier
break
}
}
write-host $tmpVpgIdentifier
##End of scriptAlex SOctober 15, 2019 08:54:16 PMHi all,
Tried troubleshooting the above script but im getting an error when it gets to the create VPG setting API call. Its moaning about this bit in particular;
# Create VPG Setting
$VPGSettingsIdentifier = Invoke-RestMethod -Method POST -Uri (“$($ZertoRestURL)VPGSettings”) -Body $JSON -ContentType “application/json” -Headers $ZertoSession$VPGSettingsVMsNics += [PSCustomObject]@{
NicIdentifier = “Ethernet”;
Failover = $VPGSettingsVMsNicsFailover;
FailoverTest = $VPGSettingsVMsNicsFailoverTest
}PS C:\Users\admnlasm> $Error[0] Invoke-RestMethod : {“Message”:”Attempt to delete adapter ‘Ethernet’ which doesn’t exist”}
At line:302 char:26
+ … dentifier = Invoke-RestMethod -Method POST -Uri (“$($ZertoRestURL)VPG …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommandPS C:\Users\admnlasm>
Jodie RMay 7, 2020 08:57:18 PMHi Alex S, just wondering if you got anywhere with your script to automate VPG creation. I found a powershell script for version 6.0, however it does not work with 7.5. Seems like this would be an out of the box option, to create VPGs, and then protect vms via script.
Alex SMay 8, 2020 10:09:07 AMHi Jodie.
No, unfortunately, I didn’t get to finish the script. I know where the issue is though. In our environment, we use Netapp so all of the VMDKs disks need to be of the ‘Thin’ type. So You basically need to gather all of the SCSI IDs for all of the Disks of the VMs you need to protect using PowerCLI and then create some JSON code that specifies each disk is thin. It sounds like a foreach statement with some JSON code, but taking that JSON code and inserting it into the existing JSON skeleton seems quite hard to do in PowerShell and is above my ability at the moment.