• This topic has 8 replies, 2 voices, and was last updated May 8, 2020 by Alex S.

Create Simple VPG using PowerShell and API

  • 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..

    I 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.

    Hi 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

    Thanks Tobias, I will give it a shot and let you know.

    Hey, 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?

    If 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 TrustAllCertsPolicy

    function 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 script

    Hi 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.InvokeRestMethodCommand

    PS C:\Users\admnlasm>

    Hi 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.

    Hi 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.

You must be logged in to create new topics. Click here to login